Merging ub-launcher3-master, build 5356125

Test: Manual

Bug: 112934365
Bug: 114136250
Bug: 118441555
Bug: 123900446
Bug: 123904290
Bug: 123906429
Bug: 124288578
Bug: 124620962
Bug: 124730625
Bug: 125364936
Bug: 125551024
Bug: 126045978
Bug: 126229665
Bug: 126289691
Bug: 126327184
Bug: 126336729
Bug: 126416861
Bug: 126767319
Bug: 127282292
Bug: 127345257
Bug: 127532177
Change-Id: I6f56221b8a7009d1f14ba47a0c63822d3f3ba06a
This commit is contained in:
Tony Wickham
2019-03-06 10:26:28 -08:00
70 changed files with 1769 additions and 784 deletions
+2 -1
View File
@@ -38,7 +38,8 @@
<service
android:name="com.android.quickstep.TouchInteractionService"
android:permission="android.permission.STATUS_BAR_SERVICE" >
android:permission="android.permission.STATUS_BAR_SERVICE"
android:directBootAware="true" >
<intent-filter>
<action android:name="android.intent.action.QUICKSTEP_SERVICE" />
</intent-filter>
@@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<com.android.quickstep.hints.HintView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="@dimen/chip_hint_height"
android:layout_gravity="center_horizontal|bottom"
android:paddingStart="@dimen/chip_hint_start_padding"
android:paddingEnd="@dimen/chip_hint_end_padding"
android:background="@drawable/chip_hint_background_light"
android:gravity="center"
android:layout_marginHorizontal="@dimen/chip_hint_horizontal_margin"
android:orientation="horizontal"
android:elevation="@dimen/chip_hint_elevation"
android:layoutDirection="ltr">
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/chip_icon_size"
android:layout_height="@dimen/chip_icon_size"
android:visibility="gone"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:contentDescription="@null"/>
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
android:layout_height="@dimen/chip_text_height"
android:paddingTop="@dimen/chip_text_top_padding"
android:paddingStart="@dimen/chip_text_start_padding"
android:fontFamily="google-sans-medium"
android:textAlignment="textStart"
android:singleLine="true"
android:textColor="@color/chip_hint_foreground_color"
android:textSize="@dimen/chip_text_size"
android:ellipsize="none"
android:includeFontPadding="true"/>
</com.android.quickstep.hints.HintView>
@@ -14,9 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.quickstep.hints.HintsContainer
<com.android.quickstep.hints.ChipsContainer
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="@dimen/chip_hint_height"
android:layout_gravity="bottom"
android:gravity="center_horizontal"
android:orientation="horizontal"/>
@@ -44,6 +44,7 @@ import java.util.ArrayList;
*/
public abstract class RecentsUiFactory {
public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) ->
WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height);
@@ -61,7 +62,6 @@ public abstract class RecentsUiFactory {
if (swipeUpToHome) {
list.add(new FlingAndHoldTouchController(launcher));
list.add(new OverviewToAllAppsTouchController(launcher));
} else {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
list.add(new OverviewToAllAppsTouchController(launcher));
@@ -0,0 +1,200 @@
/*
* 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_UP;
import static android.view.MotionEvent.ACTION_UP;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.launcher3.R;
/**
* Touch consumer for handling events to launch assistant from launcher
*/
public class AssistantTouchConsumer implements InputConsumer {
private static final String TAG = "AssistantTouchConsumer";
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private int mActivePointerId = -1;
private final int mDisplayRotation;
private final Rect mStableInsets = new Rect();
private final float mDragSlop;
private final float mTouchSlop;
private final float mThreshold;
private float mStartDisplacement;
private boolean mPassedDragSlop;
private boolean mPassedTouchSlop;
private long mPassedTouchSlopTime;
private boolean mLaunchedAssistant;
private float mLastProgress;
private final ISystemUiProxy mSysUiProxy;
public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy) {
mSysUiProxy = systemUiProxy;
mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
mThreshold = context.getResources().getDimension(R.dimen.gestures_assistant_threshold);
Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
mDisplayRotation = display.getRotation();
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
}
@Override
public int getType() {
return TYPE_ASSISTANT;
}
@Override
public void onMotionEvent(MotionEvent ev) {
// TODO add logging
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mLastProgress = -1;
break;
}
case 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 ACTION_MOVE: {
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
float displacement = getDisplacement(ev);
if (!mPassedDragSlop) {
// Normal gesture, ensure we pass the drag slop before we start tracking
// the gesture
if (Math.abs(displacement) > mDragSlop) {
mPassedDragSlop = true;
mStartDisplacement = displacement;
mPassedTouchSlopTime = SystemClock.uptimeMillis();
}
}
if (!mPassedTouchSlop) {
if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
mTouchSlop) {
mPassedTouchSlop = true;
if (!mPassedDragSlop) {
mPassedDragSlop = true;
mStartDisplacement = displacement;
mPassedTouchSlopTime = SystemClock.uptimeMillis();
}
}
}
if (mPassedDragSlop) {
// Move
float distance = mStartDisplacement - displacement;
if (distance >= 0) {
onAssistantProgress(distance / mThreshold);
}
}
break;
}
case ACTION_CANCEL:
break;
case ACTION_UP: {
if (ev.getEventTime() - mPassedTouchSlopTime < ViewConfiguration.getTapTimeout()) {
onAssistantProgress(1);
}
break;
}
}
}
private void onAssistantProgress(float progress) {
if (mLastProgress == progress) {
return;
}
try {
mSysUiProxy.onAssistantProgress(Math.max(0, Math.min(1, progress)));
if (progress >= 1 && !mLaunchedAssistant) {
mSysUiProxy.startAssistant(new Bundle());
mLaunchedAssistant = true;
}
mLastProgress = progress;
} catch (RemoteException e) {
Log.w(TAG, "Failed to notify SysUI to start/send assistant progress: " + progress, e);
}
}
private boolean isNavBarOnRight() {
return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
}
private boolean isNavBarOnLeft() {
return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
}
private float getDisplacement(MotionEvent ev) {
float eventX = ev.getX();
float eventY = ev.getY();
float displacement = eventY - mDownPos.y;
if (isNavBarOnRight()) {
displacement = eventX - mDownPos.x;
} else if (isNavBarOnLeft()) {
displacement = mDownPos.x - eventX;
}
return displacement;
}
static boolean withinTouchRegion(Context context, float x) {
return x > context.getResources().getDisplayMetrics().widthPixels
- context.getResources().getDimension(R.dimen.gestures_assistant_width);
}
}
@@ -0,0 +1,47 @@
/*
* 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 android.content.Context;
import android.content.Intent;
import android.view.MotionEvent;
/**
* A dummy input consumer used when the device is still locked, e.g. from secure camera.
*/
public class DeviceLockedInputConsumer implements InputConsumer {
private final Context mContext;
public DeviceLockedInputConsumer(Context context) {
mContext = context;
}
@Override
public int getType() {
return TYPE_DEVICE_LOCKED;
}
@Override
public void onMotionEvent(MotionEvent ev) {
// For now, just start the home intent so user is prompted to unlock the device.
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
}
}
@@ -23,7 +23,16 @@ import android.view.MotionEvent;
@TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer {
InputConsumer NO_OP = new InputConsumer() { };
int TYPE_NO_OP = 0;
int TYPE_OVERVIEW = 1;
int TYPE_OTHER_ACTIVITY = 2;
int TYPE_ASSISTANT = 3;
int TYPE_DEVICE_LOCKED = 4;
InputConsumer NO_OP = () -> TYPE_NO_OP;
int getType();
default boolean isActive() {
return false;
@@ -100,17 +100,19 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
public HomeAnimationFactory prepareHomeUI(Launcher activity) {
final DeviceProfile dp = activity.getDeviceProfile();
final RecentsView recentsView = activity.getOverviewPanel();
final ComponentName component = recentsView.getRunningTaskView().getTask().key
.sourceComponent;
final View workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
final FloatingIconView floatingView = workspaceView == null ? null
: new FloatingIconView(activity);
final Rect iconLocation = new Rect();
if (floatingView != null) {
floatingView.matchPositionOf(activity, workspaceView, true /* hideOriginal */,
iconLocation);
final TaskView runningTaskView = recentsView.getRunningTaskView();
final View workspaceView;
if (runningTaskView != null) {
ComponentName component = runningTaskView.getTask().key.sourceComponent;
workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
} else {
workspaceView = null;
}
final Rect iconLocation = new Rect();
final FloatingIconView floatingView = workspaceView == null ? null
: FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, false /* useDrawableAsIs */,
activity.getDeviceProfile().getAspectRatioWithInsets(), iconLocation, null);
return new HomeAnimationFactory() {
@Nullable
@@ -36,6 +36,8 @@ import android.content.Intent;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -105,6 +107,12 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
// TODO: Start displacement should have both x and y
private float mStartDisplacement;
private Handler mMainThreadHandler;
private Runnable mCancelRecentsAnimationRunnable = () -> {
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
true /* restoreHomeStackPosition */);
};
public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
@@ -113,6 +121,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
SwipeSharedState swipeSharedState) {
super(base);
mMainThreadHandler = new Handler(Looper.getMainLooper());
mRunningTask = runningTaskInfo;
mRecentsModel = recentsModel;
mHomeIntent = homeIntent;
@@ -138,6 +147,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mPassedTouchSlop = mPassedDragSlop = mSwipeSharedState.getActiveListener() != null;
}
@Override
public int getType() {
return TYPE_OTHER_ACTIVITY;
}
@Override
public void onMotionEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
@@ -216,7 +230,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mTouchSlop) {
mPassedTouchSlop = true;
TOUCH_INTERACTION_LOG.startQuickStep();
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
if (mIsDeferredDownTarget) {
// Deferred gesture, start the animation and gesture tracking once
// we pass the actual touch slop
@@ -279,7 +293,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
TOUCH_INTERACTION_LOG.startRecentsAnimation();
TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation");
RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
@@ -328,10 +342,12 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
onConsumerAboutToBeSwitched();
onInteractionGestureFinished();
// Also clean up in case the system has handled the UP and canceled the animation before
// we had a chance to start the recents animation. In such a case, we will not receive
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
true /* restoreHomeStackPosition */);
// Cancel the recents animation if SysUI happens to handle UP before we have a chance
// to start the recents animation. In addition, workaround for b/126336729 by delaying
// the cancel of the animation for a period, in case SysUI is slow to handle UP and we
// handle DOWN & UP and move the home stack before SysUI can start the activity
mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -341,6 +357,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
@Override
public void onConsumerAboutToBeSwitched() {
Preconditions.assertUIThread();
mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
if (mInteractionHandler != null) {
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
@@ -59,6 +59,11 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
mStartingInActivityBounds = startingInActivityBounds;
}
@Override
public int getType() {
return TYPE_OVERVIEW;
}
@Override
public void onMotionEvent(MotionEvent ev) {
if (mInvalidated) {
@@ -120,7 +125,7 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
TOUCH_INTERACTION_LOG.startQuickStep();
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
}
mTrackingStarted = true;
@@ -152,4 +157,4 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
}
return new OverviewInputConsumer(activity, startingInActivityBounds);
}
}
}
@@ -1,88 +0,0 @@
/*
* Copyright (C) 2017 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 android.view.MotionEvent;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
/**
* Keeps track of debugging logs for a particular quickstep gesture.
*/
public class TouchInteractionLog {
// The number of gestures to log
private static final int MAX_NUM_LOG_GESTURES = 5;
private final Calendar mCalendar = Calendar.getInstance();
private final SimpleDateFormat mDateFormat = new SimpleDateFormat("MMM dd - kk:mm:ss:SSS");
private final LinkedList<ArrayList<String>> mGestureLogs = new LinkedList<>();
public void prepareForNewGesture() {
mGestureLogs.add(new ArrayList<>());
while (mGestureLogs.size() > MAX_NUM_LOG_GESTURES) {
mGestureLogs.pop();
}
getCurrentLog().add("[" + mDateFormat.format(mCalendar.getTime()) + "]");
}
public void setInputConsumer(InputConsumer consumer) {
getCurrentLog().add("tc=" + consumer.getClass().getSimpleName());
}
public void addMotionEvent(MotionEvent event) {
getCurrentLog().add("ev=" + event.getActionMasked());
}
public void startQuickStep() {
getCurrentLog().add("qstStart");
}
public void startRecentsAnimation() {
getCurrentLog().add("raStart");
}
public void startRecentsAnimationCallback(int numTargets) {
getCurrentLog().add("raStartCb=" + numTargets);
}
public void cancelRecentsAnimation() {
getCurrentLog().add("raCancel");
}
public void finishRecentsAnimation(boolean toHome) {
getCurrentLog().add("raFinish=" + toHome);
}
public void dump(PrintWriter pw) {
pw.println("TouchInteractionLog {");
for (ArrayList<String> gesture : mGestureLogs) {
pw.print(" ");
for (String log : gesture) {
pw.print(log + " ");
}
pw.println();
}
pw.println("}");
}
private ArrayList<String> getCurrentLog() {
return mGestureLogs.getLast();
}
}
@@ -23,13 +23,18 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYS
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.KeyguardManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Region;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.util.Pair;
import android.view.Choreographer;
@@ -37,6 +42,10 @@ import android.view.InputEvent;
import android.view.MotionEvent;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.UiThreadHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -49,6 +58,8 @@ import com.android.systemui.shared.system.InputConsumerController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* Service connected by system-UI for handling touch interaction.
@@ -60,7 +71,8 @@ public class TouchInteractionService extends Service {
public static final LooperExecutor BACKGROUND_EXECUTOR =
new LooperExecutor(UiThreadHelper.getBackgroundLooper());
public static final TouchInteractionLog TOUCH_INTERACTION_LOG = new TouchInteractionLog();
public static final EventLogArray TOUCH_INTERACTION_LOG =
new EventLogArray("touch_interaction_log", 40);
public static final int EDGE_NAV_BAR = 1 << 8;
@@ -75,8 +87,10 @@ public class TouchInteractionService extends Service {
public void onInitialize(Bundle bundle) {
mISystemUiProxy = ISystemUiProxy.Stub
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
runWhenUserUnlocked(() -> {
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
});
disposeEventHandlers();
mInputEventReceiver = InputChannelCompat.fromBundle(bundle, KEY_EXTRA_INPUT_CHANNEL,
@@ -128,8 +142,10 @@ public class TouchInteractionService extends Service {
public void onBind(ISystemUiProxy iSystemUiProxy) {
mISystemUiProxy = iSystemUiProxy;
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
runWhenUserUnlocked(() -> {
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
});
// On Bind is received before onInitialize which will dispose these handlers
disposeEventHandlers();
@@ -138,7 +154,6 @@ public class TouchInteractionService extends Service {
TouchInteractionService.this::onInputEvent);
mDeprecatedDispatcher = pair.first;
mInputEventReceiver = pair.second;
}
};
@@ -148,6 +163,7 @@ public class TouchInteractionService extends Service {
return sConnected;
}
private KeyguardManager mKM;
private ActivityManagerWrapper mAM;
private RecentsModel mRecentsModel;
private ISystemUiProxy mISystemUiProxy;
@@ -159,6 +175,17 @@ public class TouchInteractionService extends Service {
private InputConsumerController mInputConsumer;
private SwipeSharedState mSwipeSharedState;
private boolean mIsUserUnlocked;
private List<Runnable> mOnUserUnlockedCallbacks;
private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
initWhenUserUnlocked();
}
}
};
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
@@ -170,10 +197,29 @@ public class TouchInteractionService extends Service {
@Override
public void onCreate() {
super.onCreate();
// Initialize anything here that is needed in direct boot mode.
// Everything else should be initialized in initWhenUserUnlocked() below.
mKM = getSystemService(KeyguardManager.class);
mMainChoreographer = Choreographer.getInstance();
mOnUserUnlockedCallbacks = new ArrayList<>();
if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) {
initWhenUserUnlocked();
} else {
mIsUserUnlocked = false;
registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
}
sConnected = true;
}
private void initWhenUserUnlocked() {
mIsUserUnlocked = true;
mAM = ActivityManagerWrapper.getInstance();
mRecentsModel = RecentsModel.INSTANCE.get(this);
mOverviewComponentObserver = new OverviewComponentObserver(this);
mMainChoreographer = Choreographer.getInstance();
mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
@@ -183,18 +229,34 @@ public class TouchInteractionService extends Service {
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
mInputConsumer.registerInputConsumer();
sConnected = true;
for (Runnable callback : mOnUserUnlockedCallbacks) {
callback.run();
}
mOnUserUnlockedCallbacks.clear();
// Temporarily disable model preload
// new ModelPreload().start(this);
Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
}
private void runWhenUserUnlocked(Runnable callback) {
if (mIsUserUnlocked) {
callback.run();
} else {
mOnUserUnlockedCallbacks.add(callback);
}
}
@Override
public void onDestroy() {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
if (mIsUserUnlocked) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
disposeEventHandlers();
sConnected = false;
Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
super.onDestroy();
}
@@ -221,19 +283,24 @@ public class TouchInteractionService extends Service {
return;
}
MotionEvent event = (MotionEvent) ev;
TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
if (event.getAction() == ACTION_DOWN) {
TOUCH_INTERACTION_LOG.prepareForNewGesture();
boolean useSharedState = mConsumer.isActive();
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(useSharedState, event);
TOUCH_INTERACTION_LOG.setInputConsumer(mConsumer);
TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
}
TOUCH_INTERACTION_LOG.addMotionEvent(event);
mConsumer.onMotionEvent(event);
}
private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
// TODO: this makes a binder call every touch down. we should move to a listener pattern.
if (mKM.isDeviceLocked()) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps launched
// while device is locked even after exiting direct boot mode (e.g. camera).
return new DeviceLockedInputConsumer(this);
}
RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
if (!useSharedState) {
mSwipeSharedState.clearAllState();
@@ -241,6 +308,10 @@ public class TouchInteractionService extends Service {
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
return InputConsumer.NO_OP;
} else if (mOverviewInteractionState.isSwipeUpGestureEnabled()
&& FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
&& AssistantTouchConsumer.withinTouchRegion(this, event.getX())) {
return new AssistantTouchConsumer(this, mRecentsModel.getSystemUiProxy());
} else if (mSwipeSharedState.goingToLauncher ||
mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
return OverviewInputConsumer.newInstance(
@@ -271,6 +342,6 @@ public class TouchInteractionService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
TOUCH_INTERACTION_LOG.dump(pw);
TOUCH_INTERACTION_LOG.dump("", pw);
}
}
@@ -671,7 +671,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
initTransitionEndpoints(dp);
mRecentsAnimationWrapper.setController(targetSet);
TOUCH_INTERACTION_LOG.startRecentsAnimationCallback(targetSet.apps.length);
TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length);
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
mPassedOverviewThreshold = false;
@@ -682,7 +682,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mRecentsAnimationWrapper.setController(null);
mActivityInitListener.unregister();
setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
TOUCH_INTERACTION_LOG.cancelRecentsAnimation();
TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
}
@UiThread
@@ -972,27 +972,36 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
final RectF currentRect = new RectF();
final View floatingView = homeAnimationFactory.getFloatingView();
final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
if (floatingView instanceof FloatingIconView) {
if (isFloatingIconView) {
anim.addListener((FloatingIconView) floatingView);
}
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
final float windowAlphaThreshold = isFloatingIconView ? 0.75f : 1f;
anim.addUpdateListener(animation -> {
float progress = animation.getAnimatedFraction();
float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
float interpolatedProgress = Interpolators.ACCEL_1_5.getInterpolation(progress);
// Initially go towards original target (task view in recents),
// but accelerate towards the final target.
// TODO: This is technically not correct. Instead, motion should continue at
// the released velocity but accelerate towards the target.
targetRect.set(rectFEvaluator.evaluate(interpolatedProgress,
originalTarget, finalTarget));
currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
float alpha = 1 - interpolatedProgress;
mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
currentRect.set(rectFEvaluator.evaluate(interpolatedProgress, startRect, targetRect));
float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0,
windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR);
mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha)
.setSyncTransactionApplier(mSyncTransactionApplier);
mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
if (floatingView instanceof FloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, 1f - alpha);
if (isFloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress,
windowAlphaThreshold);
}
});
anim.addListener(new AnimationSuccessListener() {
@@ -1016,7 +1025,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
@UiThread
private void resumeLastTask() {
mRecentsAnimationWrapper.finish(false /* toRecents */, null);
TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
}
@UiThread
@@ -1034,7 +1043,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mMainThreadHandler);
});
}
TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
doLogGesture(NEW_TASK);
}
@@ -1143,7 +1152,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
}
TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
}
private void finishCurrentTransitionToHome() {
@@ -1151,7 +1160,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mRecentsAnimationWrapper.finish(true /* toRecents */,
() -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
doLogGesture(HOME);
}
@@ -0,0 +1,72 @@
/*
* 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.hints;
import android.content.Context;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.View;
import android.widget.FrameLayout;
public class ChipsContainer extends FrameLayout {
private static final String TAG = "ChipsContainer";
public static final FloatProperty<ChipsContainer> HINT_VISIBILITY =
new FloatProperty<ChipsContainer>("hint_visibility") {
@Override
public void setValue(ChipsContainer chipsContainer, float v) {
chipsContainer.setHintVisibility(v);
}
@Override
public Float get(ChipsContainer chipsContainer) {
return chipsContainer.mHintVisibility;
}
};
private float mHintVisibility;
public ChipsContainer(Context context) {
super(context);
}
public ChipsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ChipsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ChipsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setView(View v) {
removeAllViews();
addView(v);
}
public void setHintVisibility(float v) {
if (v == 1) {
setVisibility(VISIBLE);
} else {
setVisibility(GONE);
}
mHintVisibility = v;
}
}
@@ -1,218 +0,0 @@
/*
* 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.hints;
import static com.android.quickstep.hints.UiHintListenerConstants.HINTS_KEY;
import static com.android.quickstep.hints.UiHintListenerConstants.ON_HINTS_RETURNED_CODE;
import static com.android.quickstep.hints.UiInterfaceConstants.REQUEST_HINTS_CODE;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import java.util.ArrayList;
public class HintsContainer extends LinearLayout {
private static final String TAG = "HintsView";
public static final FloatProperty<HintsContainer> HINT_VISIBILITY =
new FloatProperty<HintsContainer>("hint_visibility") {
@Override
public void setValue(HintsContainer hintsContainer, float v) {
hintsContainer.setHintVisibility(v);
}
@Override
public Float get(HintsContainer hintsContainer) {
return hintsContainer.mHintVisibility;
}
};
private static Intent mServiceIntent =
new Intent("com.android.systemui.action.UI_PULL_INTERFACE")
.setClassName(
"com.android.systemui.navbarhint",
"com.android.systemui.navbarhint.service.HintService");
@Nullable
private Messenger mHintServiceInterface;
private UiHintListener mUiHintListener;
private boolean mBound = false;
private float mHintVisibility;
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mHintServiceInterface = new Messenger(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mHintServiceInterface = null;
attemptBinding();
}
@Override
public void onBindingDied(ComponentName componentName) {
mHintServiceInterface = null;
attemptBinding();
}
};
public HintsContainer(Context context) {
super(context);
}
public HintsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mUiHintListener == null) {
mUiHintListener = new UiHintListener(this);
}
if (!mBound) {
attemptBinding();
}
}
@Override
protected void onDetachedFromWindow() {
if (mBound) {
getContext().unbindService(mServiceConnection);
mBound = false;
}
super.onDetachedFromWindow();
}
public void setHintVisibility(float v) {
if (v == 1) {
getHints();
setVisibility(VISIBLE);
} else {
setVisibility(GONE);
}
mHintVisibility = v;
}
private void attemptBinding() {
if (mBound) {
getContext().unbindService(mServiceConnection);
mBound = false;
}
boolean success = getContext().bindService(mServiceIntent,
mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
if (success) {
mBound = true;
} else {
Log.w(TAG, "Binding to hint supplier failed");
}
}
private void sendOnHintTap(Bundle hint) {
if (mHintServiceInterface != null) {
Message msg = Message.obtain(null, UiInterfaceConstants.ON_HINT_TAP_CODE);
Bundle data = new Bundle();
data.putString(UiInterfaceConstants.HINT_ID_KEY, HintUtil.getId(hint));
data.putInt(UiInterfaceConstants.WIDTH_PX_KEY, getWidth());
data.putInt(UiInterfaceConstants.HEIGHT_PX_KEY, getHeight());
data.putInt(UiInterfaceConstants.HINT_SPACE_WIDTH_PX_KEY, 0);
data.putInt(UiInterfaceConstants.HINT_SPACE_HEIGHT_PX_KEY, 0);
msg.setData(data);
try {
mHintServiceInterface.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send hint tap", e);
}
}
}
private void getHints() {
if (mHintServiceInterface != null) {
try {
Message m = Message.obtain(null, REQUEST_HINTS_CODE);
m.replyTo = new Messenger(mUiHintListener);
mHintServiceInterface.send(m);
} catch (RemoteException e) {
Log.e(TAG, "Failed to send message", e);
}
}
}
private static class UiHintListener extends Handler {
private HintsContainer mView;
UiHintListener(HintsContainer v) {
mView = v;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case ON_HINTS_RETURNED_CODE:
handleHints(msg);
break;
default:
Log.e(TAG, "UiPullInterface got unrecognized code: " + msg.what);
break;
}
}
private void handleHints(Message msg) {
Bundle bundle = msg.getData();
ArrayList<Bundle> hints = bundle.getParcelableArrayList(HINTS_KEY);
if (hints != null) {
mView.removeAllViews();
for (Bundle hint : hints) {
HintView h = (HintView) LayoutInflater.from(mView.getContext()).inflate(
R.layout.hint, mView, false);
h.setHint(hint);
h.setOnClickListener((v) -> mView.sendOnHintTap(hint));
mView.addView(h);
}
}
}
}
}
@@ -185,6 +185,7 @@ public class ClipAnimationHelper {
if (mSupportsRoundedCornersOnWindows) {
cornerRadius = Utilities.mapRange(params.progress, mWindowCornerRadius,
mTaskCornerRadius);
mCurrentCornerRadius = cornerRadius;
}
}
alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
@@ -50,6 +50,7 @@ import java.util.Locale;
public final class DigitalWellBeingToast extends LinearLayout {
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
public interface InitializeCallback {
void call(float saturation, String contentDescription);
@@ -185,7 +186,11 @@ public final class DigitalWellBeingToast extends LinearLayout {
/* forceFormatWidth= */ false);
}
private String getShorterReadableDuration(Duration duration) {
private String getRoundedUpToMinuteReadableDuration(long remainingTime) {
final Duration duration = Duration.ofMillis(
remainingTime > MINUTE_MS ?
(remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
remainingTime);
return getReadableDuration(
duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
}
@@ -196,7 +201,7 @@ public final class DigitalWellBeingToast extends LinearLayout {
resources.getString(R.string.app_in_grayscale) :
resources.getString(
R.string.time_left_for_app,
getShorterReadableDuration(Duration.ofMillis(remainingTime)));
getRoundedUpToMinuteReadableDuration(remainingTime));
}
public void openAppUsageSettings() {
@@ -42,9 +42,10 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.hints.HintsContainer;
import com.android.quickstep.hints.ChipsContainer;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import com.android.quickstep.util.LayoutUtils;
@@ -77,7 +78,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
private float mTranslationYFactor;
private final TransformParams mTransformParams = new TransformParams();
private HintsContainer mHintsContainer;
private ChipsContainer mChipsContainer;
public LauncherRecentsView(Context context) {
this(context, null);
@@ -111,8 +112,9 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mHintsContainer = mActivity.findViewById(R.id.hints);
mHintsContainer.setPadding(0, 0, 0, mActivity.getDeviceProfile().chipHintBottomMarginPx);
mChipsContainer = mActivity.findViewById(R.id.hints);
BaseDragLayer.LayoutParams params = (BaseDragLayer.LayoutParams) mChipsContainer.getLayoutParams();
params.bottomMargin = mActivity.getDeviceProfile().chipHintBottomMarginPx;
}
public void setTranslationYFactor(float translationFactor) {
@@ -131,11 +133,15 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
}
public void setHintVisibility(float v) {
if (mHintsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
mHintsContainer.setHintVisibility(v);
if (mChipsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
mChipsContainer.setHintVisibility(v);
}
}
public ChipsContainer getChipsContainer() {
return mChipsContainer;
}
@Override
public void draw(Canvas canvas) {
maybeDrawEmptyMessage(canvas);
@@ -192,7 +198,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
if (ENABLE_HINTS_IN_OVERVIEW.get()) {
anim.anim.play(ObjectAnimator.ofFloat(
mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
mChipsContainer, ChipsContainer.HINT_VISIBILITY, 0));
}
return anim;
@@ -206,10 +212,10 @@ public class LauncherRecentsView extends RecentsView<Launcher> {
if (ENABLE_HINTS_IN_OVERVIEW.get()) {
anim.anim.play(ObjectAnimator.ofFloat(
mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
mChipsContainer, ChipsContainer.HINT_VISIBILITY, 0));
anim.addEndListener(onEndListener -> {
if (!onEndListener.isSuccess) {
mHintsContainer.setHintVisibility(1);
mChipsContainer.setHintVisibility(1);
}
});
}
@@ -90,6 +90,7 @@ import com.android.launcher3.util.ViewPool;
import com.android.quickstep.OverviewCallbacks;
import com.android.quickstep.RecentsAnimationWrapper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.ClipAnimationHelper;
@@ -113,7 +114,7 @@ import java.util.function.Consumer;
@TargetApi(Build.VERSION_CODES.P)
public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
InvariantDeviceProfile.OnIDPChangeListener {
InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener {
private static final String TAG = RecentsView.class.getSimpleName();
@@ -170,14 +171,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
if (!mHandleTaskStackChanges) {
return;
}
updateThumbnail(taskId, snapshot);
}
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
if (!mHandleTaskStackChanges) {
@@ -262,7 +255,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private boolean mOverviewStateEnabled;
private boolean mHandleTaskStackChanges;
private Runnable mNextPageSwitchRunnable;
private boolean mSwipeDownShouldLaunchApp;
private boolean mTouchDownToStartHome;
private final int mTouchSlop;
@@ -340,6 +332,19 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return mIsRtl;
}
@Override
public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
if (mHandleTaskStackChanges) {
TaskView taskView = getTaskView(taskId);
if (taskView != null) {
Task task = taskView.getTask();
taskView.getThumbnail().setThumbnail(task, thumbnailData);
return task;
}
}
return null;
}
public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
TaskView taskView = getTaskView(taskId);
if (taskView != null) {
@@ -371,6 +376,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this);
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIdp.addOnChangeListener(this);
}
@@ -382,6 +388,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = null;
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
mIdp.removeOnChangeListener(this);
}
@@ -421,17 +428,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
updateTaskStackListenerState();
}
public void setNextPageSwitchRunnable(Runnable r) {
mNextPageSwitchRunnable = r;
}
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
if (mNextPageSwitchRunnable != null) {
mNextPageSwitchRunnable.run();
mNextPageSwitchRunnable = null;
}
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
}
@@ -774,7 +773,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
setCurrentTask(runningTaskId);
}
public TaskView getRunningTaskView() {
public @Nullable TaskView getRunningTaskView() {
return getTaskView(mRunningTaskId);
}
@@ -18,6 +18,7 @@ package com.android.quickstep.views;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.BaseActivity.fromContext;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -28,7 +29,6 @@ import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.drawable.Drawable;
@@ -91,7 +91,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
private static final long TASK_LAUNCH_ANIM_DURATION = 200;
public static final Property<TaskView, Float> ZOOM_SCALE =
new FloatProperty<TaskView>("zoomScale") {
@@ -214,6 +213,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
* Updates this task view to the given {@param task}.
*/
public void bind(Task task) {
cancelPendingLoadTasks();
mTask = task;
mSnapshotView.bind(task);
}
@@ -236,10 +236,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
final PendingAnimation pendingAnimation =
getRecentsView().createTaskLauncherAnimation(this, TASK_LAUNCH_ANIM_DURATION);
pendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
getRecentsView().createTaskLauncherAnimation(this, RECENTS_LAUNCH_DURATION);
pendingAnimation.anim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
AnimatorPlaybackController currentAnimation = AnimatorPlaybackController
.wrap(pendingAnimation.anim, TASK_LAUNCH_ANIM_DURATION, null);
.wrap(pendingAnimation.anim, RECENTS_LAUNCH_DURATION, null);
currentAnimation.setEndAction(() -> {
pendingAnimation.finish(true, Touch.SWIPE);
launchTask(false);
@@ -304,15 +304,15 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
if (mTask == null) {
return;
}
cancelPendingLoadTasks();
if (visible) {
// These calls are no-ops if the data is already loaded, try and load the high
// resolution thumbnail if the state permits
RecentsModel model = RecentsModel.INSTANCE.get(getContext());
TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
TaskIconCache iconCache = model.getIconCache();
mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(mTask,
!thumbnailCache.getHighResLoadingState().isEnabled() /* reducedResolution */,
(task) -> mSnapshotView.setThumbnail(task, task.thumbnail));
mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail));
mIconLoadRequest = iconCache.updateIconInBackground(mTask,
(task) -> {
setIcon(task.icon);
@@ -324,17 +324,22 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
});
});
} else {
if (mThumbnailLoadRequest != null) {
mThumbnailLoadRequest.cancel();
}
if (mIconLoadRequest != null) {
mIconLoadRequest.cancel();
}
mSnapshotView.setThumbnail(null, null);
setIcon(null);
}
}
private void cancelPendingLoadTasks() {
if (mThumbnailLoadRequest != null) {
mThumbnailLoadRequest.cancel();
mThumbnailLoadRequest = null;
}
if (mIconLoadRequest != null) {
mIconLoadRequest.cancel();
mIconLoadRequest = null;
}
}
private boolean showTaskMenu() {
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
mMenuView = TaskMenuView.showForTask(this);
+4 -1
View File
@@ -39,7 +39,6 @@
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
<dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
<dimen name="motion_pause_detector_min_displacement">48dp</dimen>
<dimen name="motion_pause_detector_max_orthogonal_displacement">48dp</dimen>
<!-- Launcher app transition -->
<dimen name="content_trans_y">50dp</dimen>
@@ -65,4 +64,8 @@
<dimen name="shelf_surface_radius">16dp</dimen>
<!-- same as vertical_drag_handle_size -->
<dimen name="shelf_surface_offset">24dp</dimen>
<!-- Assistant Gestures -->
<dimen name="gestures_assistant_width">70dp</dimen>
<dimen name="gestures_assistant_threshold">200dp</dimen>
</resources>
@@ -403,9 +403,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
boolean toggleVisibility) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
if (mFloatingView == null) {
mFloatingView = new FloatingIconView(mLauncher);
} else {
if (mFloatingView != null) {
mFloatingView.setTranslationX(0);
mFloatingView.setTranslationY(0);
mFloatingView.setScaleX(1);
@@ -414,7 +412,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
mFloatingView.setBackground(null);
}
Rect rect = new Rect();
mFloatingView.matchPositionOf(mLauncher, v, toggleVisibility, rect);
mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
true /* useDrawableAsIs */, -1 /* aspectRatio */, rect, mFloatingView);
int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
@@ -615,11 +614,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(false /* fromUnlock */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
definition.addRemoteAnimation(
WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(true /* fromUnlock */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
}
}
@@ -175,7 +175,7 @@ public class OverviewInteractionState {
}
}
public void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
private void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, swipeUpEnabled ? 1 : 0, 0).
sendToTarget();
@@ -60,6 +60,7 @@ public class RecentTasksList extends TaskStackChangeListener {
mBgThreadExecutor = BackgroundExecutor.get();
mKeyguardManager = new KeyguardManagerCompat(context);
mChangeId = 1;
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
/**
@@ -97,6 +98,7 @@ public class RecentTasksList extends TaskStackChangeListener {
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
// The list is up to date, callback with the same list
mMainThreadExecutor.execute(resultCallback);
return requestLoadId;
}
// Kick off task loading in the background
@@ -15,33 +15,29 @@
*/
package com.android.quickstep;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import androidx.annotation.WorkerThread;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
/**
* Singleton class to load and manage recents model.
*/
@@ -54,8 +50,8 @@ public class RecentsModel extends TaskStackChangeListener {
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(c -> new RecentsModel(c));
private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
private final MainThreadExecutor mMainThreadExecutor;
private ISystemUiProxy mSystemUiProxy;
@@ -68,9 +64,6 @@ public class RecentsModel extends TaskStackChangeListener {
private RecentsModel(Context context) {
mContext = context;
mMainThreadExecutor = new MainThreadExecutor();
HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
Process.THREAD_PRIORITY_BACKGROUND);
loaderThread.start();
@@ -168,6 +161,18 @@ public class RecentsModel extends TaskStackChangeListener {
});
}
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
if (task != null) {
task.thumbnail = snapshot;
}
}
}
public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
mSystemUiProxy = systemUiProxy;
}
@@ -239,4 +244,17 @@ public class RecentsModel extends TaskStackChangeListener {
+ ": ", e);
}
}
public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.remove(listener);
}
public interface TaskThumbnailChangeListener {
Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
}
}
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -24,14 +26,17 @@ import android.os.Handler;
import android.os.Looper;
import android.util.LruCache;
import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.uioverrides.RecentsUiFactory;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.function.Consumer;
/**
@@ -125,8 +130,9 @@ public class TaskIconCache {
return label;
}
// Skip loading content descriptions if accessibility is not enabled
if (!mAccessibilityManager.isEnabled()) {
// Skip loading content descriptions if accessibility is disabled unless low RAM recents
// is enabled.
if (!GO_LOW_RAM_RECENTS_ENABLED && !mAccessibilityManager.isEnabled()) {
return "";
}
@@ -26,6 +26,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -38,7 +39,7 @@ public class TaskThumbnailCache {
private final MainThreadExecutor mMainThreadExecutor;
private final int mCacheSize;
private final TaskKeyLruCache<ThumbnailData> mCache;
private final ThumbnailCache mCache;
private final HighResLoadingState mHighResLoadingState;
public static class HighResLoadingState {
@@ -98,7 +99,7 @@ public class TaskThumbnailCache {
Resources res = context.getResources();
mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
mCache = new TaskKeyLruCache<>(mCacheSize);
mCache = new ThumbnailCache(mCacheSize);
}
/**
@@ -106,13 +107,20 @@ public class TaskThumbnailCache {
*/
public void updateThumbnailInCache(Task task) {
Preconditions.assertUIThread();
// Fetch the thumbnail for this task and put it in the cache
updateThumbnailInBackground(task, true /* reducedResolution */, (t) -> {
mCache.put(task.key, t.thumbnail);
});
if (task.thumbnail == null) {
updateThumbnailInBackground(task.key, true /* reducedResolution */,
t -> task.thumbnail = t);
}
}
/**
* Synchronously updates the thumbnail in the cache if it is already there.
*/
public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
Preconditions.assertUIThread();
mCache.updateIfAlreadyInCache(taskId, thumbnail);
}
/**
* Asynchronously fetches the icon and other task data for the given {@param task}.
@@ -120,22 +128,33 @@ public class TaskThumbnailCache {
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
public ThumbnailLoadRequest updateThumbnailInBackground(Task task, boolean reducedResolution,
Consumer<Task> callback) {
public ThumbnailLoadRequest updateThumbnailInBackground(
Task task, Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
boolean reducedResolution = !mHighResLoadingState.isEnabled();
if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
// Nothing to load, the thumbnail is already high-resolution or matches what the
// request, so just callback
callback.accept(task);
callback.accept(task.thumbnail);
return null;
}
ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(task.key);
return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
task.thumbnail = t;
callback.accept(t);
});
}
private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
// Already cached, lets use that thumbnail
task.thumbnail = cachedThumbnail;
callback.accept(task);
callback.accept(cachedThumbnail);
return null;
}
@@ -144,14 +163,14 @@ public class TaskThumbnailCache {
@Override
public void run() {
ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
task.key.id, reducedResolution);
key.id, reducedResolution);
if (isCanceled()) {
// We don't call back to the provided callback in this case
return;
}
mMainThreadExecutor.execute(() -> {
task.thumbnail = thumbnail;
callback.accept(task);
mCache.put(key, thumbnail);
callback.accept(thumbnail);
onEnd();
});
}
@@ -196,4 +215,21 @@ public class TaskThumbnailCache {
this.reducedResolution = reducedResolution;
}
}
private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
public ThumbnailCache(int cacheSize) {
super(cacheSize);
}
/**
* Updates the cache entry if it is already present in the cache
*/
public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
ThumbnailData oldData = getCacheEntry(taskId);
if (oldData != null) {
putCacheEntry(taskId, thumbnailData);
}
}
}
}
@@ -40,7 +40,6 @@ public class MotionPauseDetector {
private final float mSpeedSomewhatFast;
private final float mSpeedFast;
private final float mMinDisplacementForPause;
private final float mMaxOrthogonalDisplacementForPause;
private final Alarm mForcePauseTimeout;
private Long mPreviousTime = null;
@@ -62,8 +61,6 @@ public class MotionPauseDetector {
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
mMaxOrthogonalDisplacementForPause = res.getDimension(
R.dimen.motion_pause_detector_max_orthogonal_displacement);
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
}
@@ -77,7 +74,6 @@ public class MotionPauseDetector {
if (mOnMotionPauseListener != null) {
mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
}
mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
}
/**
@@ -94,6 +90,7 @@ public class MotionPauseDetector {
if (mFirstOrthogonalPosition == null) {
mFirstOrthogonalPosition = orthogonalPosition;
}
mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
long time = SystemClock.uptimeMillis();
if (mPreviousTime != null && mPreviousPosition != null) {
long changeInTime = Math.max(1, time - mPreviousTime);
@@ -108,7 +105,6 @@ public class MotionPauseDetector {
}
mPreviousTime = time;
mPreviousPosition = position;
mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
}
private void checkMotionPaused(float velocity, float prevVelocity,
@@ -135,9 +131,11 @@ public class MotionPauseDetector {
}
}
boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause;
boolean passedMaxOrthogonalDisplacement =
totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause;
isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement;
boolean isDisplacementOrthogonal = totalDisplacement.orthogonal > totalDisplacement.primary;
if (!passedMinDisplacement || isDisplacementOrthogonal) {
mForcePauseTimeout.cancelAlarm();
isPaused = false;
}
updatePaused(isPaused);
}
@@ -19,6 +19,11 @@ package com.android.quickstep;
import static com.android.quickstep.QuickStepOnOffRule.Mode.BOTH;
import static com.android.quickstep.QuickStepOnOffRule.Mode.OFF;
import static com.android.quickstep.QuickStepOnOffRule.Mode.ON;
import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
import static org.junit.Assert.assertTrue;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -69,38 +74,52 @@ public class QuickStepOnOffRule implements TestRule {
@Override
public void evaluate() throws Throwable {
try {
if (mode == ON || mode == BOTH) {
evaluateWithQuickstepOn();
}
if (mode == OFF || mode == BOTH) {
evaluateWithQuickstepOff();
if (SwipeUpSetting.isSwipeUpSettingAvailable()) {
if (mode == ON || mode == BOTH) {
evaluateWithQuickstepOn();
}
if (mode == OFF || mode == BOTH) {
evaluateWithQuickstepOff();
}
} else {
// Execute without changing the setting, if the requested mode is
// compatible.
final boolean swipeUpEnabledDefaultValue =
SwipeUpSetting.isSwipeUpEnabledDefaultValue();
if (mode == BOTH ||
mode == ON && swipeUpEnabledDefaultValue ||
mode == OFF && !swipeUpEnabledDefaultValue) {
evaluateWithoutChangingSetting(base);
}
}
} finally {
overrideSwipeUpEnabled(null);
setSwipeUpSetting(null);
}
}
private void evaluateWithQuickstepOff() throws Throwable {
overrideSwipeUpEnabled(false);
public void setSwipeUpSetting(String value) {
assertTrue("Couldn't change Quickstep mode",
Settings.Secure.putString(
InstrumentationRegistry.getInstrumentation().getTargetContext().
getContentResolver(),
SWIPE_UP_SETTING_NAME,
value));
}
public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
base.evaluate();
}
private void evaluateWithQuickstepOff() throws Throwable {
setSwipeUpSetting("0");
evaluateWithoutChangingSetting(base);
}
private void evaluateWithQuickstepOn() throws Throwable {
overrideSwipeUpEnabled(true);
setSwipeUpSetting("1");
base.evaluate();
}
private void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride)
throws Throwable {
mLauncher.overrideSwipeUpEnabled(swipeUpEnabledOverride);
mMainThreadExecutor.execute(() -> OverviewInteractionState.INSTANCE.get(
InstrumentationRegistry.getInstrumentation().getTargetContext()).
notifySwipeUpSettingChanged(mLauncher.isSwipeUpEnabled()));
// TODO(b/124236673): avoid using sleep().
mLauncher.getDevice().waitForIdle();
Thread.sleep(2000);
mLauncher.getDevice().waitForIdle();
}
};
} else {
return base;