Merge "3/ Update OneHanded to use shell main thread"

This commit is contained in:
Winson Chung
2021-01-12 21:13:43 +00:00
committed by Android (Google) Code Review
24 changed files with 582 additions and 610 deletions

View File

@@ -18,6 +18,7 @@ package com.android.wm.shell.common;
import android.annotation.NonNull;
import android.os.Handler;
import android.os.Looper;
/** Executor implementation which is backed by a Handler. */
public class HandlerExecutor implements ShellExecutor {
@@ -49,4 +50,14 @@ public class HandlerExecutor implements ShellExecutor {
public void removeCallbacks(@NonNull Runnable r) {
mHandler.removeCallbacks(r);
}
@Override
public boolean hasCallback(Runnable r) {
return mHandler.hasCallbacks(r);
}
@Override
public Looper getLooper() {
return mHandler.getLooper();
}
}

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell.common;
import android.os.Looper;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -69,4 +71,14 @@ public interface ShellExecutor extends Executor {
* See {@link android.os.Handler#removeCallbacks}.
*/
void removeCallbacks(Runnable r);
/**
* See {@link android.os.Handler#hasCallbacks(Runnable)}.
*/
boolean hasCallback(Runnable r);
/**
* Returns the looper that this executor is running on.
*/
Looper getLooper();
}

View File

@@ -127,7 +127,6 @@ public class OneHandedAnimationController {
mSurfaceControlTransactionFactory;
private @TransitionDirection int mTransitionDirection;
private int mTransitionOffset;
private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
mLeash = leash;
@@ -231,11 +230,6 @@ public class OneHandedAnimationController {
return this;
}
OneHandedTransitionAnimator<T> setTransitionOffset(int offset) {
mTransitionOffset = offset;
return this;
}
T getStartValue() {
return mStartValue;
}

View File

@@ -26,7 +26,6 @@ import android.content.om.OverlayInfo;
import android.database.ContentObserver;
import android.graphics.Point;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -41,8 +40,10 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEventCallback;
import java.io.PrintWriter;
@@ -51,7 +52,7 @@ import java.util.concurrent.Executor;
/**
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
public class OneHandedController implements OneHanded {
public class OneHandedController {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -61,8 +62,8 @@ public class OneHandedController implements OneHanded {
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
private boolean mIsOneHandedEnabled;
private boolean mIsSwipeToNotificationEnabled;
private volatile boolean mIsOneHandedEnabled;
private volatile boolean mIsSwipeToNotificationEnabled;
private boolean mTaskChangeToExit;
private float mOffSetFraction;
@@ -73,7 +74,9 @@ public class OneHandedController implements OneHanded {
private final OneHandedTouchHandler mTouchHandler;
private final OneHandedTutorialHandler mTutorialHandler;
private final IOverlayManager mOverlayManager;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final ShellExecutor mMainExecutor;
private final Handler mMainHandler;
private final OneHandedImpl mImpl = new OneHandedImpl();
private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
private final AccessibilityManager mAccessibilityManager;
@@ -89,83 +92,10 @@ public class OneHandedController implements OneHanded {
}
};
private final ContentObserver mEnabledObserver = new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver());
OneHandedEvents.writeEvent(enabled
? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
: OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
setOneHandedEnabled(enabled);
// Also checks swipe to notification settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver()));
}
};
private final ContentObserver mTimeoutObserver = new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
mContext.getContentResolver());
int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId();
switch (newTimeout) {
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
break;
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
break;
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
break;
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
break;
default:
// do nothing
break;
}
OneHandedEvents.writeEvent(metricsId);
if (mTimeoutHandler != null) {
mTimeoutHandler.setTimeout(newTimeout);
}
}
};
private final ContentObserver mTaskChangeExitObserver = new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
mContext.getContentResolver());
OneHandedEvents.writeEvent(enabled
? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
: OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
setTaskChangeToExit(enabled);
}
};
private final ContentObserver mSwipeToNotificationEnabledObserver =
new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
final boolean enabled =
OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver());
setSwipeToNotificationEnabled(enabled);
// Also checks one handed mode settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver()));
}
};
private final ContentObserver mEnabledObserver;
private final ContentObserver mTimeoutObserver;
private final ContentObserver mTaskChangeExitObserver;
private final ContentObserver mSwipeToNotificationEnabledObserver;
private AccessibilityManager.AccessibilityStateChangeListener
mAccessibilityStateChangeListener =
@@ -188,33 +118,39 @@ public class OneHandedController implements OneHanded {
};
/**
* Creates {@link OneHandedController}, returns {@code null} if the feature is not supported.
* Creates {@link OneHanded}, returns {@code null} if the feature is not supported.
*/
@Nullable
public static OneHandedController create(
public static OneHanded create(
Context context, DisplayController displayController,
TaskStackListenerImpl taskStackListener, Executor executor) {
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor,
Handler mainHandler) {
if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
Slog.w(TAG, "Device doesn't support OneHanded feature");
return null;
}
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context);
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
mainExecutor);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler();
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
mainExecutor);
OneHandedGestureHandler gestureHandler = new OneHandedGestureHandler(
context, displayController);
context, displayController, mainExecutor);
OneHandedBackgroundPanelOrganizer oneHandedBackgroundPanelOrganizer =
new OneHandedBackgroundPanelOrganizer(context, displayController, executor);
new OneHandedBackgroundPanelOrganizer(context, displayController, mainExecutor);
OneHandedDisplayAreaOrganizer organizer = new OneHandedDisplayAreaOrganizer(
context, displayController, animationController, tutorialHandler, executor,
oneHandedBackgroundPanelOrganizer);
context, displayController, animationController, tutorialHandler,
oneHandedBackgroundPanelOrganizer, mainExecutor);
IOverlayManager overlayManager = IOverlayManager.Stub.asInterface(
ServiceManager.getService(Context.OVERLAY_SERVICE));
return new OneHandedController(context, displayController,
oneHandedBackgroundPanelOrganizer, organizer, touchHandler, tutorialHandler,
gestureHandler, overlayManager, taskStackListener);
gestureHandler, timeoutHandler, overlayManager, taskStackListener, mainExecutor,
mainHandler).mImpl;
}
@VisibleForTesting
@@ -225,8 +161,11 @@ public class OneHandedController implements OneHanded {
OneHandedTouchHandler touchHandler,
OneHandedTutorialHandler tutorialHandler,
OneHandedGestureHandler gestureHandler,
OneHandedTimeoutHandler timeoutHandler,
IOverlayManager overlayManager,
TaskStackListenerImpl taskStackListener) {
TaskStackListenerImpl taskStackListener,
ShellExecutor mainExecutor,
Handler mainHandler) {
mContext = context;
mBackgroundPanelOrganizer = backgroundPanelOrganizer;
mDisplayAreaOrganizer = displayAreaOrganizer;
@@ -235,6 +174,8 @@ public class OneHandedController implements OneHanded {
mTutorialHandler = tutorialHandler;
mGestureHandler = gestureHandler;
mOverlayManager = overlayManager;
mMainExecutor = mainExecutor;
mMainHandler = mainHandler;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
@@ -246,7 +187,13 @@ public class OneHandedController implements OneHanded {
mIsSwipeToNotificationEnabled =
OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
context.getContentResolver());
mTimeoutHandler = OneHandedTimeoutHandler.get();
mTimeoutHandler = timeoutHandler;
mEnabledObserver = getObserver(this::onEnabledSettingChanged);
mTimeoutObserver = getObserver(this::onTimeoutSettingChanged);
mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged);
mSwipeToNotificationEnabledObserver =
getObserver(this::onSwipeToNotificationEnabledSettingChanged);
mDisplayController.addDisplayChangingController(mRotationController);
@@ -298,18 +245,8 @@ public class OneHandedController implements OneHanded {
updateOneHandedEnabled();
}
@Override
public boolean isOneHandedEnabled() {
return mIsOneHandedEnabled;
}
@Override
public boolean isSwipeToNotificationEnabled() {
return mIsSwipeToNotificationEnabled;
}
@Override
public void startOneHanded() {
@VisibleForTesting
void startOneHanded() {
if (!mDisplayAreaOrganizer.isInOneHanded()) {
final int yOffSet = Math.round(getDisplaySize().y * mOffSetFraction);
mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
@@ -319,16 +256,15 @@ public class OneHandedController implements OneHanded {
}
}
@Override
public void stopOneHanded() {
@VisibleForTesting
void stopOneHanded() {
if (mDisplayAreaOrganizer.isInOneHanded()) {
mDisplayAreaOrganizer.scheduleOffset(0, 0);
mTimeoutHandler.removeTimer();
}
}
@Override
public void stopOneHanded(int event) {
private void stopOneHanded(int event) {
if (!mTaskChangeToExit && event == OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_APP_TAPS_OUT) {
//Task change exit not enable, do nothing and return here.
return;
@@ -341,18 +277,16 @@ public class OneHandedController implements OneHanded {
stopOneHanded();
}
@Override
public void setThreeButtonModeEnabled(boolean enabled) {
private void setThreeButtonModeEnabled(boolean enabled) {
mGestureHandler.onThreeButtonModeEnabled(enabled);
}
@Override
public void registerTransitionCallback(OneHandedTransitionCallback callback) {
@VisibleForTesting
void registerTransitionCallback(OneHandedTransitionCallback callback) {
mDisplayAreaOrganizer.registerTransitionCallback(callback);
}
@Override
public void registerGestureCallback(OneHandedGestureEventCallback callback) {
private void registerGestureCallback(OneHandedGestureEventCallback callback) {
mGestureHandler.setGestureEventListener(callback);
}
@@ -388,6 +322,80 @@ public class OneHandedController implements OneHanded {
.getSettingsSwipeToNotificationEnabled(mContext.getContentResolver()));
}
private ContentObserver getObserver(Runnable onChangeRunnable) {
return new ContentObserver(mMainHandler) {
@Override
public void onChange(boolean selfChange) {
onChangeRunnable.run();
}
};
}
private void onEnabledSettingChanged() {
final boolean enabled = OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver());
OneHandedEvents.writeEvent(enabled
? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_ON
: OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_ENABLED_OFF);
setOneHandedEnabled(enabled);
// Also checks swipe to notification settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver()));
}
private void onTimeoutSettingChanged() {
final int newTimeout = OneHandedSettingsUtil.getSettingsOneHandedModeTimeout(
mContext.getContentResolver());
int metricsId = OneHandedEvents.OneHandedSettingsTogglesEvent.INVALID.getId();
switch (newTimeout) {
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_NEVER;
break;
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4;
break;
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8;
break;
case OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_LONG_IN_SECONDS:
metricsId = OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12;
break;
default:
// do nothing
break;
}
OneHandedEvents.writeEvent(metricsId);
if (mTimeoutHandler != null) {
mTimeoutHandler.setTimeout(newTimeout);
}
}
private void onTaskChangeExitSettingChanged() {
final boolean enabled = OneHandedSettingsUtil.getSettingsTapsAppToExit(
mContext.getContentResolver());
OneHandedEvents.writeEvent(enabled
? OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_ON
: OneHandedEvents.EVENT_ONE_HANDED_SETTINGS_APP_TAPS_EXIT_OFF);
setTaskChangeToExit(enabled);
}
private void onSwipeToNotificationEnabledSettingChanged() {
final boolean enabled =
OneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver());
setSwipeToNotificationEnabled(enabled);
// Also checks one handed mode settings since they all need gesture overlay.
setEnabledGesturalOverlay(
enabled || OneHandedSettingsUtil.getSettingsOneHandedModeEnabled(
mContext.getContentResolver()));
}
private void setupTimeoutListener() {
mTimeoutHandler.registerTimeoutListener(timeoutTime -> {
stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_TIMEOUT_OUT);
@@ -451,8 +459,7 @@ public class OneHandedController implements OneHanded {
}
}
@Override
public void dump(@NonNull PrintWriter pw) {
private void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mOffSetFraction=");
@@ -489,4 +496,68 @@ public class OneHandedController implements OneHanded {
}
}
}
@ExternalThread
private class OneHandedImpl implements OneHanded {
@Override
public boolean isOneHandedEnabled() {
// This is volatile so return directly
return mIsOneHandedEnabled;
}
@Override
public boolean isSwipeToNotificationEnabled() {
// This is volatile so return directly
return mIsSwipeToNotificationEnabled;
}
@Override
public void startOneHanded() {
mMainExecutor.execute(() -> {
OneHandedController.this.startOneHanded();
});
}
@Override
public void stopOneHanded() {
mMainExecutor.execute(() -> {
OneHandedController.this.stopOneHanded();
});
}
@Override
public void stopOneHanded(int event) {
mMainExecutor.execute(() -> {
OneHandedController.this.stopOneHanded(event);
});
}
@Override
public void setThreeButtonModeEnabled(boolean enabled) {
mMainExecutor.execute(() -> {
OneHandedController.this.setThreeButtonModeEnabled(enabled);
});
}
@Override
public void registerTransitionCallback(OneHandedTransitionCallback callback) {
mMainExecutor.execute(() -> {
OneHandedController.this.registerTransitionCallback(callback);
});
}
@Override
public void registerGestureCallback(OneHandedGestureEventCallback callback) {
mMainExecutor.execute(() -> {
OneHandedController.this.registerGestureCallback(callback);
});
}
@Override
public void dump(@NonNull PrintWriter pw) {
mMainExecutor.execute(() -> {
OneHandedController.this.dump(pw);
});
}
}
}

View File

@@ -24,8 +24,6 @@ import static com.android.wm.shell.onehanded.OneHandedAnimationController.TRANSI
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Log;
@@ -39,9 +37,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -64,17 +62,9 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
private static final String ONE_HANDED_MODE_TRANSLATE_ANIMATION_DURATION =
"persist.debug.one_handed_translate_animation_duration";
@VisibleForTesting
static final int MSG_RESET_IMMEDIATE = 1;
@VisibleForTesting
static final int MSG_OFFSET_ANIMATE = 2;
@VisibleForTesting
static final int MSG_OFFSET_FINISH = 3;
private final Rect mLastVisualDisplayBounds = new Rect();
private final Rect mDefaultDisplayBounds = new Rect();
private Handler mUpdateHandler;
private boolean mIsInOneHanded;
private int mEnterExitAnimationDurationMs;
@@ -101,8 +91,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getLeash());
if (mAnimationController.isAnimatorsConsumed()) {
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
obtainArgsFromAnimator(animator)));
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
}
}
@@ -111,52 +101,22 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mAnimationController.removeAnimator(animator.getLeash());
if (mAnimationController.isAnimatorsConsumed()) {
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_FINISH,
obtainArgsFromAnimator(animator)));
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
}
}
};
@SuppressWarnings("unchecked")
private Handler.Callback mUpdateCallback = (msg) -> {
SomeArgs args = (SomeArgs) msg.obj;
final Rect currentBounds = args.arg1 != null ? (Rect) args.arg1 : mDefaultDisplayBounds;
final WindowContainerTransaction wctFromRotate = (WindowContainerTransaction) args.arg2;
final int yOffset = args.argi2;
final int direction = args.argi3;
switch (msg.what) {
case MSG_RESET_IMMEDIATE:
resetWindowsOffset(wctFromRotate);
mDefaultDisplayBounds.set(currentBounds);
mLastVisualDisplayBounds.set(currentBounds);
finishOffset(0, TRANSITION_DIRECTION_EXIT);
break;
case MSG_OFFSET_ANIMATE:
final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
mDefaultDisplayBounds.top + yOffset,
mDefaultDisplayBounds.right,
mDefaultDisplayBounds.bottom + yOffset);
offsetWindows(currentBounds, toBounds, direction, mEnterExitAnimationDurationMs);
break;
case MSG_OFFSET_FINISH:
finishOffset(yOffset, direction);
break;
}
args.recycle();
return true;
};
/**
* Constructor of OneHandedDisplayAreaOrganizer
*/
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
OneHandedAnimationController animationController,
OneHandedTutorialHandler tutorialHandler, Executor executor,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer) {
super(executor);
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
OneHandedTutorialHandler tutorialHandler,
OneHandedBackgroundPanelOrganizer oneHandedBackgroundGradientOrganizer,
ShellExecutor mainExecutor) {
super(mainExecutor);
mAnimationController = animationController;
mDisplayController = displayController;
mDefaultDisplayBounds.set(getDisplayBounds());
@@ -176,12 +136,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@NonNull SurfaceControl leash) {
Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
Objects.requireNonNull(leash, "leash must not be null");
synchronized (this) {
if (mDisplayAreaMap.get(displayAreaInfo) == null) {
// mDefaultDisplayBounds may out of date after removeDisplayChangingController()
mDefaultDisplayBounds.set(getDisplayBounds());
mDisplayAreaMap.put(displayAreaInfo, leash);
}
if (mDisplayAreaMap.get(displayAreaInfo) == null) {
// mDefaultDisplayBounds may out of date after removeDisplayChangingController()
mDefaultDisplayBounds.set(getDisplayBounds());
mDisplayAreaMap.put(displayAreaInfo, leash);
}
}
@@ -189,13 +147,11 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
Objects.requireNonNull(displayAreaInfo,
"Requires valid displayArea, and displayArea must not be null");
synchronized (this) {
if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
return;
}
mDisplayAreaMap.remove(displayAreaInfo);
if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
return;
}
mDisplayAreaMap.remove(displayAreaInfo);
}
@Override
@@ -212,7 +168,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
@Override
public void unregisterOrganizer() {
super.unregisterOrganizer();
mUpdateHandler.post(() -> resetWindowsOffset(null));
resetWindowsOffset(null);
}
/**
@@ -230,14 +186,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
final boolean isOrientationDiff = Math.abs(fromRotation - toRotation) % 2 == 1;
if (isOrientationDiff) {
newBounds.set(newBounds.left, newBounds.top, newBounds.bottom, newBounds.right);
SomeArgs args = SomeArgs.obtain();
args.arg1 = newBounds;
args.arg2 = wct;
args.argi1 = 0 /* xOffset */;
args.argi2 = 0 /* yOffset */;
args.argi3 = TRANSITION_DIRECTION_EXIT;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESET_IMMEDIATE, args));
resetWindowsOffset(wct);
mDefaultDisplayBounds.set(newBounds);
mLastVisualDisplayBounds.set(newBounds);
finishOffset(0, TRANSITION_DIRECTION_EXIT);
}
}
@@ -246,79 +198,65 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
* Directly perform manipulation/offset on the leash.
*/
public void scheduleOffset(int xOffset, int yOffset) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = getLastVisualDisplayBounds();
args.argi1 = xOffset;
args.argi2 = yOffset;
args.argi3 = yOffset > 0 ? TRANSITION_DIRECTION_TRIGGER : TRANSITION_DIRECTION_EXIT;
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
}
final Rect toBounds = new Rect(mDefaultDisplayBounds.left,
mDefaultDisplayBounds.top + yOffset,
mDefaultDisplayBounds.right,
mDefaultDisplayBounds.bottom + yOffset);
final Rect fromBounds = getLastVisualDisplayBounds() != null
? getLastVisualDisplayBounds()
: mDefaultDisplayBounds;
final int direction = yOffset > 0
? TRANSITION_DIRECTION_TRIGGER
: TRANSITION_DIRECTION_EXIT;
private void offsetWindows(Rect fromBounds, Rect toBounds, int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of this "
+ "directly");
}
synchronized (this) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mDisplayAreaMap.forEach(
(key, leash) -> {
animateWindows(leash, fromBounds, toBounds, direction, durationMs);
wct.setBounds(key.token, toBounds);
});
applyTransaction(wct);
}
final WindowContainerTransaction wct = new WindowContainerTransaction();
mDisplayAreaMap.forEach(
(key, leash) -> {
animateWindows(leash, fromBounds, toBounds, direction,
mEnterExitAnimationDurationMs);
wct.setBounds(key.token, toBounds);
});
applyTransaction(wct);
}
private void resetWindowsOffset(WindowContainerTransaction wct) {
synchronized (this) {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mDisplayAreaMap.forEach(
(key, leash) -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
mAnimationController.getAnimatorMap().remove(leash);
if (animator != null && animator.isRunning()) {
animator.cancel();
}
tx.setPosition(leash, 0, 0)
.setWindowCrop(leash, -1/* reset */, -1/* reset */);
// DisplayRotationController will applyTransaction() after finish rotating
if (wct != null) {
wct.setBounds(key.token, null/* reset */);
}
});
tx.apply();
}
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
mDisplayAreaMap.forEach(
(key, leash) -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
mAnimationController.getAnimatorMap().remove(leash);
if (animator != null && animator.isRunning()) {
animator.cancel();
}
tx.setPosition(leash, 0, 0)
.setWindowCrop(leash, -1/* reset */, -1/* reset */);
// DisplayRotationController will applyTransaction() after finish rotating
if (wct != null) {
wct.setBounds(key.token, null/* reset */);
}
});
tx.apply();
}
private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
@OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException("Callers should call scheduleOffset() instead of "
+ "this directly");
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.addOneHandedAnimationCallback(mOneHandedAnimationCallback)
.addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
.addOneHandedAnimationCallback(
mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
.setDuration(durationMs)
.start();
}
mUpdateHandler.post(() -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.addOneHandedAnimationCallback(mOneHandedAnimationCallback)
.addOneHandedAnimationCallback(mTutorialHandler.getAnimationCallback())
.addOneHandedAnimationCallback(
mBackgroundPanelOrganizer.getOneHandedAnimationCallback())
.setDuration(durationMs)
.start();
}
});
}
private void finishOffset(int offset,
@VisibleForTesting
void finishOffset(int offset,
@OneHandedAnimationController.TransitionDirection int direction) {
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
throw new RuntimeException(
"Callers should call scheduleOffset() instead of this directly.");
}
// Only finishOffset() can update mIsInOneHanded to ensure the state is handle in sequence,
// the flag *MUST* be updated before dispatch mTransitionCallbacks
mIsInOneHanded = (offset > 0 || direction == TRANSITION_DIRECTION_TRIGGER);
@@ -361,11 +299,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
return new Rect(0, 0, realSize.x, realSize.y);
}
@VisibleForTesting
void setUpdateHandler(Handler updateHandler) {
mUpdateHandler = updateHandler;
}
/**
* Register transition callback
*/
@@ -373,16 +306,6 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer {
mTransitionCallbacks.add(callback);
}
private SomeArgs obtainArgsFromAnimator(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = animator.getDestinationBounds();
args.argi1 = 0 /* xOffset */;
args.argi2 = animator.getDestinationOffset();
args.argi3 = animator.getTransitionDirection();
return args;
}
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG + "states: ");

