summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TYM Tsai <tymtsai@google.com> 2022-05-13 05:20:46 +0800
committer TYM Tsai <tymtsai@google.com> 2022-05-24 21:33:54 +0800
commit3392d187ebd3704bc2e86de76f7d96ec3f132ee1 (patch)
tree71d1e7612820f7b7b817575dfa07baef4504bb1c
parentfc974566b8543c9e3a3a94bb1ef8b8f74f1c0176 (diff)
Fix inline tooltip position is wrong
The PopupWindow will show up above the anchor with the real rectangle. If the anchor is doing an animation, it will get a temporary top of the anchor because the rectangle is conisince changed by the animation. So attach tooltip after a while for waiting the animation is done. And when the tooltip overrides the inline suggestion, clicking the suggestion won't work. Force the y offset above or below the anchor to fix the problem. Bug: 223088398 Test: atest android.autofillservice.cts.inline.InlineTooltipTest Change-Id: I068efc3d20f4486decd2c9e690ebeb2981fb6722
-rw-r--r--core/java/android/view/autofill/AutofillManager.java8
-rw-r--r--core/java/com/android/internal/view/inline/InlineTooltipUi.java154
2 files changed, 149 insertions, 13 deletions
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index dcedb3083f76..7353bbccf77c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -506,6 +506,14 @@ public final class AutofillManager {
public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS =
"autofill_dialog_hints";
+ /**
+ * Sets a value of delay time to show up the inline tooltip view.
+ *
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY =
+ "autofill_inline_tooltip_first_show_delay";
+
private static final String DIALOG_HINTS_DELIMITER = ":";
/** @hide */
diff --git a/core/java/com/android/internal/view/inline/InlineTooltipUi.java b/core/java/com/android/internal/view/inline/InlineTooltipUi.java
index 25fa678d0507..3eae89e350a0 100644
--- a/core/java/com/android/internal/view/inline/InlineTooltipUi.java
+++ b/core/java/com/android/internal/view/inline/InlineTooltipUi.java
@@ -15,24 +15,30 @@
*/
package com.android.internal.view.inline;
+import static android.view.autofill.AutofillManager.DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY;
import static android.view.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.ContextWrapper;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.transition.Transition;
import android.util.Slog;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.inline.InlineContentView;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
/**
* UI container for the inline suggestion tooltip.
@@ -40,6 +46,8 @@ import java.io.PrintWriter;
public final class InlineTooltipUi extends PopupWindow implements AutoCloseable {
private static final String TAG = "InlineTooltipUi";
+ private static final int FIRST_TIME_SHOW_DEFAULT_DELAY_MS = 250;
+
private final WindowManager mWm;
private final ViewGroup mContentContainer;
@@ -47,6 +55,16 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
private WindowManager.LayoutParams mWindowLayoutParams;
+ private DelayShowRunnable mDelayShowTooltip;
+
+ private boolean mHasEverDetached;
+
+ private boolean mDelayShowAtStart = true;
+ private boolean mDelaying = false;
+ private int mShowDelayConfigMs;
+
+ private final Rect mTmpRect = new Rect();
+
private final View.OnAttachStateChangeListener mAnchorOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@Override
@@ -56,6 +74,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
@Override
public void onViewDetachedFromWindow(View v) {
+ mHasEverDetached = true;
dismiss();
}
};
@@ -66,6 +85,13 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mHasEverDetached) {
+ // If the tooltip is ever detached, skip adjusting the position,
+ // because it only accepts to attach once and does not show again
+ // after detaching.
+ return;
+ }
+
if (mHeight != bottom - top) {
mHeight = bottom - top;
adjustPosition();
@@ -77,6 +103,13 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
mContentContainer = new LinearLayout(new ContextWrapper(context));
mWm = context.getSystemService(WindowManager.class);
+ // That's a default delay time, and it will scale via the value of
+ // Settings.Global.ANIMATOR_DURATION_SCALE
+ mShowDelayConfigMs = DeviceConfig.getInt(
+ DeviceConfig.NAMESPACE_AUTOFILL,
+ DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY,
+ FIRST_TIME_SHOW_DEFAULT_DELAY_MS);
+
setTouchModal(false);
setOutsideTouchable(true);
setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
@@ -95,7 +128,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
@Override
public void close() {
- hide();
+ dismiss();
}
@Override
@@ -117,14 +150,57 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
* The effective {@code update} method that should be called by its clients.
*/
public void update(View anchor) {
+ if (anchor == null) {
+ final View oldAnchor = getAnchor();
+ if (oldAnchor != null) {
+ removeDelayShowTooltip(oldAnchor);
+ }
+ return;
+ }
+
+ if (mDelayShowAtStart) {
+ // To avoid showing when the anchor is doing the fade in animation. That will
+ // cause the tooltip to show in the wrong position and jump at the start.
+ mDelayShowAtStart = false;
+ mDelaying = true;
+
+ if (mDelayShowTooltip == null) {
+ mDelayShowTooltip = new DelayShowRunnable(anchor);
+ }
+
+ int delayTimeMs = mShowDelayConfigMs;
+ try {
+ final float scale = Settings.Global.getFloat(
+ anchor.getContext().getContentResolver(),
+ Settings.Global.ANIMATOR_DURATION_SCALE);
+ delayTimeMs *= scale;
+ } catch (Settings.SettingNotFoundException e) {
+ // do nothing
+ }
+ anchor.postDelayed(mDelayShowTooltip, delayTimeMs);
+ } else if (!mDelaying) {
+ // Note: If we are going to reuse the tooltip, we need to take care the delay in
+ // the case that update for the new anchor.
+ updateInner(anchor);
+ }
+ }
+
+ private void removeDelayShowTooltip(View anchor) {
+ if (mDelayShowTooltip != null) {
+ anchor.removeCallbacks(mDelayShowTooltip);
+ mDelayShowTooltip = null;
+ }
+ }
+
+ private void updateInner(View anchor) {
+ if (mHasEverDetached) {
+ return;
+ }
// set to the application type with the highest z-order
setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
- // The first time to show up, the height of tooltip is zero,
- // so set the offset Y to 2 * anchor height.
- final int achoredHeight = mContentContainer.getHeight();
- final int offsetY = (achoredHeight == 0)
- ? -anchor.getHeight() << 1 : -anchor.getHeight() - achoredHeight;
+ final int offsetY = -anchor.getHeight() - getPreferHeight(anchor);
+
if (!isShowing()) {
setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
@@ -135,6 +211,34 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
}
+ private int getPreferHeight(View anchor) {
+ // The first time to show up, the height of tooltip is zero, so make its height
+ // the same as anchor.
+ final int achoredHeight = mContentContainer.getHeight();
+ return (achoredHeight == 0) ? anchor.getHeight() : achoredHeight;
+ }
+
+ @Override
+ protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams,
+ int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
+ boolean isAbove = super.findDropDownPosition(anchor, outParams, xOffset, yOffset, width,
+ height, gravity, allowScroll);
+ // Make the tooltips y fo position is above or under the parent of the anchor,
+ // otherwise suggestions doesn't clickable.
+ ViewParent parent = anchor.getParent();
+ if (parent instanceof View) {
+ final Rect r = mTmpRect;
+ ((View) parent).getGlobalVisibleRect(r);
+ if (isAbove) {
+ outParams.y = r.top - getPreferHeight(anchor);
+ } else {
+ outParams.y = r.bottom + 1;
+ }
+ }
+
+ return isAbove;
+ }
+
@Override
protected void update(View anchor, WindowManager.LayoutParams params) {
// update content view for the anchor is scrolling
@@ -175,7 +279,9 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
final View anchor = getAnchor();
if (anchor != null) {
anchor.removeOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener);
+ removeDelayShowTooltip(anchor);
}
+ mHasEverDetached = true;
super.detachFromAnchor();
}
@@ -185,7 +291,6 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
return;
}
- setShowing(false);
setTransitioningToDismiss(true);
hide();
@@ -193,6 +298,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
if (getOnDismissListener() != null) {
getOnDismissListener().onDismiss();
}
+ super.dismiss();
}
private void adjustPosition() {
@@ -202,15 +308,15 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
private void show(WindowManager.LayoutParams params) {
- if (sVerbose) {
- Slog.v(TAG, "show()");
- }
mWindowLayoutParams = params;
try {
params.packageName = "android";
params.setTitle("Autofill Inline Tooltip"); // Title is set for debugging purposes
if (!mShowing) {
+ if (sVerbose) {
+ Slog.v(TAG, "show()");
+ }
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.privateFlags |=
@@ -232,11 +338,11 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
private void hide() {
- if (sVerbose) {
- Slog.v(TAG, "hide()");
- }
try {
if (mShowing) {
+ if (sVerbose) {
+ Slog.v(TAG, "hide()");
+ }
mContentContainer.removeOnLayoutChangeListener(mAnchoredOnLayoutChangeListener);
mWm.removeView(mContentContainer);
mShowing = false;
@@ -336,4 +442,26 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable
}
}
}
+
+ private class DelayShowRunnable implements Runnable {
+ WeakReference<View> mAnchor;
+
+ DelayShowRunnable(View anchor) {
+ mAnchor = new WeakReference<>(anchor);
+ }
+
+ @Override
+ public void run() {
+ mDelaying = false;
+ final View anchor = mAnchor.get();
+ if (anchor != null) {
+ updateInner(anchor);
+ }
+ }
+
+ public void setAnchor(View anchor) {
+ mAnchor.clear();
+ mAnchor = new WeakReference<>(anchor);
+ }
+ }
}