Merge "Integrate OneHandedTutorialHandler with OneHandedState" into sc-dev

This commit is contained in:
Bill Lin
2021-05-18 00:25:32 +00:00
committed by Android (Google) Code Review
7 changed files with 273 additions and 190 deletions

View File

@@ -19,6 +19,7 @@ package com.android.wm.shell.onehanded;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -203,8 +204,10 @@ public class OneHandedAnimationController {
}
OneHandedTransitionAnimator addOneHandedAnimationCallback(
OneHandedAnimationCallback callback) {
mOneHandedAnimationCallbacks.add(callback);
@Nullable OneHandedAnimationCallback callback) {
if (callback != null) {
mOneHandedAnimationCallbacks.add(callback);
}
return this;
}

View File

@@ -217,7 +217,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
OneHandedState transitionState = new OneHandedState();
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
displayLayout, windowManager, mainExecutor);
displayLayout, windowManager, settingsUtil, mainExecutor);
OneHandedAnimationController animationController =
new OneHandedAnimationController(context);
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
@@ -299,6 +299,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
mAccessibilityManager = AccessibilityManager.getInstance(context);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
mState.addSListeners(mTutorialHandler);
}
public OneHanded asOneHanded() {
@@ -627,13 +629,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
}
private void onConfigChanged(Configuration newConfig) {
if (mTutorialHandler != null) {
if (!mIsOneHandedEnabled
|| newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return;
}
mTutorialHandler.onConfigurationChanged(newConfig);
if (mTutorialHandler == null) {
return;
}
if (!mIsOneHandedEnabled || newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return;
}
mTutorialHandler.onConfigurationChanged();
}
private void onUserSwitch(int newUserId) {

View File

@@ -29,7 +29,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* APIs for querying or updating one handed settings .
* APIs for querying or updating one handed settings.
*/
public final class OneHandedSettingsUtil {
private static final String TAG = "OneHandedSettingsUtil";
@@ -62,7 +62,7 @@ public final class OneHandedSettingsUtil {
public static final int ONE_HANDED_TIMEOUT_LONG_IN_SECONDS = 12;
/**
* Register one handed preference settings observer
* Registers one handed preference settings observer
*
* @param key Setting key to monitor in observer
* @param resolver ContentResolver of context
@@ -82,7 +82,7 @@ public final class OneHandedSettingsUtil {
}
/**
* Unregister one handed preference settings observer
* Unregisters one handed preference settings observer.
*
* @param resolver ContentResolver of context
* @param observer preference key change observer
@@ -95,7 +95,7 @@ public final class OneHandedSettingsUtil {
}
/**
* Query one handed enable or disable flag from Settings provider.
* Queries one handed enable or disable flag from Settings provider.
*
* @return enable or disable one handed mode flag.
*/
@@ -105,7 +105,7 @@ public final class OneHandedSettingsUtil {
}
/**
* Query taps app to exit config from Settings provider.
* Queries taps app to exit config from Settings provider.
*
* @return enable or disable taps app exit.
*/
@@ -115,7 +115,7 @@ public final class OneHandedSettingsUtil {
}
/**
* Query timeout value from Settings provider. Default is
* Queries timeout value from Settings provider. Default is.
* {@link OneHandedSettingsUtil#ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS}
*
* @return timeout value in seconds.
@@ -135,10 +135,31 @@ public final class OneHandedSettingsUtil {
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0, userId) == 1;
}
/**
* Sets one handed activated or not to notify state for shortcut
* Queries tutorial shown counts from Settings provider. Default is 0.
*
* @return activated or not
* @return counts tutorial shown counts.
*/
public int getTutorialShownCounts(ContentResolver resolver, int userId) {
return Settings.Secure.getIntForUser(resolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0, userId);
}
/**
* Sets tutorial shown counts.
*
* @return true if the value was set, false on database errors.
*/
public boolean setTutorialShownCounts(ContentResolver resolver, int shownCounts, int userId) {
return Settings.Secure.putIntForUser(resolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, shownCounts, userId);
}
/**
* Sets one handed activated or not to notify state for shortcut.
*
* @return true if one handed mode is activated.
*/
public boolean getOneHandedModeActivated(ContentResolver resolver, int userId) {
return Settings.Secure.getIntForUser(resolver,
@@ -146,9 +167,9 @@ public final class OneHandedSettingsUtil {
}
/**
* Sets one handed activated or not to notify state for shortcut
* Sets one handed activated or not to notify state for shortcut.
*
* @return activated or not
* @return true if the value was set, false on database errors.
*/
public boolean setOneHandedModeActivated(ContentResolver resolver, int state, int userId) {
return Settings.Secure.putIntForUser(resolver,
@@ -167,6 +188,8 @@ public final class OneHandedSettingsUtil {
pw.println(getSettingsTapsAppToExit(resolver, userId));
pw.print(innerPrefix + "shortcutActivated=");
pw.println(getOneHandedModeActivated(resolver, userId));
pw.print(innerPrefix + "tutorialShownCounts=");
pw.println(getTutorialShownCounts(resolver, userId));
}
public OneHandedSettingsUtil() {

View File

@@ -21,6 +21,8 @@ import android.annotation.IntDef;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
Represents current OHM state by following steps, a generic CUJ is
@@ -28,13 +30,13 @@ import java.lang.annotation.RetentionPolicy;
*/
public class OneHandedState {
/** DEFAULT STATE after OHM feature initialized. */
public static final int STATE_NONE = 0x00000000;
public static final int STATE_NONE = 0;
/** The state flag set when user trigger OHM. */
public static final int STATE_ENTERING = 0x00000001;
public static final int STATE_ENTERING = 1;
/** The state flag set when transitioning */
public static final int STATE_ACTIVE = 0x00000002;
public static final int STATE_ACTIVE = 2;
/** The state flag set when user stop OHM feature. */
public static final int STATE_EXITING = 0x00000004;
public static final int STATE_EXITING = 3;
@IntDef(prefix = { "STATE_" }, value = {
STATE_NONE,
@@ -54,9 +56,18 @@ public class OneHandedState {
private static final String TAG = OneHandedState.class.getSimpleName();
private List<OnStateChangedListener> mStateChangeListeners = new ArrayList<>();
/**
* Adds listener to be called back when one handed state changed.
* @param listener the listener to be called back
*/
public void addSListeners(OnStateChangedListener listener) {
mStateChangeListeners.add(listener);
}
/**
* Gets current transition state of One handed mode.
*
* @return The bitwise flags representing current states.
*/
public @State int getState() {
@@ -85,6 +96,9 @@ public class OneHandedState {
*/
public void setState(@State int newState) {
sCurrentState = newState;
if (!mStateChangeListeners.isEmpty()) {
mStateChangeListeners.forEach((listener) -> listener.onStateChanged(newState));
}
}
/** Dumps internal state. */
@@ -93,4 +107,14 @@ public class OneHandedState {
pw.println(TAG);
pw.println(innerPrefix + "sCurrentState=" + sCurrentState);
}
/**
* Gets notified when one handed state changed
*
* @see OneHandedState
*/
public interface OnStateChangedListener {
/** Called when one handed state changed */
default void onStateChanged(@State int newState) {}
}
}

View File

@@ -16,13 +16,19 @@
package com.android.wm.shell.onehanded;
import static android.os.UserHandle.myUserId;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.provider.Settings;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -32,6 +38,7 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -39,42 +46,33 @@ import com.android.wm.shell.common.ShellExecutor;
import java.io.PrintWriter;
/**
* Manages the user tutorial handling for One Handed operations, including animations synchronized
* with one-handed translation.
* Refer {@link OneHandedGestureHandler} and {@link OneHandedTouchHandler} to see start and stop
* one handed gesture
* Handles tutorial visibility and synchronized transition for One Handed operations,
* TargetViewContainer only be created and attach to window when
* shown counts < {@link MAX_TUTORIAL_SHOW_COUNT}, and detach TargetViewContainer from window
* after exiting one handed mode.
*/
public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
OneHandedState.OnStateChangedListener {
private static final String TAG = "OneHandedTutorialHandler";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
"persist.debug.one_handed_offset_percentage";
private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
private final WindowManager mWindowManager;
private final String mPackageName;
private final float mTutorialHeightRatio;
private final WindowManager mWindowManager;
private final OneHandedSettingsUtil mSettingsUtil;
private final ShellExecutor mShellExecutor;
private boolean mCanShow;
private @OneHandedState.State int mCurrentState;
private int mShownCounts;
private int mTutorialAreaHeight;
private Context mContext;
private Rect mDisplayBounds;
private View mTutorialView;
private ContentResolver mContentResolver;
private boolean mCanShowTutorial;
private boolean mIsOneHandedMode;
private enum ONE_HANDED_TRIGGER_STATE {
UNSET, ENTERING, EXITING
}
/**
* Current One-Handed trigger state.
* Note: This is a dynamic state, whenever last state has been confirmed
* (i.e. onStartFinished() or onStopFinished()), the state should be set "UNSET" at final.
*/
private ONE_HANDED_TRIGGER_STATE mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
/**
* Container of the tutorial panel showing at outside region when one handed starting
*/
private ViewGroup mTargetViewContainer;
private int mTutorialAreaHeight;
private Rect mDisplayBounds;
private @Nullable View mTutorialView;
private @Nullable ViewGroup mTargetViewContainer;
private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
@Override
@@ -82,63 +80,51 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
if (!canShowTutorial()) {
return;
}
mTargetViewContainer.setVisibility(View.VISIBLE);
mTargetViewContainer.setTransitionGroup(true);
mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
}
@Override
public void onOneHandedAnimationStart(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
final float startValue = (float) animator.getStartValue();
if (mTriggerState == ONE_HANDED_TRIGGER_STATE.UNSET) {
mTriggerState = (startValue == 0f)
? ONE_HANDED_TRIGGER_STATE.ENTERING : ONE_HANDED_TRIGGER_STATE.EXITING;
if (mCanShowTutorial && mTriggerState == ONE_HANDED_TRIGGER_STATE.ENTERING) {
attachTurtorialTarget();
}
}
}
};
public OneHandedTutorialHandler(Context context, DisplayLayout displayLayout,
WindowManager windowManager, ShellExecutor mainExecutor) {
WindowManager windowManager, OneHandedSettingsUtil settingsUtil,
ShellExecutor mainExecutor) {
mContext = context;
mWindowManager = windowManager;
mPackageName = context.getPackageName();
mContentResolver = context.getContentResolver();
mWindowManager = windowManager;
mSettingsUtil = settingsUtil;
mShellExecutor = mainExecutor;
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));
mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
onDisplayChanged(displayLayout);
mCanShowTutorial = (Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0) >= MAX_TUTORIAL_SHOW_COUNT)
? false : true;
mIsOneHandedMode = false;
mainExecutor.execute(() -> {
recreateTutorialView(mContext);
});
mShownCounts = mSettingsUtil.getTutorialShownCounts(mContentResolver, myUserId());
}
@Override
public void onStartFinished(Rect bounds) {
updateFinished(View.VISIBLE, 0f);
updateTutorialCount();
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
}
@Override
public void onStopFinished(Rect bounds) {
updateFinished(View.INVISIBLE, -mTargetViewContainer.getHeight());
removeTutorialFromWindowManager();
mTriggerState = ONE_HANDED_TRIGGER_STATE.UNSET;
public void onStateChanged(int newState) {
mCurrentState = newState;
if (!canShowTutorial()) {
return;
}
switch (newState) {
case STATE_ENTERING:
createViewAndAttachToWindow(mContext);
break;
case STATE_ACTIVE:
case STATE_EXITING:
// no - op
break;
case STATE_NONE:
removeTutorialFromWindowManager(true /* increment */);
break;
default:
break;
}
}
/**
* Called when onDisplayAdded() or onDisplayRemoved() callback
* Called when onDisplayAdded() or onDisplayRemoved() callback.
* @param displayLayout The latest {@link DisplayLayout} representing current displayId
*/
public void onDisplayChanged(DisplayLayout displayLayout) {
@@ -151,38 +137,32 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
mTutorialAreaHeight = Math.round(mDisplayBounds.height() * mTutorialHeightRatio);
}
private void recreateTutorialView(Context context) {
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial,
null);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
mTargetViewContainer.addView(mTutorialView);
mTargetViewContainer.setVisibility(mIsOneHandedMode ? View.VISIBLE : View.GONE);
}
private void updateFinished(int visible, float finalPosition) {
@VisibleForTesting
void createViewAndAttachToWindow(Context context) {
if (!canShowTutorial()) {
return;
}
mIsOneHandedMode = (finalPosition == 0f) ? true : false;
mTargetViewContainer.setVisibility(visible);
mTargetViewContainer.setTranslationY(finalPosition);
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
mTargetViewContainer = new FrameLayout(context);
mTargetViewContainer.setClipChildren(false);
mTargetViewContainer.addView(mTutorialView);
attachTargetToWindow();
}
private void updateTutorialCount() {
int showCount = Settings.Secure.getInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, 0);
showCount = Math.min(MAX_TUTORIAL_SHOW_COUNT, showCount + 1);
mCanShowTutorial = showCount < MAX_TUTORIAL_SHOW_COUNT;
Settings.Secure.putInt(mContentResolver,
Settings.Secure.ONE_HANDED_TUTORIAL_SHOW_COUNT, showCount);
@VisibleForTesting
boolean setTutorialShownCountIncrement() {
if (!canShowTutorial()) {
return false;
}
mShownCounts += 1;
return mSettingsUtil.setTutorialShownCounts(mContentResolver, mShownCounts, myUserId());
}
/**
* Adds the tutorial target view to the WindowManager and update its layout, so it's ready
* to be animated in.
* Adds the tutorial target view to the WindowManager and update its layout.
*/
private void attachTurtorialTarget() {
private void attachTargetToWindow() {
if (!mTargetViewContainer.isAttachedToWindow()) {
try {
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
@@ -195,14 +175,18 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
}
}
private void removeTutorialFromWindowManager() {
if (mTargetViewContainer.isAttachedToWindow()) {
@VisibleForTesting
void removeTutorialFromWindowManager(boolean increment) {
if (mTargetViewContainer != null && mTargetViewContainer.isAttachedToWindow()) {
mWindowManager.removeViewImmediate(mTargetViewContainer);
if (increment) {
setTutorialShownCountIncrement();
}
}
}
OneHandedAnimationCallback getAnimationCallback() {
return mAnimationCallback;
@Nullable OneHandedAnimationCallback getAnimationCallback() {
return canShowTutorial() ? mAnimationCallback : null /* Disabled */;
}
/**
@@ -222,38 +206,36 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback {
return lp;
}
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
pw.print(innerPrefix + "mTriggerState=");
pw.println(mTriggerState);
pw.print(innerPrefix + "mDisplayBounds=");
pw.println(mDisplayBounds);
pw.print(innerPrefix + "mTutorialAreaHeight=");
pw.println(mTutorialAreaHeight);
}
private boolean canShowTutorial() {
if (!mCanShowTutorial) {
// Since canSHowTutorial() will be called in onAnimationUpdate() and we still need to
// hide Tutorial text in the period of continuously onAnimationUpdate() API call,
// so we have to hide mTargetViewContainer here.
mTargetViewContainer.setVisibility(View.GONE);
return false;
}
return true;
@VisibleForTesting
boolean canShowTutorial() {
return mCanShow = mShownCounts < MAX_TUTORIAL_SHOW_COUNT;
}
/**
* onConfigurationChanged events for updating tutorial text.
* @param newConfig
*/
public void onConfigurationChanged(Configuration newConfig) {
if (!mCanShowTutorial) {
public void onConfigurationChanged() {
if (!canShowTutorial()) {
return;
}
removeTutorialFromWindowManager();
recreateTutorialView(mContext.createConfigurationContext(newConfig));
attachTurtorialTarget();
removeTutorialFromWindowManager(false /* increment */);
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
createViewAndAttachToWindow(mContext);
}
}
void dump(@NonNull PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
pw.print(innerPrefix + "mCanShow=");
pw.println(mCanShow);
pw.print(innerPrefix + "mCurrentState=");
pw.println(mCurrentState);
pw.print(innerPrefix + "mDisplayBounds=");
pw.println(mDisplayBounds);
pw.print(innerPrefix + "mShownCounts=");
pw.println(mShownCounts);
pw.print(innerPrefix + "mTutorialAreaHeight=");
pw.println(mTutorialAreaHeight);
}
}

View File

@@ -398,4 +398,14 @@ public class OneHandedControllerTest extends OneHandedTestCase {
verify(mSpiedOneHandedController, never()).startOneHanded();
verify(mSpiedOneHandedController, never()).stopOneHanded();
}
@Test
public void testControllerInit_tutorialAddStateChangeListener() {
when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true);
when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE);
when(mSpiedTransitionState.isTransitioning()).thenReturn(false);
when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false);
verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler);
}
}

View File

@@ -16,19 +16,26 @@
package com.android.wm.shell.onehanded;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ENTERING;
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.om.IOverlayManager;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.util.ArrayMap;
import android.view.Display;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import org.junit.Before;
import org.junit.Test;
@@ -39,62 +46,94 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
Display mDisplay;
DisplayLayout mDisplayLayout;
OneHandedTimeoutHandler mTimeoutHandler;
OneHandedController mOneHandedController;
OneHandedState mSpiedTransitionState;
OneHandedTutorialHandler mSpiedTutorialHandler;
@Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
OneHandedTutorialHandler mMockTutorialHandler;
@Mock
DisplayController mMockDisplayController;
@Mock
OneHandedBackgroundPanelOrganizer mMockBackgroundOrganizer;
@Mock
OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
@Mock
IOverlayManager mMockOverlayManager;
@Mock
TaskStackListenerImpl mMockTaskStackListener;
@Mock
ShellExecutor mMockShellMainExecutor;
@Mock
Handler mMockShellMainHandler;
@Mock
OneHandedUiEventLogger mMockUiEventLogger;
@Mock
OneHandedSettingsUtil mMockSettingsUtil;
@Mock
OneHandedAccessibilityUtil mMockAccessibilityUtil;
WindowManager mMockWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
mSpiedTransitionState = new OneHandedState();
when(mMockSettingsUtil.getTutorialShownCounts(any(), anyInt())).thenReturn(0);
when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>());
mOneHandedController = new OneHandedController(
mContext,
mMockDisplayController,
mMockBackgroundOrganizer,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
mMockTutorialHandler,
mMockSettingsUtil,
mMockAccessibilityUtil,
mTimeoutHandler,
mSpiedTransitionState,
mMockUiEventLogger,
mMockOverlayManager,
mMockTaskStackListener,
mMockShellMainExecutor,
mMockShellMainHandler);
mDisplay = mContext.getDisplay();
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
mSpiedTransitionState = spy(new OneHandedState());
mSpiedTutorialHandler = spy(
new OneHandedTutorialHandler(mContext, mDisplayLayout, mMockWindowManager,
mMockSettingsUtil, mMockShellMainExecutor));
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
}
@Test
public void testRegisterForDisplayAreaOrganizer() {
verify(mMockDisplayAreaOrganizer).registerTransitionCallback(mMockTutorialHandler);
public void testDefaultZeroShownCounts_canShowTutorial() {
assertThat(mSpiedTutorialHandler.canShowTutorial()).isTrue();
verify(mMockShellMainExecutor, never()).execute(any());
}
@Test
public void testDefaultZeroShownCounts_doNotAttachWindow() {
verify(mMockShellMainExecutor, never()).execute(any());
}
@Test
public void testOnStateChangedEntering_createViewAndAttachToWindow() {
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
} catch (ClassCastException e) {
// no-op, just assert createViewAndAttachToWindow() to be called
}
verify(mSpiedTutorialHandler).createViewAndAttachToWindow(any());
}
@Test
public void testOnStateChangedNone_removeViewAndAttachToWindow() {
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
} catch (ClassCastException e) {
// no-op, just assert removeTutorialFromWindowManager() to be called
}
verify(mSpiedTutorialHandler).removeTutorialFromWindowManager(true);
}
@Test
public void testOnStateChangedNone_shouldNotAttachWindow() {
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
} catch (ClassCastException e) {
// no-op, just assert setTutorialShownCountIncrement() never be called
}
verify(mSpiedTutorialHandler, never()).setTutorialShownCountIncrement();
}
@Test
public void testOnConfigurationChanged_shouldUpdateViewContent() {
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
try {
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
} catch (ClassCastException e) {
// no-op, set current state for test onConfigurationChanged()
}
try {
mSpiedTutorialHandler.onConfigurationChanged();
} catch (ClassCastException e) {
// no-op, just assert removeTutorialFromWindowManager() be called
}
verify(mSpiedTutorialHandler).removeTutorialFromWindowManager(false);
}
}