Continue gestures on interaction after recents animation finishes
am: 0e44900863
Change-Id: Iafc9d0a6c9daad4a9ff07e313e52a6804400b127
This commit is contained in:
+1
-1
@@ -68,7 +68,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
|
||||
* @param wasVisible true if it was visible before
|
||||
*/
|
||||
boolean onActivityReady(T activity, Boolean wasVisible) {
|
||||
activity.<RecentsView>getOverviewPanel().setCurrentTask(mTargetTaskId);
|
||||
activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTaskId);
|
||||
AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
|
||||
ActivityControlHelper.AnimationFactory factory =
|
||||
mHelper.prepareRecentsUI(activity, wasVisible,
|
||||
|
||||
+1
-1
@@ -382,7 +382,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||
mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
|
||||
mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
|
||||
if (mSwipeSharedState.canGestureBeContinued) {
|
||||
mInteractionHandler.cancelCurrentAnimation();
|
||||
mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState);
|
||||
} else {
|
||||
mInteractionHandler.reset();
|
||||
}
|
||||
|
||||
@@ -59,6 +59,10 @@ public class RecentsAnimationWrapper {
|
||||
mInputProxySupplier = inputProxySupplier;
|
||||
}
|
||||
|
||||
public boolean hasTargets() {
|
||||
return targetSet != null && targetSet.hasTargets();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public synchronized void setController(SwipeAnimationTargetSet targetSet) {
|
||||
Preconditions.assertUIThread();
|
||||
|
||||
@@ -38,6 +38,8 @@ public class SwipeSharedState implements SwipeAnimationListener {
|
||||
|
||||
public boolean canGestureBeContinued;
|
||||
public boolean goingToLauncher;
|
||||
public boolean recentsAnimationFinishInterrupted;
|
||||
public int nextRunningTaskId = -1;
|
||||
|
||||
public SwipeSharedState(OverviewComponentObserver overviewComponentObserver) {
|
||||
mOverviewComponentObserver = overviewComponentObserver;
|
||||
@@ -114,9 +116,22 @@ public class SwipeSharedState implements SwipeAnimationListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a recents animation has finished, but was interrupted before the next task was
|
||||
* launched. The given {@param runningTaskId} should be used as the running task for the
|
||||
* continuing input consumer.
|
||||
*/
|
||||
public void setRecentsAnimationFinishInterrupted(int runningTaskId) {
|
||||
recentsAnimationFinishInterrupted = true;
|
||||
nextRunningTaskId = runningTaskId;
|
||||
mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets();
|
||||
}
|
||||
|
||||
public void clearAllState() {
|
||||
clearListenerState();
|
||||
canGestureBeContinued = false;
|
||||
recentsAnimationFinishInterrupted = false;
|
||||
nextRunningTaskId = -1;
|
||||
goingToLauncher = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,7 @@ public final class TaskViewUtils {
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
// TODO: Take into account the current fullscreen progress for animating the insets
|
||||
params.setProgress(1 - percent);
|
||||
RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
|
||||
if (!skipViewChanges) {
|
||||
|
||||
+10
-2
@@ -26,6 +26,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.Service;
|
||||
@@ -456,7 +457,7 @@ public class TouchInteractionService extends Service implements
|
||||
return new DeviceLockedInputConsumer(this);
|
||||
}
|
||||
|
||||
RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
|
||||
final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
|
||||
if (!useSharedState) {
|
||||
mSwipeSharedState.clearAllState();
|
||||
}
|
||||
@@ -465,8 +466,15 @@ public class TouchInteractionService extends Service implements
|
||||
mOverviewComponentObserver.getActivityControlHelper();
|
||||
|
||||
InputConsumer base;
|
||||
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
|
||||
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher
|
||||
&& !mSwipeSharedState.recentsAnimationFinishInterrupted) {
|
||||
base = InputConsumer.NO_OP;
|
||||
} else if (mSwipeSharedState.recentsAnimationFinishInterrupted) {
|
||||
// If the finish animation was interrupted, then continue using the other activity input
|
||||
// consumer but with the next task as the running task
|
||||
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||
info.id = mSwipeSharedState.nextRunningTaskId;
|
||||
base = createOtherActivityInputConsumer(event, info);
|
||||
} else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
|
||||
base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
|
||||
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
|
||||
|
||||
+48
-23
@@ -256,7 +256,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
private AnimationFactory mAnimationFactory = (t) -> { };
|
||||
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
|
||||
|
||||
private boolean mCanceled;
|
||||
private boolean mWasLauncherAlreadyVisible;
|
||||
private int mFinishingRecentsAnimationForNewTaskId = -1;
|
||||
|
||||
private boolean mPassedOverviewThreshold;
|
||||
private boolean mGestureStarted;
|
||||
@@ -412,7 +414,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
mRecentsAnimationWrapper.runOnInit(() ->
|
||||
mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
|
||||
});
|
||||
mRecentsView.setEnableFreeScroll(false);
|
||||
|
||||
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||
if (mGestureEndTarget != HOME) {
|
||||
@@ -500,10 +501,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
if (mContinuingLastGesture) {
|
||||
return;
|
||||
}
|
||||
mRecentsView.setEnableDrawingLiveTile(false);
|
||||
mRecentsView.showTask(mRunningTaskId);
|
||||
mRecentsView.setRunningTaskHidden(true);
|
||||
mRecentsView.setRunningTaskIconScaledDown(true);
|
||||
mRecentsView.onGestureAnimationStart(mRunningTaskId);
|
||||
}
|
||||
|
||||
private void launcherFrameDrawn() {
|
||||
@@ -679,7 +677,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex();
|
||||
if (runningTaskIndex >= 0) {
|
||||
for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
|
||||
if (i != runningTaskIndex) {
|
||||
if (i != runningTaskIndex || !mRecentsAnimationWrapper.hasTargets()) {
|
||||
mRecentsView.getTaskViewAt(i).setFullscreenProgress(1 - mCurrentShift.value);
|
||||
}
|
||||
}
|
||||
@@ -838,9 +836,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
Interpolator interpolator = DEACCEL;
|
||||
final boolean goingToNewTask;
|
||||
if (mRecentsView != null) {
|
||||
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
|
||||
final int taskToLaunch = mRecentsView.getNextPage();
|
||||
goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
|
||||
if (!mRecentsAnimationWrapper.hasTargets()) {
|
||||
// If there are no running tasks, then we can assume that this is a continuation of
|
||||
// the last gesture, but after the recents animation has finished
|
||||
goingToNewTask = true;
|
||||
} else {
|
||||
final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
|
||||
final int taskToLaunch = mRecentsView.getNextPage();
|
||||
goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex;
|
||||
}
|
||||
} else {
|
||||
goingToNewTask = false;
|
||||
}
|
||||
@@ -1124,14 +1128,22 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
|
||||
true /* freezeTaskList */);
|
||||
} else {
|
||||
int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
|
||||
mFinishingRecentsAnimationForNewTaskId = taskId;
|
||||
mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
|
||||
mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(
|
||||
false /* animate */, true /* freezeTaskList */);
|
||||
if (!mCanceled) {
|
||||
TaskView nextTask = mRecentsView.getTaskView(taskId);
|
||||
if (nextTask != null) {
|
||||
nextTask.launchTask(false /* animate */, true /* freezeTaskList */);
|
||||
doLogGesture(NEW_TASK);
|
||||
}
|
||||
reset();
|
||||
}
|
||||
mCanceled = false;
|
||||
mFinishingRecentsAnimationForNewTaskId = -1;
|
||||
});
|
||||
}
|
||||
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
|
||||
doLogGesture(NEW_TASK);
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
@@ -1142,12 +1154,26 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
* Cancels any running animation so that the active target can be overriden by a new swipe
|
||||
* handle (in case of quick switch).
|
||||
*/
|
||||
public void cancelCurrentAnimation() {
|
||||
public void cancelCurrentAnimation(SwipeSharedState sharedState) {
|
||||
mCanceled = true;
|
||||
mCurrentShift.cancelAnimation();
|
||||
if (mLauncherTransitionController != null && mLauncherTransitionController
|
||||
.getAnimationPlayer().isStarted()) {
|
||||
mLauncherTransitionController.getAnimationPlayer().cancel();
|
||||
}
|
||||
|
||||
if (mFinishingRecentsAnimationForNewTaskId != -1) {
|
||||
// If we are canceling mid-starting a new task, switch to the screenshot since the
|
||||
// recents animation has finished
|
||||
switchToScreenshot();
|
||||
TaskView newRunningTaskView = mRecentsView.getTaskView(
|
||||
mFinishingRecentsAnimationForNewTaskId);
|
||||
int newRunningTaskId = newRunningTaskView != null
|
||||
? newRunningTaskView.getTask().key.id
|
||||
: -1;
|
||||
mRecentsView.setCurrentTask(newRunningTaskId);
|
||||
sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateHandler() {
|
||||
@@ -1169,11 +1195,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
|
||||
mRecentsView.setEnableFreeScroll(true);
|
||||
mRecentsView.setRunningTaskIconScaledDown(false);
|
||||
mRecentsView.setOnScrollChangeListener(null);
|
||||
mRecentsView.setRunningTaskHidden(false);
|
||||
mRecentsView.setEnableDrawingLiveTile(true);
|
||||
mRecentsView.onGestureAnimationEnd();
|
||||
|
||||
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
|
||||
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
|
||||
@@ -1194,6 +1216,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
private void switchToScreenshot() {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
|
||||
} else if (!mRecentsAnimationWrapper.hasTargets()) {
|
||||
// If there are no targets, then we don't need to capture anything
|
||||
setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
|
||||
} else {
|
||||
boolean finishTransitionPosted = false;
|
||||
SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
|
||||
@@ -1241,6 +1266,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
private void finishCurrentTransitionToRecents() {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
|
||||
} else if (!mRecentsAnimationWrapper.hasTargets()) {
|
||||
// If there are no targets, then there is nothing to finish
|
||||
setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
|
||||
} else {
|
||||
synchronized (mRecentsAnimationWrapper) {
|
||||
mRecentsAnimationWrapper.finish(true /* toRecents */,
|
||||
@@ -1267,10 +1295,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
||||
}
|
||||
mActivityControlHelper.onSwipeUpComplete(mActivity);
|
||||
mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
|
||||
|
||||
// Animate the first icon.
|
||||
mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation());
|
||||
mRecentsView.setSwipeDownShouldLaunchApp(true);
|
||||
mRecentsView.onSwipeUpAnimationSuccess();
|
||||
|
||||
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
||||
|
||||
|
||||
+14
@@ -53,6 +53,20 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
|
||||
this.mOnFinishListener = onFinishListener;
|
||||
}
|
||||
|
||||
public boolean hasTargets() {
|
||||
return unfilteredApps.length != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones the target set without any actual targets. Used only when continuing a gesture after
|
||||
* the actual recents animation has finished.
|
||||
*/
|
||||
public SwipeAnimationTargetSet cloneWithoutTargets() {
|
||||
return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0],
|
||||
homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
|
||||
mOnFinishListener);
|
||||
}
|
||||
|
||||
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
|
||||
mOnFinishListener.accept(this);
|
||||
BACKGROUND_EXECUTOR.execute(() -> {
|
||||
|
||||
@@ -744,9 +744,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
public abstract void startHome();
|
||||
|
||||
public void reset() {
|
||||
setRunningTaskViewShowScreenshot(false);
|
||||
mRunningTaskId = -1;
|
||||
mRunningTaskTileHidden = false;
|
||||
setCurrentTask(-1);
|
||||
mIgnoreResetTaskId = -1;
|
||||
mTaskListChangeId = -1;
|
||||
|
||||
@@ -757,6 +755,15 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
setCurrentPage(0);
|
||||
}
|
||||
|
||||
public @Nullable TaskView getRunningTaskView() {
|
||||
return getTaskView(mRunningTaskId);
|
||||
}
|
||||
|
||||
public int getRunningTaskIndex() {
|
||||
TaskView tv = getRunningTaskView();
|
||||
return tv == null ? -1 : indexOfChild(tv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the view if anything in recents changed.
|
||||
*/
|
||||
@@ -767,14 +774,50 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the first task in the view represents {@param task} and reloads the view
|
||||
* if needed. This allows the swipe-up gesture to assume that the first tile always
|
||||
* corresponds to the correct task.
|
||||
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
|
||||
* is called.
|
||||
* Also scrolls the view to this task
|
||||
* Called when a gesture from an app is starting.
|
||||
*/
|
||||
public void showTask(int runningTaskId) {
|
||||
public void onGestureAnimationStart(int runningTaskId) {
|
||||
// This needs to be called before the other states are set since it can create the task view
|
||||
showCurrentTask(runningTaskId);
|
||||
setEnableFreeScroll(false);
|
||||
setEnableDrawingLiveTile(false);
|
||||
setRunningTaskHidden(true);
|
||||
setRunningTaskIconScaledDown(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called only when a swipe-up gesture from an app has completed. Only called after
|
||||
* {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}.
|
||||
*/
|
||||
public void onSwipeUpAnimationSuccess() {
|
||||
if (getRunningTaskView() != null) {
|
||||
float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get()
|
||||
? mLiveTileOverlay.cancelIconAnimation()
|
||||
: 0f;
|
||||
animateUpRunningTaskIconScale(startProgress);
|
||||
}
|
||||
setSwipeDownShouldLaunchApp(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a gesture from an app has finished.
|
||||
*/
|
||||
public void onGestureAnimationEnd() {
|
||||
setEnableFreeScroll(true);
|
||||
setEnableDrawingLiveTile(true);
|
||||
setOnScrollChangeListener(null);
|
||||
setRunningTaskViewShowScreenshot(true);
|
||||
setRunningTaskHidden(false);
|
||||
animateUpRunningTaskIconScale();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
|
||||
*
|
||||
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
|
||||
* is called. Also scrolls the view to this task.
|
||||
*/
|
||||
public void showCurrentTask(int runningTaskId) {
|
||||
if (getChildCount() == 0) {
|
||||
// Add an empty view for now until the task plan is loaded and applied
|
||||
final TaskView taskView = mTaskViewPool.getView();
|
||||
@@ -789,16 +832,33 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
new ComponentName("", ""), false);
|
||||
taskView.bind(mTmpRunningTask);
|
||||
}
|
||||
|
||||
boolean runningTaskTileHidden = mRunningTaskTileHidden;
|
||||
setCurrentTask(runningTaskId);
|
||||
setCurrentPage(getRunningTaskIndex());
|
||||
setRunningTaskViewShowScreenshot(false);
|
||||
setRunningTaskHidden(runningTaskTileHidden);
|
||||
|
||||
// Reload the task list
|
||||
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
|
||||
}
|
||||
|
||||
public @Nullable TaskView getRunningTaskView() {
|
||||
return getTaskView(mRunningTaskId);
|
||||
}
|
||||
/**
|
||||
* Sets the running task id, cleaning up the old running task if necessary.
|
||||
* @param runningTaskId
|
||||
*/
|
||||
public void setCurrentTask(int runningTaskId) {
|
||||
if (mRunningTaskId == runningTaskId) {
|
||||
return;
|
||||
}
|
||||
|
||||
public int getRunningTaskIndex() {
|
||||
TaskView tv = getRunningTaskView();
|
||||
return tv == null ? -1 : indexOfChild(tv);
|
||||
if (mRunningTaskId != -1) {
|
||||
// Reset the state on the old running task view
|
||||
setRunningTaskIconScaledDown(false);
|
||||
setRunningTaskViewShowScreenshot(true);
|
||||
setRunningTaskHidden(false);
|
||||
}
|
||||
mRunningTaskId = runningTaskId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -812,27 +872,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #showTask(int)} but does not put any restrictions on the first tile.
|
||||
*/
|
||||
public void setCurrentTask(int runningTaskId) {
|
||||
boolean runningTaskTileHidden = mRunningTaskTileHidden;
|
||||
boolean runningTaskIconScaledDown = mRunningTaskIconScaledDown;
|
||||
|
||||
setRunningTaskIconScaledDown(false);
|
||||
setRunningTaskHidden(false);
|
||||
setRunningTaskViewShowScreenshot(true);
|
||||
mRunningTaskId = runningTaskId;
|
||||
setRunningTaskViewShowScreenshot(false);
|
||||
setRunningTaskIconScaledDown(runningTaskIconScaledDown);
|
||||
setRunningTaskHidden(runningTaskTileHidden);
|
||||
|
||||
setCurrentPage(getRunningTaskIndex());
|
||||
|
||||
// Load the tasks (if the loading is already
|
||||
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
|
||||
}
|
||||
|
||||
private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
TaskView runningTaskView = getRunningTaskView();
|
||||
@@ -1520,7 +1559,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
|
||||
|
||||
protected void onTaskLaunched(boolean success) {
|
||||
resetTaskVisuals();
|
||||
if (success) {
|
||||
resetTaskVisuals();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1072,6 +1072,10 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
|
||||
|
||||
|
||||
public void setEnableFreeScroll(boolean freeScroll) {
|
||||
if (mFreeScroll == freeScroll) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean wasFreeScroll = mFreeScroll;
|
||||
mFreeScroll = freeScroll;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user