Merge "Integrate OneHandedTutorialHandler with OneHandedState" into sc-dev
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user