View File

@@ -41,6 +41,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
/**
* The class manage swipe up and down gesture for 3-Button mode navigation,
@@ -70,7 +71,8 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
InputMonitor mInputMonitor;
@VisibleForTesting
InputEventReceiver mInputEventReceiver;
private DisplayController mDisplayController;
private final DisplayController mDisplayController;
private final ShellExecutor mMainExecutor;
@VisibleForTesting
@Nullable
OneHandedGestureEventCallback mGestureEventCallback;
@@ -84,8 +86,10 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
* @param context {@link Context}
* @param displayController {@link DisplayController}
*/
public OneHandedGestureHandler(Context context, DisplayController displayController) {
public OneHandedGestureHandler(Context context, DisplayController displayController,
ShellExecutor mainExecutor) {
mDisplayController = displayController;
mMainExecutor = mainExecutor;
displayController.addDisplayChangingController(this);
mNavGestureHeight = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.navigation_bar_gesture_larger_height);
@@ -93,6 +97,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
R.dimen.gestures_onehanded_drag_threshold);
final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
mSquaredSlop = slop * slop;
updateIsEnabled();
}
@@ -217,7 +222,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-gesture-offset", DEFAULT_DISPLAY);
mInputEventReceiver = new EventReceiver(
mInputMonitor.getInputChannel(), Looper.getMainLooper());
mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
}
}
@@ -233,6 +238,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback,
mRotation = toRotation;
}
// TODO: Use BatchedInputEventReceiver
private class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);

