Avoid hiding the toolbar and pinned keys during inline autofill (#474)

This commit is contained in:
arcarum 2024-02-07 23:49:38 +04:00 committed by GitHub
parent ea2e2b301b
commit abc23d23be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 137 additions and 14 deletions

View file

@ -1346,8 +1346,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final View inlineSuggestionView = InlineAutofillUtils.createView(inlineSuggestions, mDisplayContext); final View inlineSuggestionView = InlineAutofillUtils.createView(inlineSuggestions, mDisplayContext);
// Without this function the inline autofill suggestions will not be visible
mHandler.cancelResumeSuggestions(); mHandler.cancelResumeSuggestions();
mHandler.cancelUpdateSuggestionStrip();
mSuggestionStripView.setInlineSuggestionsView(inlineSuggestionView); mSuggestionStripView.setInlineSuggestionsView(inlineSuggestionView);
return true; return true;

View file

@ -112,6 +112,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final SuggestionStripLayoutHelper mLayoutHelper; private final SuggestionStripLayoutHelper mLayoutHelper;
private final StripVisibilityGroup mStripVisibilityGroup; private final StripVisibilityGroup mStripVisibilityGroup;
private boolean isInlineAutofillSuggestionsVisible = false; // Required to disable the more suggestions if inline autofill suggestions are visible
private static class StripVisibilityGroup { private static class StripVisibilityGroup {
private final View mSuggestionStripView; private final View mSuggestionStripView;
@ -259,7 +260,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
: km.isKeyguardLocked(); : km.isKeyguardLocked();
mToolbarExpandKey.setOnClickListener(hideToolbarKeys ? null : this); mToolbarExpandKey.setOnClickListener(hideToolbarKeys ? null : this);
mPinnedKeys.setVisibility(hideToolbarKeys ? GONE : VISIBLE); mPinnedKeys.setVisibility(hideToolbarKeys ? GONE : VISIBLE);
mToolbarExpandKey.setVisibility(VISIBLE); isInlineAutofillSuggestionsVisible = false;
} }
public void setRtl(final boolean isRtlLanguage) { public void setRtl(final boolean isRtlLanguage) {
@ -278,8 +279,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
public void setInlineSuggestionsView(final View view) { public void setInlineSuggestionsView(final View view) {
clear(); clear();
mToolbarExpandKey.setVisibility(GONE); isInlineAutofillSuggestionsVisible = true;
mPinnedKeys.setVisibility(GONE);
mSuggestionsStrip.addView(view); mSuggestionsStrip.addView(view);
} }
@ -552,7 +552,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
public boolean onInterceptTouchEvent(final MotionEvent me) { public boolean onInterceptTouchEvent(final MotionEvent me) {
// Disable More Suggestions if inline autofill suggestions is visible // Disable More Suggestions if inline autofill suggestions is visible
if(mToolbarExpandKey.getVisibility() != VISIBLE && mPinnedKeys.getVisibility() != VISIBLE) { if(isInlineAutofillSuggestionsVisible) {
return false; return false;
} }

View file

@ -8,17 +8,32 @@ package helium314.keyboard.latin.utils;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Icon; import android.graphics.drawable.Icon;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Size; import android.util.Size;
import android.view.Choreographer;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InlineSuggestion; import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView; import android.widget.HorizontalScrollView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.inline.InlineContentView;
import android.widget.inline.InlinePresentationSpec; import android.widget.inline.InlinePresentationSpec;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.autofill.inline.UiVersions; import androidx.autofill.inline.UiVersions;
import androidx.autofill.inline.UiVersions.StylesBuilder; import androidx.autofill.inline.UiVersions.StylesBuilder;
@ -28,14 +43,14 @@ import androidx.autofill.inline.common.ViewStyle;
import androidx.autofill.inline.v1.InlineSuggestionUi; import androidx.autofill.inline.v1.InlineSuggestionUi;
import androidx.autofill.inline.v1.InlineSuggestionUi.Style; import androidx.autofill.inline.v1.InlineSuggestionUi.Style;
import java.util.ArrayList;
import java.util.List;
import helium314.keyboard.latin.R; import helium314.keyboard.latin.R;
import helium314.keyboard.latin.common.ColorType; import helium314.keyboard.latin.common.ColorType;
import helium314.keyboard.latin.common.Colors; import helium314.keyboard.latin.common.Colors;
import helium314.keyboard.latin.settings.Settings; import helium314.keyboard.latin.settings.Settings;
import java.util.ArrayList;
import java.util.List;
@RequiresApi(api = Build.VERSION_CODES.R) @RequiresApi(api = Build.VERSION_CODES.R)
public class InlineAutofillUtils { public class InlineAutofillUtils {
@ -49,7 +64,7 @@ public class InlineAutofillUtils {
new ViewStyle.Builder() new ViewStyle.Builder()
.setBackground( .setBackground(
Icon.createWithResource(context, Icon.createWithResource(context,
androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background) androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background)
.setTint(colors.get(ColorType.AUTOFILL_BACKGROUND_CHIP))) .setTint(colors.get(ColorType.AUTOFILL_BACKGROUND_CHIP)))
.setPadding(0, 0, 0, 0) .setPadding(0, 0, 0, 0)
.build()) .build())
@ -57,7 +72,7 @@ public class InlineAutofillUtils {
new ViewStyle.Builder() new ViewStyle.Builder()
.setBackground( .setBackground(
Icon.createWithResource(context, Icon.createWithResource(context,
androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background) androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background)
.setTint(colors.get(ColorType.AUTOFILL_BACKGROUND_CHIP))) .setTint(colors.get(ColorType.AUTOFILL_BACKGROUND_CHIP)))
.build()) .build())
.setStartIconStyle(new ImageViewStyle.Builder().setLayoutMargin(0, 0, 0, 0).build()) .setStartIconStyle(new ImageViewStyle.Builder().setLayoutMargin(0, 0, 0, 0).build())
@ -80,6 +95,7 @@ public class InlineAutofillUtils {
final Size min = new Size(100, height); final Size min = new Size(100, height);
final Size max = new Size(740, height); final Size max = new Size(740, height);
// Three InlinePresentationSpec are required for some password managers
final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>(); final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
presentationSpecs.add(new InlinePresentationSpec.Builder(min, max).setStyle(stylesBundle).build()); presentationSpecs.add(new InlinePresentationSpec.Builder(min, max).setStyle(stylesBundle).build());
presentationSpecs.add(new InlinePresentationSpec.Builder(min, max).setStyle(stylesBundle).build()); presentationSpecs.add(new InlinePresentationSpec.Builder(min, max).setStyle(stylesBundle).build());
@ -90,8 +106,7 @@ public class InlineAutofillUtils {
.build(); .build();
} }
public static HorizontalScrollView createView(List<InlineSuggestion> inlineSuggestions, Context context) { public static InlineContentClipView createView(List<InlineSuggestion> inlineSuggestions, Context context) {
final int totalSuggestionsCount = inlineSuggestions.size(); final int totalSuggestionsCount = inlineSuggestions.size();
LinearLayout container = new LinearLayout(context); LinearLayout container = new LinearLayout(context);
@ -108,8 +123,115 @@ public class InlineAutofillUtils {
HorizontalScrollView inlineSuggestionView = new HorizontalScrollView(context); HorizontalScrollView inlineSuggestionView = new HorizontalScrollView(context);
inlineSuggestionView.setHorizontalScrollBarEnabled(false); inlineSuggestionView.setHorizontalScrollBarEnabled(false);
inlineSuggestionView.setOverScrollMode(View.OVER_SCROLL_NEVER);
inlineSuggestionView.addView(container); inlineSuggestionView.addView(container);
return inlineSuggestionView; InlineContentClipView mScrollableSuggestionsClip = new InlineContentClipView(context);
mScrollableSuggestionsClip.addView(inlineSuggestionView);
return mScrollableSuggestionsClip;
} }
}
/**
* This class is a container for showing {@link InlineContentView}s for cases
* where you want to ensure they appear only in a given area in your app. An
* example is having a scrollable list of items. Note that without this container
* the InlineContentViews' surfaces would cover parts of your app as these surfaces
* are owned by another process and always appearing on top of your app.
*/
private static class InlineContentClipView extends FrameLayout {
@NonNull
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
this::clipDescendantInlineContentViews;
@NonNull
private final Rect mParentBounds = new Rect();
@NonNull
private final Rect mContentBounds = new Rect();
@NonNull
private final SurfaceView mBackgroundView;
private int mBackgroundColor;
public InlineContentClipView(@NonNull Context context) {
this(context, /*attrs*/ null);
}
public InlineContentClipView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, /*defStyleAttr*/ 0);
}
public InlineContentClipView(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBackgroundView = new SurfaceView(context);
mBackgroundView.setZOrderOnTop(true);
mBackgroundView.getHolder().setFormat(PixelFormat.TRANSPARENT);
mBackgroundView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
mBackgroundView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
drawBackgroundColorIfReady();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
int height) { /*do nothing*/ }
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
/*do nothing*/
}
});
addView(mBackgroundView);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
getViewTreeObserver().addOnDrawListener(mOnDrawListener);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
}
@Override
public void setBackgroundColor(int color) {
mBackgroundColor = color;
Choreographer.getInstance().postFrameCallback((frameTimeNanos) ->
drawBackgroundColorIfReady());
}
private void drawBackgroundColorIfReady() {
final Surface surface = mBackgroundView.getHolder().getSurface();
if (surface.isValid()) {
final Canvas canvas = surface.lockCanvas(null);
try {
canvas.drawColor(mBackgroundColor);
} finally {
surface.unlockCanvasAndPost(canvas);
}
}
}
private void clipDescendantInlineContentViews() {
mParentBounds.right = getWidth();
mParentBounds.bottom = getHeight();
clipDescendantInlineContentViews(this);
}
private void clipDescendantInlineContentViews(@Nullable View root) {
if (root == null) {
return;
}
if (root instanceof InlineContentView inlineContentView) {
mContentBounds.set(mParentBounds);
offsetRectIntoDescendantCoords(inlineContentView, mContentBounds);
inlineContentView.setClipBounds(mContentBounds);
return;
}
if (root instanceof ViewGroup rootGroup) {
final int childCount = rootGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = rootGroup.getChildAt(i);
clipDescendantInlineContentViews(child);
}
}
}
}
}