diff options
| -rw-r--r-- | core/java/android/view/autofill/AutofillManager.java | 8 | ||||
| -rw-r--r-- | core/java/com/android/internal/view/inline/InlineTooltipUi.java | 154 |
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); + } + } } |