View File

@@ -1,62 +0,0 @@
/*
* Copyright (C) 2020 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.wm.shell.onehanded;
import android.os.Handler;
import android.os.HandlerThread;
/**
* Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton
* foreground thread for each process for updating one handed.
*/
public class OneHandedThread extends HandlerThread {
private static OneHandedThread sInstance;
private static Handler sHandler;
private OneHandedThread() {
super("OneHanded");
}
private static void ensureThreadLocked() {
if (sInstance == null) {
sInstance = new OneHandedThread();
sInstance.start();
sHandler = new Handler(sInstance.getLooper());
}
}
/**
* @return the static update thread instance
*/
public static OneHandedThread get() {
synchronized (OneHandedThread.class) {
ensureThreadLocked();
return sInstance;
}
}
/**
* @return the static update thread handler instance
*/
public static Handler getHandler() {
synchronized (OneHandedThread.class) {
ensureThreadLocked();
return sHandler;
}
}
}

View File

@@ -19,12 +19,12 @@ package com.android.wm.shell.onehanded;
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -35,18 +35,15 @@ import java.util.concurrent.TimeUnit;
*/
public class OneHandedTimeoutHandler {
private static final String TAG = "OneHandedTimeoutHandler";
private static boolean sIsDragging = false;
// Default timeout is ONE_HANDED_TIMEOUT_MEDIUM
private static @OneHandedSettingsUtil.OneHandedTimeout int sTimeout =
ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
private static long sTimeoutMs = TimeUnit.SECONDS.toMillis(sTimeout);
private static OneHandedTimeoutHandler sInstance;
private static List<TimeoutListener> sListeners = new ArrayList<>();
@VisibleForTesting
static final int ONE_HANDED_TIMEOUT_STOP_MSG = 1;
@VisibleForTesting
static Handler sHandler;
private final ShellExecutor mMainExecutor;
// Default timeout is ONE_HANDED_TIMEOUT_MEDIUM
private @OneHandedSettingsUtil.OneHandedTimeout int mTimeout =
ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
private long mTimeoutMs = TimeUnit.SECONDS.toMillis(mTimeout);
private final Runnable mTimeoutRunnable = this::onStop;
private List<TimeoutListener> mListeners = new ArrayList<>();
/**
* Get the current config of timeout
@@ -54,7 +51,7 @@ public class OneHandedTimeoutHandler {
* @return timeout of current config
*/
public @OneHandedSettingsUtil.OneHandedTimeout int getTimeout() {
return sTimeout;
return mTimeout;
}
/**
@@ -69,32 +66,36 @@ public class OneHandedTimeoutHandler {
void onTimeout(int timeoutTime);
}
public OneHandedTimeoutHandler(ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
}
/**
* Set the specific timeout of {@link OneHandedSettingsUtil.OneHandedTimeout}
*/
public static void setTimeout(@OneHandedSettingsUtil.OneHandedTimeout int timeout) {
sTimeout = timeout;
sTimeoutMs = TimeUnit.SECONDS.toMillis(sTimeout);
public void setTimeout(@OneHandedSettingsUtil.OneHandedTimeout int timeout) {
mTimeout = timeout;
mTimeoutMs = TimeUnit.SECONDS.toMillis(mTimeout);
resetTimer();
}
/**
* Reset the timer when one handed trigger or user is operating in some conditions
*/
public static void removeTimer() {
sHandler.removeMessages(ONE_HANDED_TIMEOUT_STOP_MSG);
public void removeTimer() {
mMainExecutor.removeCallbacks(mTimeoutRunnable);
}
/**
* Reset the timer when one handed trigger or user is operating in some conditions
*/
public static void resetTimer() {
public void resetTimer() {
removeTimer();
if (sTimeout == OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
if (mTimeout == OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
return;
}
if (sTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
sHandler.sendEmptyMessageDelayed(ONE_HANDED_TIMEOUT_STOP_MSG, sTimeoutMs);
if (mTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
mMainExecutor.executeDelayed(mTimeoutRunnable, mTimeoutMs);
}
}
@@ -103,47 +104,19 @@ public class OneHandedTimeoutHandler {
*
* @param listener the listener be sent events when times up
*/
public static void registerTimeoutListener(TimeoutListener listener) {
sListeners.add(listener);
public void registerTimeoutListener(TimeoutListener listener) {
mListeners.add(listener);
}
/**
* Private constructor due to Singleton pattern
*/
private OneHandedTimeoutHandler() {
@VisibleForTesting
boolean hasScheduledTimeout() {
return mMainExecutor.hasCallback(mTimeoutRunnable);
}
/**
* Singleton pattern to get {@link OneHandedTimeoutHandler} instance
*
* @return the static update thread instance
*/
public static OneHandedTimeoutHandler get() {
synchronized (OneHandedTimeoutHandler.class) {
if (sInstance == null) {
sInstance = new OneHandedTimeoutHandler();
}
if (sHandler == null) {
sHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == ONE_HANDED_TIMEOUT_STOP_MSG) {
onStop();
}
}
};
if (sTimeout != OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER) {
sHandler.sendEmptyMessageDelayed(ONE_HANDED_TIMEOUT_STOP_MSG, sTimeoutMs);
}
}
return sInstance;
}
}
private static void onStop() {
for (int i = sListeners.size() - 1; i >= 0; i--) {
final TimeoutListener listener = sListeners.get(i);
listener.onTimeout(sTimeout);
private void onStop() {
for (int i = mListeners.size() - 1; i >= 0; i--) {
final TimeoutListener listener = mListeners.get(i);
listener.onTimeout(mTimeout);
}
}
@@ -151,9 +124,9 @@ public class OneHandedTimeoutHandler {
final String innerPrefix = " ";
pw.println(TAG + "states: ");
pw.print(innerPrefix + "sTimeout=");
pw.println(sTimeout);
pw.println(mTimeout);
pw.print(innerPrefix + "sListeners=");
pw.println(sListeners);
pw.println(mListeners);
}
}

View File

@@ -30,6 +30,8 @@ import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
/**
@@ -41,7 +43,8 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
private static final String TAG = "OneHandedTouchHandler";
private final Rect mLastUpdatedBounds = new Rect();
private OneHandedTimeoutHandler mTimeoutHandler;
private final OneHandedTimeoutHandler mTimeoutHandler;
private final ShellExecutor mMainExecutor;
@VisibleForTesting
InputMonitor mInputMonitor;
@@ -54,8 +57,10 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
private boolean mIsOnStopTransitioning;
private boolean mIsInOutsideRegion;
public OneHandedTouchHandler() {
mTimeoutHandler = OneHandedTimeoutHandler.get();
public OneHandedTouchHandler(OneHandedTimeoutHandler timeoutHandler,
ShellExecutor mainExecutor) {
mTimeoutHandler = timeoutHandler;
mMainExecutor = mainExecutor;
updateIsEnabled();
}
@@ -128,7 +133,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-touch", DEFAULT_DISPLAY);
mInputEventReceiver = new EventReceiver(
mInputMonitor.getInputChannel(), Looper.getMainLooper());
mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
}
}
@@ -150,6 +155,7 @@ public class OneHandedTouchHandler implements OneHandedTransitionCallback {
pw.println(mLastUpdatedBounds);
}
// TODO: Use BatchedInputEventReceiver
private class EventReceiver extends InputEventReceiver {
EventReceiver(InputChannel channel, Looper looper) {
super(channel, looper);

View File

@@ -21,7 +21,6 @@ import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemProperties;
import android.provider.Settings;
import android.view.Gravity;
@@ -36,6 +35,7 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
@@ -57,7 +57,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
private View mTutorialView;
private Point mDisplaySize = new Point();
private Handler mUpdateHandler;
private ContentResolver mContentResolver;
private boolean mCanShowTutorial;
private String mStartOneHandedDescription;
@@ -82,69 +81,67 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
@Override
public void onTutorialAnimationUpdate(int offset) {
mUpdateHandler.post(() -> onAnimationUpdate(offset));
onAnimationUpdate(offset);
}
@Override
public void onOneHandedAnimationStart(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
mUpdateHandler.post(() -> {
final Rect startValue = (Rect) animator.getStartValue();
if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
mTriggerState = (startValue.top == 0)
? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
createTutorialTarget();
}
final Rect startValue = (Rect) animator.getStartValue();
if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
mTriggerState = (startValue.top == 0)
? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
createTutorialTarget();
}
});
}
}
};
public OneHandedTutorialHandler(Context context) {
public OneHandedTutorialHandler(Context context, ShellExecutor mainExecutor) {
context.getDisplay().getRealSize(mDisplaySize);
mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
mUpdateHandler = new Handler();
mWindowManager = context.getSystemService(WindowManager.class);
mAccessibilityManager = (AccessibilityManager)
context.getSystemService(Context.ACCESSIBILITY_SERVICE);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
mStartOneHandedDescription = context.getResources().getString(
R.string.accessibility_action_start_one_handed);
mStopOneHandedDescription = context.getResources().getString(
R.string.accessibility_action_stop_one_handed);
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
? false : true;
final float offsetPercentageConfig = context.getResources().getFraction(
R.fraction.config_one_handed_offset, 1, 1);
final int sysPropPercentageConfig = SystemProperties.getInt(
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
mTutorialAreaHeight = Math.round(mDisplaySize.y * (sysPropPercentageConfig / 100.0f));
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
mTargetViewContainer.addView(mTutorialView);
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
? false : true;
mStartOneHandedDescription = context.getResources().getString(
R.string.accessibility_action_start_one_handed);
mStopOneHandedDescription = context.getResources().getString(
R.string.accessibility_action_stop_one_handed);
mainExecutor.execute(() -> {
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial,
null);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
mTargetViewContainer.addView(mTutorialView);
});
}
@Override
public void onStartFinished(Rect bounds) {
mUpdateHandler.post(() -> {
updateFinished(View.VISIBLE, 0f);
updateTutorialCount();
announcementForScreenReader(true);
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
});
updateFinished(View.VISIBLE, 0f);
updateTutorialCount();
announcementForScreenReader(true);
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
}
@Override
public void onStopFinished(Rect bounds) {
mUpdateHandler.post(() -> {
updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
announcementForScreenReader(false);
removeTutorialFromWindowManager();
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
});
updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
announcementForScreenReader(false);
removeTutorialFromWindowManager();
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
}
private void updateFinished(int visible, float finalPosition) {

View File

@@ -53,7 +53,7 @@ public class PipAccessibilityInteractionConnection {
private List<AccessibilityNodeInfo> mAccessibilityNodeInfoList;
private final Context mContext;
private final ShellExecutor mShellMainExcutor;
private final ShellExecutor mMainExcutor;
private final @NonNull PipBoundsState mPipBoundsState;
private final PipMotionHelper mMotionHelper;
private final PipTaskOrganizer mTaskOrganizer;
@@ -72,9 +72,9 @@ public class PipAccessibilityInteractionConnection {
@NonNull PipBoundsState pipBoundsState, PipMotionHelper motionHelper,
PipTaskOrganizer taskOrganizer, PipSnapAlgorithm snapAlgorithm,
AccessibilityCallbacks callbacks, Runnable updateMovementBoundCallback,
ShellExecutor shellMainExcutor) {
ShellExecutor mainExcutor) {
mContext = context;
mShellMainExcutor = shellMainExcutor;
mMainExcutor = mainExcutor;
mPipBoundsState = pipBoundsState;
mMotionHelper = motionHelper;
mTaskOrganizer = taskOrganizer;
@@ -271,7 +271,7 @@ public class PipAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec,
Bundle arguments) throws RemoteException {
mShellMainExcutor.execute(() -> {
mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this
.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId, bounds,
interactionId, callback, flags, interrogatingPid, interrogatingTid,
@@ -285,7 +285,7 @@ public class PipAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
mShellMainExcutor.execute(() -> {
mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByViewId(
accessibilityNodeId, viewId, bounds, interactionId, callback, flags,
interrogatingPid, interrogatingTid, spec);
@@ -298,7 +298,7 @@ public class PipAccessibilityInteractionConnection {
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
mShellMainExcutor.execute(() -> {
mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.findAccessibilityNodeInfosByText(
accessibilityNodeId, text, bounds, interactionId, callback, flags,
interrogatingPid, interrogatingTid, spec);
@@ -310,7 +310,7 @@ public class PipAccessibilityInteractionConnection {
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
mShellMainExcutor.execute(() -> {
mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.findFocus(accessibilityNodeId, focusType,
bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
spec);
@@ -322,7 +322,7 @@ public class PipAccessibilityInteractionConnection {
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid, MagnificationSpec spec)
throws RemoteException {
mShellMainExcutor.execute(() -> {
mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.focusSearch(accessibilityNodeId,
direction,
bounds, interactionId, callback, flags, interrogatingPid, interrogatingTid,
@@ -335,7 +335,7 @@ public class PipAccessibilityInteractionConnection {
Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid) throws RemoteException {
mShellMainExcutor.execute(() -> {
mMainExcutor.execute(() -> {
PipAccessibilityInteractionConnection.this.performAccessibilityAction(
accessibilityNodeId, action, arguments, interactionId, callback, flags,
interrogatingPid, interrogatingTid);

View File

@@ -25,6 +25,8 @@ import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,11 +51,14 @@ public class OneHandedAnimationControllerTest extends OneHandedTestCase {
@Mock
private SurfaceControl mMockLeash;
@Mock
private ShellExecutor mMainExecutor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mTutorialHandler = new OneHandedTutorialHandler(mContext);
mTutorialHandler = new OneHandedTutorialHandler(mContext, mMainExecutor);
mOneHandedAnimationController = new OneHandedAnimationController(mContext);
}

View File

@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.om.IOverlayManager;
import android.os.Handler;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -33,6 +34,7 @@ import android.view.Display;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -45,7 +47,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedControllerTest extends OneHandedTestCase {
Display mDisplay;
OneHandedController mOneHandedController;
@@ -69,11 +70,16 @@ public class OneHandedControllerTest extends OneHandedTestCase {
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
Handler mMockShellMainHandler;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDisplay = mContext.getDisplay();
mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMockShellMainExecutor));
OneHandedController oneHandedController = new OneHandedController(
mContext,
mMockDisplayController,
@@ -82,10 +88,12 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mMockTouchHandler,
mMockTutorialHandler,
mMockGestureHandler,
mTimeoutHandler,
mMockOverlayManager,
mMockTaskStackListener);
mMockTaskStackListener,
mMockShellMainExecutor,
mMockShellMainHandler);
mOneHandedController = Mockito.spy(oneHandedController);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockDisplayAreaOrganizer.isInOneHanded()).thenReturn(false);
@@ -97,7 +105,7 @@ public class OneHandedControllerTest extends OneHandedTestCase {
mContext);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
mContext, mMockDisplayController, animationController, mMockTutorialHandler,
Runnable::run, mMockBackgroundOrganizer);
mMockBackgroundOrganizer, mMockShellMainExecutor);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}

View File

@@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
@@ -53,7 +54,6 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
static final int DISPLAY_WIDTH = 1000;
static final int DISPLAY_HEIGHT = 1000;
@@ -82,9 +82,8 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
WindowContainerTransaction mMockWindowContainerTransaction;
@Mock
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
Handler mSpyUpdateHandler;
Handler.Callback mUpdateCallback = (msg) -> false;
@Mock
ShellExecutor mMockShellMainExecutor;
@Before
public void setUp() throws Exception {
@@ -110,19 +109,17 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
mDisplayAreaOrganizer = spy(new OneHandedDisplayAreaOrganizer(mContext,
mMockDisplayController,
mMockAnimationController,
mTutorialHandler,
Runnable::run, mMockBackgroundOrganizer);
mSpyUpdateHandler = spy(new Handler(OneHandedThread.get().getLooper(), mUpdateCallback));
mDisplayAreaOrganizer.setUpdateHandler(mSpyUpdateHandler);
mMockBackgroundOrganizer,
mMockShellMainExecutor));
}
@Test
public void testOnDisplayAreaAppeared() {
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mTestableLooper.processAllMessages();
verify(mMockAnimationController, never()).getAnimator(any(), any(), any());
}
@@ -130,31 +127,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
@Test
public void testOnDisplayAreaVanished() {
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mTestableLooper.processAllMessages();
mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo);
assertThat(mDisplayAreaOrganizer.mDisplayAreaMap).isEmpty();
}
@Test
public void testScheduleOffset() {
final int xOffSet = 0;
final int yOffSet = 100;
mDisplayAreaOrganizer.scheduleOffset(xOffSet, yOffSet);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
}
@Test
public void testRotation_portrait_0_to_landscape_90() {
when(mMockLeash.isValid()).thenReturn(false);
// Rotate 0 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_90,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -163,9 +147,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 0 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_270,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -174,9 +156,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_90,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -185,9 +165,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_270,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -196,9 +174,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_0,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -207,9 +183,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_180,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -218,9 +192,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_0,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -229,9 +201,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_180,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler).sendMessage(any());
verify(mDisplayAreaOrganizer).finishOffset(anyInt(), anyInt());
}
@Test
@@ -240,9 +210,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 0 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_0,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -251,9 +219,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 0 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_0, Surface.ROTATION_180,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -262,9 +228,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 180
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_180,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -273,9 +237,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 180 -> 0
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_180, Surface.ROTATION_0,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -284,9 +246,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_90,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -295,9 +255,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 90 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_90, Surface.ROTATION_270,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -306,9 +264,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 270
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_270,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
@Test
@@ -317,8 +273,6 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
// Rotate 270 -> 90
mDisplayAreaOrganizer.onRotateDisplay(Surface.ROTATION_270, Surface.ROTATION_90,
mMockWindowContainerTransaction);
mTestableLooper.processAllMessages();
verify(mSpyUpdateHandler, never()).sendMessage(any());
verify(mDisplayAreaOrganizer, never()).finishOffset(anyInt(), anyInt());
}
}

View File

@@ -26,6 +26,7 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Ignore;
@@ -36,17 +37,20 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedGestureHandlerTest extends OneHandedTestCase {
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
@Mock
DisplayController mMockDisplayController;
@Mock
ShellExecutor mMockShellMainExecutor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mTutorialHandler = new OneHandedTutorialHandler(mContext);
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor);
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
mMockShellMainExecutor);
}
@Test

View File

@@ -38,7 +38,6 @@ import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedSettingsUtilTest extends OneHandedTestCase {
ContentResolver mContentResolver;
ContentObserver mContentObserver;

View File

@@ -20,43 +20,46 @@ import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TI
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS;
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_NEVER;
import static com.android.wm.shell.onehanded.OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS;
import static com.android.wm.shell.onehanded.OneHandedTimeoutHandler.ONE_HANDED_TIMEOUT_STOP_MSG;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.verify;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
OneHandedTimeoutHandler mTimeoutHandler;
private OneHandedTimeoutHandler mTimeoutHandler;
private ShellExecutor mMainExecutor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
}
@Test
public void testTimeoutHandler_isNotNull() {
assertThat(OneHandedTimeoutHandler.get()).isNotNull();
mMainExecutor = new TestShellExecutor();
mTimeoutHandler = Mockito.spy(new OneHandedTimeoutHandler(mMainExecutor));
}
@Test
public void testTimeoutHandler_getTimeout_defaultMedium() {
assertThat(OneHandedTimeoutHandler.get().getTimeout()).isEqualTo(
assertThat(mTimeoutHandler.getTimeout()).isEqualTo(
ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
}
@@ -64,28 +67,29 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
public void testTimeoutHandler_setNewTime_resetTimer() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
verify(mTimeoutHandler).resetTimer();
assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
@Test
public void testSetTimeoutNever_neverResetTimer() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_NEVER);
assertThat(!mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
assertFalse(mTimeoutHandler.hasScheduledTimeout());
}
@Test
public void testSetTimeoutShort() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS);
verify(mTimeoutHandler).resetTimer();
assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
assertThat(mTimeoutHandler.getTimeout()).isEqualTo(ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS);
assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
@Test
public void testSetTimeoutMedium() {
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
verify(mTimeoutHandler).resetTimer();
assertThat(mTimeoutHandler.sHandler.hasMessages(
ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS)).isNotNull();
assertThat(mTimeoutHandler.getTimeout()).isEqualTo(ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
@Test
@@ -96,10 +100,38 @@ public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
@Test
public void testDragging_shouldRemoveAndSendEmptyMessageDelay() {
final boolean isDragging = true;
mTimeoutHandler.setTimeout(ONE_HANDED_TIMEOUT_LONG_IN_SECONDS);
mTimeoutHandler.resetTimer();
TestableLooper.get(this).processAllMessages();
assertThat(mTimeoutHandler.sHandler.hasMessages(ONE_HANDED_TIMEOUT_STOP_MSG)).isNotNull();
assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
private class TestShellExecutor implements ShellExecutor {
private ArrayList<Runnable> mExecuted = new ArrayList<>();
private ArrayList<Runnable> mDelayed = new ArrayList<>();
@Override
public void execute(Runnable runnable) {
mExecuted.add(runnable);
}
@Override
public void executeDelayed(Runnable r, long delayMillis) {
mDelayed.add(r);
}
@Override
public void removeCallbacks(Runnable r) {
mDelayed.remove(r);
}
@Override
public boolean hasCallback(Runnable r) {
return mDelayed.contains(r);
}
@Override
public Looper getLooper() {
return Looper.myLooper();
}
}
}

View File

@@ -23,22 +23,30 @@ import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedTouchHandlerTest extends OneHandedTestCase {
OneHandedTouchHandler mTouchHandler;
private OneHandedTouchHandler mTouchHandler;
@Mock
private OneHandedTimeoutHandler mTimeoutHandler;
@Mock
private ShellExecutor mMainExecutor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTouchHandler = new OneHandedTouchHandler();
mTouchHandler = new OneHandedTouchHandler(mTimeoutHandler, mMainExecutor);
}
@Test

View File

@@ -19,12 +19,14 @@ package com.android.wm.shell.onehanded;
import static org.mockito.Mockito.verify;
import android.content.om.IOverlayManager;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
@@ -35,12 +37,12 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
@Mock
OneHandedTouchHandler mTouchHandler;
OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
OneHandedTimeoutHandler mTimeoutHandler;
OneHandedController mOneHandedController;
@Mock
DisplayController mMockDisplayController;
@@ -52,12 +54,18 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
Handler mMockShellMainHandler;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTutorialHandler = new OneHandedTutorialHandler(mContext);
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController);
mTutorialHandler = new OneHandedTutorialHandler(mContext, mMockShellMainExecutor);
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
mMockShellMainExecutor);
mOneHandedController = new OneHandedController(
getContext(),
mMockDisplayController,
@@ -66,8 +74,11 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
mTouchHandler,
mTutorialHandler,
mGestureHandler,
mTimeoutHandler,
mMockOverlayManager,
mMockTaskStackListener);
mMockTaskStackListener,
mMockShellMainExecutor,
mMockShellMainHandler);
}
@Test

View File

@@ -45,9 +45,9 @@ public class TvWMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor,
DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
return new DisplayImeController(wmService, displayController, shellMainExecutor,
return new DisplayImeController(wmService, displayController, mainExecutor,
transactionPool);
}

View File

@@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -67,6 +68,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -86,18 +88,21 @@ public final class WMShell extends SystemUI
| SYSUI_STATE_BUBBLES_EXPANDED
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
// Shell interfaces
private final Optional<Pip> mPipOptional;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final Optional<ShellCommandHandler> mShellCommandHandler;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final NavigationModeController mNavigationModeController;
private final ScreenLifecycle mScreenLifecycle;
private final SysUiState mSysUiState;
private final Optional<Pip> mPipOptional;
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional;
private final ProtoTracer mProtoTracer;
private final Optional<ShellCommandHandler> mShellCommandHandler;
private final Executor mSysUiMainExecutor;
private boolean mIsSysUiStateValid;
private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback;
@@ -105,18 +110,20 @@ public final class WMShell extends SystemUI
private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback;
@Inject
public WMShell(Context context, CommandQueue commandQueue,
public WMShell(Context context,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
Optional<ShellCommandHandler> shellCommandHandler,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
NavigationModeController navigationModeController,
ScreenLifecycle screenLifecycle,
SysUiState sysUiState,
Optional<Pip> pipOptional,
Optional<LegacySplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutoutOptional,
ProtoTracer protoTracer,
Optional<ShellCommandHandler> shellCommandHandler) {
@Main Executor sysUiMainExecutor) {
super(context);
mCommandQueue = commandQueue;
mConfigurationController = configurationController;
@@ -129,12 +136,13 @@ public final class WMShell extends SystemUI
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutoutOptional = hideDisplayCutoutOptional;
mProtoTracer = protoTracer;
mProtoTracer.add(this);
mShellCommandHandler = shellCommandHandler;
mSysUiMainExecutor = sysUiMainExecutor;
}
@Override
public void start() {
mProtoTracer.add(this);
mCommandQueue.addCallback(this);
mPipOptional.ifPresent(this::initPip);
mSplitScreenOptional.ifPresent(this::initSplitScreen);
@@ -213,34 +221,43 @@ public final class WMShell extends SystemUI
oneHanded.registerTransitionCallback(new OneHandedTransitionCallback() {
@Override
public void onStartFinished(Rect bounds) {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
true).commitUpdate(DEFAULT_DISPLAY);
mSysUiMainExecutor.execute(() -> {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
true).commitUpdate(DEFAULT_DISPLAY);
});
}
@Override
public void onStopFinished(Rect bounds) {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
false).commitUpdate(DEFAULT_DISPLAY);
mSysUiMainExecutor.execute(() -> {
mSysUiState.setFlag(SYSUI_STATE_ONE_HANDED_ACTIVE,
false).commitUpdate(DEFAULT_DISPLAY);
});
}
});
oneHanded.registerGestureCallback(new OneHandedGestureEventCallback() {
@Override
public void onStart() {
if (oneHanded.isOneHandedEnabled()) {
oneHanded.startOneHanded();
} else if (oneHanded.isSwipeToNotificationEnabled()) {
mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
}
mSysUiMainExecutor.execute(() -> {
if (oneHanded.isOneHandedEnabled()) {
oneHanded.startOneHanded();
} else if (oneHanded.isSwipeToNotificationEnabled()) {
mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
}
});
}
@Override
public void onStop() {
if (oneHanded.isOneHandedEnabled()) {
oneHanded.stopOneHanded(OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
} else if (oneHanded.isSwipeToNotificationEnabled()) {
mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
}
mSysUiMainExecutor.execute(() -> {
if (oneHanded.isOneHandedEnabled()) {
oneHanded.stopOneHanded(
OneHandedEvents.EVENT_ONE_HANDED_TRIGGER_GESTURE_OUT);
} else if (oneHanded.isSwipeToNotificationEnabled()) {
mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP);
}
});
}
});

View File

@@ -110,9 +110,9 @@ public abstract class WMShellBaseModule {
@ShellMainThread
public static Handler provideShellMainHandler(@Main Handler sysuiMainHandler) {
if (ENABLE_SHELL_MAIN_THREAD) {
HandlerThread shellMainThread = new HandlerThread("wmshell.main");
shellMainThread.start();
return shellMainThread.getThreadHandler();
HandlerThread mainThread = new HandlerThread("wmshell.main");
mainThread.start();
return mainThread.getThreadHandler();
}
return sysuiMainHandler;
}
@@ -123,10 +123,10 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
@ShellMainThread
public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler shellMainHandler,
public static ShellExecutor provideShellMainExecutor(@ShellMainThread Handler mainHandler,
@Main ShellExecutor sysuiMainExecutor) {
if (ENABLE_SHELL_MAIN_THREAD) {
return new HandlerExecutor(shellMainHandler);
return new HandlerExecutor(mainHandler);
}
return sysuiMainExecutor;
}
@@ -177,7 +177,7 @@ public abstract class WMShellBaseModule {
Optional<AppPairs> appPairsOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
@ShellMainThread ShellExecutor shellMainExecutor) {
@ShellMainThread ShellExecutor mainExecutor) {
return ShellInitImpl.create(displayImeController,
dragAndDropController,
shellTaskOrganizer,
@@ -185,7 +185,7 @@ public abstract class WMShellBaseModule {
appPairsOptional,
fullscreenTaskListener,
transitions,
shellMainExecutor);
mainExecutor);
}
/**
@@ -201,10 +201,10 @@ public abstract class WMShellBaseModule {
Optional<OneHanded> oneHandedOptional,
Optional<HideDisplayCutout> hideDisplayCutout,
Optional<AppPairs> appPairsOptional,
@ShellMainThread ShellExecutor shellMainExecutor) {
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
legacySplitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
appPairsOptional, shellMainExecutor));
appPairsOptional, mainExecutor));
}
@WMSingleton
@@ -216,8 +216,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static DisplayController provideDisplayController(Context context,
IWindowManager wmService, @ShellMainThread ShellExecutor shellMainExecutor) {
return new DisplayController(context, wmService, shellMainExecutor);
IWindowManager wmService, @ShellMainThread ShellExecutor mainExecutor) {
return new DisplayController(context, wmService, mainExecutor);
}
@WMSingleton
@@ -236,8 +236,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static WindowManagerShellWrapper provideWindowManagerShellWrapper(
@ShellMainThread ShellExecutor shellMainExecutor) {
return new WindowManagerShellWrapper(shellMainExecutor);
@ShellMainThread ShellExecutor mainExecutor) {
return new WindowManagerShellWrapper(mainExecutor);
}
@WMSingleton
@@ -277,8 +277,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static SyncTransactionQueue provideSyncTransactionQueue(TransactionPool pool,
@ShellMainThread ShellExecutor shellMainExecutor) {
return new SyncTransactionQueue(pool, shellMainExecutor);
@ShellMainThread ShellExecutor mainExecutor) {
return new SyncTransactionQueue(pool, mainExecutor);
}
@WMSingleton
@@ -299,8 +299,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static TaskStackListenerImpl providerTaskStackListenerImpl(
@ShellMainThread Handler shellMainHandler) {
return new TaskStackListenerImpl(shellMainHandler);
@ShellMainThread Handler mainHandler) {
return new TaskStackListenerImpl(mainHandler);
}
@BindsOptionalOf
@@ -319,20 +319,22 @@ public abstract class WMShellBaseModule {
LauncherApps launcherApps,
UiEventLogger uiEventLogger,
ShellTaskOrganizer organizer,
@ShellMainThread ShellExecutor shellMainExecutor) {
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(BubbleController.create(context, null /* synchronizer */,
floatingContentCoordinator, statusBarService, windowManager,
windowManagerShellWrapper, launcherApps, uiEventLogger, organizer,
shellMainExecutor));
mainExecutor));
}
// Needs the shell main handler for ContentObserver callbacks
@WMSingleton
@Provides
static Optional<OneHanded> provideOneHandedController(Context context,
DisplayController displayController, TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor mainExecutor) {
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return Optional.ofNullable(OneHandedController.create(context, displayController,
taskStackListener, mainExecutor));
taskStackListener, mainExecutor, mainHandler));
}
@WMSingleton

