Merge "Redesign One-haned tutorial UX behavior" into sc-dev
This commit is contained in:
@@ -223,7 +223,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>
|
|||||||
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
|
OneHandedTimeoutHandler timeoutHandler = new OneHandedTimeoutHandler(mainExecutor);
|
||||||
OneHandedState transitionState = new OneHandedState();
|
OneHandedState transitionState = new OneHandedState();
|
||||||
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
|
OneHandedTutorialHandler tutorialHandler = new OneHandedTutorialHandler(context,
|
||||||
displayLayout, windowManager, settingsUtil, mainExecutor);
|
windowManager);
|
||||||
OneHandedAnimationController animationController =
|
OneHandedAnimationController animationController =
|
||||||
new OneHandedAnimationController(context);
|
new OneHandedAnimationController(context);
|
||||||
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
|
OneHandedTouchHandler touchHandler = new OneHandedTouchHandler(timeoutHandler,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.onehanded;
|
package com.android.wm.shell.onehanded;
|
||||||
|
|
||||||
import static android.os.UserHandle.myUserId;
|
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||||
|
|
||||||
import static com.android.wm.shell.onehanded.OneHandedState.STATE_ACTIVE;
|
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_ENTERING;
|
||||||
@@ -24,7 +24,6 @@ import static com.android.wm.shell.onehanded.OneHandedState.STATE_EXITING;
|
|||||||
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
|
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.PixelFormat;
|
import android.graphics.PixelFormat;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -41,7 +40,6 @@ import androidx.annotation.NonNull;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.common.DisplayLayout;
|
import com.android.wm.shell.common.DisplayLayout;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
@@ -56,57 +54,44 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
private static final String TAG = "OneHandedTutorialHandler";
|
private static final String TAG = "OneHandedTutorialHandler";
|
||||||
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
|
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
|
||||||
"persist.debug.one_handed_offset_percentage";
|
"persist.debug.one_handed_offset_percentage";
|
||||||
private static final int MAX_TUTORIAL_SHOW_COUNT = 2;
|
|
||||||
|
|
||||||
private final float mTutorialHeightRatio;
|
private final float mTutorialHeightRatio;
|
||||||
private final WindowManager mWindowManager;
|
private final WindowManager mWindowManager;
|
||||||
private final OneHandedSettingsUtil mSettingsUtil;
|
|
||||||
private final ShellExecutor mShellExecutor;
|
|
||||||
|
|
||||||
private boolean mCanShow;
|
private boolean mIsShowing;
|
||||||
private @OneHandedState.State int mCurrentState;
|
private @OneHandedState.State int mCurrentState;
|
||||||
private int mShownCounts;
|
|
||||||
private int mTutorialAreaHeight;
|
private int mTutorialAreaHeight;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ContentResolver mContentResolver;
|
|
||||||
private Rect mDisplayBounds;
|
private Rect mDisplayBounds;
|
||||||
private @Nullable View mTutorialView;
|
private @Nullable View mTutorialView;
|
||||||
private @Nullable ViewGroup mTargetViewContainer;
|
private @Nullable ViewGroup mTargetViewContainer;
|
||||||
|
|
||||||
private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
|
private final OneHandedAnimationCallback mAnimationCallback;
|
||||||
@Override
|
|
||||||
public void onAnimationUpdate(float xPos, float yPos) {
|
|
||||||
if (!canShowTutorial()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mTargetViewContainer.setTransitionGroup(true);
|
|
||||||
mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public OneHandedTutorialHandler(Context context, DisplayLayout displayLayout,
|
public OneHandedTutorialHandler(Context context, WindowManager windowManager) {
|
||||||
WindowManager windowManager, OneHandedSettingsUtil settingsUtil,
|
|
||||||
ShellExecutor mainExecutor) {
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mContentResolver = context.getContentResolver();
|
|
||||||
mWindowManager = windowManager;
|
mWindowManager = windowManager;
|
||||||
mSettingsUtil = settingsUtil;
|
|
||||||
mShellExecutor = mainExecutor;
|
|
||||||
final float offsetPercentageConfig = context.getResources().getFraction(
|
final float offsetPercentageConfig = context.getResources().getFraction(
|
||||||
R.fraction.config_one_handed_offset, 1, 1);
|
R.fraction.config_one_handed_offset, 1, 1);
|
||||||
final int sysPropPercentageConfig = SystemProperties.getInt(
|
final int sysPropPercentageConfig = SystemProperties.getInt(
|
||||||
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
|
ONE_HANDED_MODE_OFFSET_PERCENTAGE, Math.round(offsetPercentageConfig * 100.0f));
|
||||||
mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
|
mTutorialHeightRatio = sysPropPercentageConfig / 100.0f;
|
||||||
mShownCounts = mSettingsUtil.getTutorialShownCounts(mContentResolver, myUserId());
|
mAnimationCallback = new OneHandedAnimationCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationUpdate(float xPos, float yPos) {
|
||||||
|
if (!isShowing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mTargetViewContainer.setTransitionGroup(true);
|
||||||
|
mTargetViewContainer.setTranslationY(yPos - mTargetViewContainer.getHeight());
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateChanged(int newState) {
|
public void onStateChanged(int newState) {
|
||||||
mCurrentState = newState;
|
mCurrentState = newState;
|
||||||
if (!canShowTutorial()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case STATE_ENTERING:
|
case STATE_ENTERING:
|
||||||
createViewAndAttachToWindow(mContext);
|
createViewAndAttachToWindow(mContext);
|
||||||
@@ -139,7 +124,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void createViewAndAttachToWindow(Context context) {
|
void createViewAndAttachToWindow(Context context) {
|
||||||
if (!canShowTutorial()) {
|
if (isShowing()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
|
mTutorialView = LayoutInflater.from(context).inflate(R.layout.one_handed_tutorial, null);
|
||||||
@@ -150,15 +135,6 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
attachTargetToWindow();
|
attachTargetToWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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.
|
* Adds the tutorial target view to the WindowManager and update its layout.
|
||||||
*/
|
*/
|
||||||
@@ -166,6 +142,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
if (!mTargetViewContainer.isAttachedToWindow()) {
|
if (!mTargetViewContainer.isAttachedToWindow()) {
|
||||||
try {
|
try {
|
||||||
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
|
mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
|
||||||
|
mIsShowing = true;
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// This shouldn't happen, but if the target is already added, just update its
|
// This shouldn't happen, but if the target is already added, just update its
|
||||||
// layout params.
|
// layout params.
|
||||||
@@ -179,14 +156,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
void removeTutorialFromWindowManager(boolean increment) {
|
void removeTutorialFromWindowManager(boolean increment) {
|
||||||
if (mTargetViewContainer != null && mTargetViewContainer.isAttachedToWindow()) {
|
if (mTargetViewContainer != null && mTargetViewContainer.isAttachedToWindow()) {
|
||||||
mWindowManager.removeViewImmediate(mTargetViewContainer);
|
mWindowManager.removeViewImmediate(mTargetViewContainer);
|
||||||
if (increment) {
|
mIsShowing = false;
|
||||||
setTutorialShownCountIncrement();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable OneHandedAnimationCallback getAnimationCallback() {
|
@Nullable OneHandedAnimationCallback getAnimationCallback() {
|
||||||
return canShowTutorial() ? mAnimationCallback : null /* Disabled */;
|
return isShowing() ? mAnimationCallback : null /* Disabled */;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -200,6 +175,7 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||||
PixelFormat.TRANSLUCENT);
|
PixelFormat.TRANSLUCENT);
|
||||||
lp.gravity = Gravity.TOP | Gravity.LEFT;
|
lp.gravity = Gravity.TOP | Gravity.LEFT;
|
||||||
|
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||||
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||||
lp.setFitInsetsTypes(0 /* types */);
|
lp.setFitInsetsTypes(0 /* types */);
|
||||||
lp.setTitle("one-handed-tutorial-overlay");
|
lp.setTitle("one-handed-tutorial-overlay");
|
||||||
@@ -207,17 +183,14 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean canShowTutorial() {
|
boolean isShowing() {
|
||||||
return mCanShow = mShownCounts < MAX_TUTORIAL_SHOW_COUNT;
|
return mIsShowing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onConfigurationChanged events for updating tutorial text.
|
* onConfigurationChanged events for updating tutorial text.
|
||||||
*/
|
*/
|
||||||
public void onConfigurationChanged() {
|
public void onConfigurationChanged() {
|
||||||
if (!canShowTutorial()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
removeTutorialFromWindowManager(false /* increment */);
|
removeTutorialFromWindowManager(false /* increment */);
|
||||||
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
|
if (mCurrentState == STATE_ENTERING || mCurrentState == STATE_ACTIVE) {
|
||||||
createViewAndAttachToWindow(mContext);
|
createViewAndAttachToWindow(mContext);
|
||||||
@@ -227,14 +200,12 @@ public class OneHandedTutorialHandler implements OneHandedTransitionCallback,
|
|||||||
void dump(@NonNull PrintWriter pw) {
|
void dump(@NonNull PrintWriter pw) {
|
||||||
final String innerPrefix = " ";
|
final String innerPrefix = " ";
|
||||||
pw.println(TAG);
|
pw.println(TAG);
|
||||||
pw.print(innerPrefix + "mCanShow=");
|
pw.print(innerPrefix + "mIsShowing=");
|
||||||
pw.println(mCanShow);
|
pw.println(mIsShowing);
|
||||||
pw.print(innerPrefix + "mCurrentState=");
|
pw.print(innerPrefix + "mCurrentState=");
|
||||||
pw.println(mCurrentState);
|
pw.println(mCurrentState);
|
||||||
pw.print(innerPrefix + "mDisplayBounds=");
|
pw.print(innerPrefix + "mDisplayBounds=");
|
||||||
pw.println(mDisplayBounds);
|
pw.println(mDisplayBounds);
|
||||||
pw.print(innerPrefix + "mShownCounts=");
|
|
||||||
pw.println(mShownCounts);
|
|
||||||
pw.print(innerPrefix + "mTutorialAreaHeight=");
|
pw.print(innerPrefix + "mTutorialAreaHeight=");
|
||||||
pw.println(mTutorialAreaHeight);
|
pw.println(mTutorialAreaHeight);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ 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_ENTERING;
|
||||||
import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
@@ -68,17 +66,10 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
|
|||||||
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
|
mDisplayLayout = new DisplayLayout(mContext, mDisplay);
|
||||||
mSpiedTransitionState = spy(new OneHandedState());
|
mSpiedTransitionState = spy(new OneHandedState());
|
||||||
mSpiedTutorialHandler = spy(
|
mSpiedTutorialHandler = spy(
|
||||||
new OneHandedTutorialHandler(mContext, mDisplayLayout, mMockWindowManager,
|
new OneHandedTutorialHandler(mContext, mMockWindowManager));
|
||||||
mMockSettingsUtil, mMockShellMainExecutor));
|
|
||||||
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
|
mTimeoutHandler = new OneHandedTimeoutHandler(mMockShellMainExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDefaultZeroShownCounts_canShowTutorial() {
|
|
||||||
assertThat(mSpiedTutorialHandler.canShowTutorial()).isTrue();
|
|
||||||
verify(mMockShellMainExecutor, never()).execute(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultZeroShownCounts_doNotAttachWindow() {
|
public void testDefaultZeroShownCounts_doNotAttachWindow() {
|
||||||
verify(mMockShellMainExecutor, never()).execute(any());
|
verify(mMockShellMainExecutor, never()).execute(any());
|
||||||
@@ -86,7 +77,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStateChangedEntering_createViewAndAttachToWindow() {
|
public void testOnStateChangedEntering_createViewAndAttachToWindow() {
|
||||||
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
|
when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
|
||||||
try {
|
try {
|
||||||
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
|
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
@@ -98,7 +89,7 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStateChangedNone_removeViewAndAttachToWindow() {
|
public void testOnStateChangedNone_removeViewAndAttachToWindow() {
|
||||||
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
|
when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
|
||||||
try {
|
try {
|
||||||
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
|
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
@@ -110,19 +101,19 @@ public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnStateChangedNone_shouldNotAttachWindow() {
|
public void testOnStateChangedNone_shouldNotAttachWindow() {
|
||||||
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
|
when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
|
||||||
try {
|
try {
|
||||||
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
|
mSpiedTutorialHandler.onStateChanged(STATE_NONE);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
// no-op, just assert setTutorialShownCountIncrement() never be called
|
// no-op, just assert setTutorialShownCountIncrement() never be called
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(mSpiedTutorialHandler, never()).setTutorialShownCountIncrement();
|
verify(mSpiedTutorialHandler, never()).createViewAndAttachToWindow(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnConfigurationChanged_shouldUpdateViewContent() {
|
public void testOnConfigurationChanged_shouldUpdateViewContent() {
|
||||||
when(mSpiedTutorialHandler.canShowTutorial()).thenReturn(true);
|
when(mSpiedTutorialHandler.isShowing()).thenReturn(true);
|
||||||
try {
|
try {
|
||||||
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
|
mSpiedTutorialHandler.onStateChanged(STATE_ENTERING);
|
||||||
} catch (ClassCastException e) {
|
} catch (ClassCastException e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user