View File

@@ -62,9 +62,9 @@ public class WMShellModule {
@WMSingleton
@Provides
static DisplayImeController provideDisplayImeController(IWindowManager wmService,
DisplayController displayController, @ShellMainThread ShellExecutor shellMainExecutor,
DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor,
TransactionPool transactionPool) {
return new DisplayImeController(wmService, displayController, shellMainExecutor,
return new DisplayImeController(wmService, displayController, mainExecutor,
transactionPool);
}
@@ -96,11 +96,11 @@ public class WMShellModule {
PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer,
PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper,
TaskStackListenerImpl taskStackListener,
@ShellMainThread ShellExecutor shellMainExecutor) {
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(context, displayController,
pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, pipMediaController,
phonePipMenuController, pipTaskOrganizer, pipTouchHandler,
windowManagerShellWrapper, taskStackListener, shellMainExecutor));
windowManagerShellWrapper, taskStackListener, mainExecutor));
}
@WMSingleton
@@ -131,10 +131,10 @@ public class WMShellModule {
PipTaskOrganizer pipTaskOrganizer,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor shellMainExecutor) {
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTouchHandler(context, menuPhoneController, pipBoundsAlgorithm,
pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger,
shellMainExecutor);
mainExecutor);
}
@WMSingleton

View File

@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
@@ -63,22 +64,22 @@ public class WMShellTest extends SysuiTestCase {
@Mock SysUiState mSysUiState;
@Mock Pip mPip;
@Mock PipTouchHandler mPipTouchHandler;
@Mock
LegacySplitScreen mLegacySplitScreen;
@Mock LegacySplitScreen mLegacySplitScreen;
@Mock OneHanded mOneHanded;
@Mock HideDisplayCutout mHideDisplayCutout;
@Mock ProtoTracer mProtoTracer;
@Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellExecutor mSysUiMainExecutor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController,
mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen),
Optional.of(mOneHanded), Optional.of(mHideDisplayCutout),
Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController,
mKeyguardUpdateMonitor, mNavigationModeController,
mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mLegacySplitScreen),
Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), mProtoTracer,
Optional.of(mShellCommandHandler));
mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor);
when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler);
}