Inline autofill support (#336)

Theming support is still missing
This commit is contained in:
arcarum 2023-12-29 12:51:46 +04:00 committed by GitHub
parent c19441310d
commit ab183c53d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 195 additions and 2 deletions

View file

@ -81,6 +81,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1" implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.1"
implementation 'com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0' implementation 'com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0'
implementation 'androidx.autofill:autofill:1.1.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.7.0' testImplementation 'org.mockito:mockito-core:5.7.0'

View file

@ -16,7 +16,9 @@ import android.graphics.Color;
import android.inputmethodservice.InputMethodService; import android.inputmethodservice.InputMethodService;
import android.media.AudioManager; import android.media.AudioManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Debug; import android.os.Debug;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.Process; import android.os.Process;
@ -33,8 +35,12 @@ import android.view.Window;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype;
import android.widget.HorizontalScrollView;
import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils; import org.dslul.openboard.inputmethod.accessibility.AccessibilityUtils;
import org.dslul.openboard.inputmethod.annotations.UsedForTesting; import org.dslul.openboard.inputmethod.annotations.UsedForTesting;
@ -73,6 +79,7 @@ import org.dslul.openboard.inputmethod.latin.suggestions.SuggestionStripViewAcce
import org.dslul.openboard.inputmethod.latin.touchinputconsumer.GestureConsumer; import org.dslul.openboard.inputmethod.latin.touchinputconsumer.GestureConsumer;
import org.dslul.openboard.inputmethod.latin.utils.ApplicationUtils; import org.dslul.openboard.inputmethod.latin.utils.ApplicationUtils;
import org.dslul.openboard.inputmethod.latin.utils.ColorUtilKt; import org.dslul.openboard.inputmethod.latin.utils.ColorUtilKt;
import org.dslul.openboard.inputmethod.latin.utils.InlineAutofillUtils;
import org.dslul.openboard.inputmethod.latin.utils.InputMethodPickerKt; import org.dslul.openboard.inputmethod.latin.utils.InputMethodPickerKt;
import org.dslul.openboard.inputmethod.latin.utils.JniUtils; import org.dslul.openboard.inputmethod.latin.utils.JniUtils;
import org.dslul.openboard.inputmethod.latin.utils.LeakGuardHandlerWrapper; import org.dslul.openboard.inputmethod.latin.utils.LeakGuardHandlerWrapper;
@ -94,6 +101,7 @@ import static org.dslul.openboard.inputmethod.latin.common.Constants.ImeOption.N
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
@ -1342,6 +1350,43 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
updateSoftInputWindowLayoutParameters(); updateSoftInputWindowLayoutParameters();
} }
@Override
@RequiresApi(api = Build.VERSION_CODES.R)
public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) {
Log.d(TAG,"onCreateInlineSuggestionsRequest called");
// Revert to default behaviour if show_suggestions is disabled
// (Maybe there is a better way to do this)
if(!mSettings.getCurrent().isSuggestionsEnabledPerUserSettings()){
return null;
}
return InlineAutofillUtils.createInlineSuggestionRequest(mDisplayContext);
}
@Override
@RequiresApi(api = Build.VERSION_CODES.R)
public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
Log.d(TAG,"onInlineSuggestionsResponse called");
final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions.isEmpty()) {
return false;
}
HorizontalScrollView view = InlineAutofillUtils.createView(inlineSuggestions, mDisplayContext);
// Delay required to show properly
new Handler().postDelayed(() -> {
mSuggestionStripView.clear();
mSuggestionStripView.hideKeys();
mSuggestionStripView.addSuggestionView(view);
}, 200);
return true;
}
private void updateSoftInputWindowLayoutParameters() { private void updateSoftInputWindowLayoutParameters() {
// Override layout parameters to expand {@link SoftInputWindow} to the entire screen. // Override layout parameters to expand {@link SoftInputWindow} to the entire screen.
// See {@link InputMethodService#setinputView(View)} and // See {@link InputMethodService#setinputView(View)} and

View file

@ -264,6 +264,15 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
getContext(), mSuggestedWords, mSuggestionsStrip, this); getContext(), mSuggestedWords, mSuggestionsStrip, this);
} }
public void addSuggestionView(final View view) {
mSuggestionsStrip.addView(view);
}
public void hideKeys() {
mToolbarKey.setVisibility(GONE);
mPinnedKeys.setVisibility(GONE);
}
public void setMoreSuggestionsHeight(final int remainingHeight) { public void setMoreSuggestionsHeight(final int remainingHeight) {
mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); mLayoutHelper.setMoreSuggestionsHeight(remainingHeight);
} }
@ -278,6 +287,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
for (final TextView word : mWordViews) { for (final TextView word : mWordViews) {
word.setOnTouchListener(null); word.setOnTouchListener(null);
} }
if (mToolbarKey.getVisibility() != VISIBLE || mPinnedKeys.getVisibility() != VISIBLE){
mToolbarKey.setVisibility(VISIBLE);
mPinnedKeys.setVisibility(VISIBLE);
}
} }
private void removeAllDebugInfoViews() { private void removeAllDebugInfoViews() {

View file

@ -0,0 +1,127 @@
/*
* Copyright (C) 2019 The Android Open Source Project
* modified
* SPDX-License-Identifier: Apache-2.0
*/
package org.dslul.openboard.inputmethod.latin.utils;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.inline.InlinePresentationSpec;
import androidx.annotation.RequiresApi;
import androidx.autofill.inline.UiVersions;
import androidx.autofill.inline.common.ImageViewStyle;
import androidx.autofill.inline.common.TextViewStyle;
import androidx.autofill.inline.common.ViewStyle;
import androidx.autofill.inline.v1.InlineSuggestionUi;
import org.dslul.openboard.inputmethod.latin.R;
import java.util.ArrayList;
import java.util.List;
// Modified code from https://android.googlesource.com/platform/development/+/master/samples/AutofillKeyboard/
@RequiresApi(api = Build.VERSION_CODES.R)
public class InlineAutofillUtils {
private static int toPixel(int dp, Context context) {
return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
}
private static int getHeight(Context context) {
return context.getResources().getDimensionPixelSize(R.dimen.config_suggestions_strip_height);
}
public static InlineSuggestionsRequest createInlineSuggestionRequest(Context context) {
UiVersions.StylesBuilder stylesBuilder = UiVersions.newStylesBuilder();
@SuppressLint("RestrictedApi") InlineSuggestionUi.Style style = InlineSuggestionUi.newStyleBuilder()
.setSingleIconChipStyle(
new ViewStyle.Builder()
.setBackground(
Icon.createWithResource(context,
androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background))
.setPadding(0, 0, 0, 0)
.build())
.setChipStyle(
new ViewStyle.Builder()
.setBackground(
Icon.createWithResource(context,
androidx.autofill.R.drawable.autofill_inline_suggestion_chip_background))
.build())
.setStartIconStyle(new ImageViewStyle.Builder().setLayoutMargin(0, 0, 0, 0).build())
.setTitleStyle(
new TextViewStyle.Builder()
.setLayoutMargin(toPixel(4, context), 0, toPixel(4, context), 0)
.setTextColor(Color.parseColor("#FF202124"))
.setTextSize(12)
.build())
.setSubtitleStyle(
new TextViewStyle.Builder()
.setLayoutMargin(0, 0, toPixel(4, context), 0)
.setTextColor(Color.parseColor("#99202124"))
.setTextSize(10)
.build())
.setEndIconStyle(new ImageViewStyle.Builder().setLayoutMargin(0, 0, 0, 0).build())
.build();
stylesBuilder.addStyle(style);
Bundle stylesBundle = stylesBuilder.build();
Size min = new Size(100, getHeight(context));
Size max = new Size(740, getHeight(context));
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());
return new InlineSuggestionsRequest.Builder(presentationSpecs)
.setMaxSuggestionCount(6)
.build();
}
public static HorizontalScrollView createView(List<InlineSuggestion> inlineSuggestions, Context context) {
final int totalSuggestionsCount = inlineSuggestions.size();
// A container to hold all views
LinearLayout container = new LinearLayout(context);
for (int i = 0; i < totalSuggestionsCount; i++) {
final InlineSuggestion inlineSuggestion = inlineSuggestions.get(i);
inlineSuggestion.inflate(context, new Size(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT), context.getMainExecutor(), (view) -> {
if (view != null)
container.addView(view);
});
}
HorizontalScrollView view = new HorizontalScrollView(context);
view.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
view.setHorizontalScrollBarEnabled(false);
view.addView(container);
return view;
}
}

View file

@ -108,9 +108,12 @@
<!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default
subtype.--> subtype.-->
<input-method xmlns:android="http://schemas.android.com/apk/res/android" <input-method xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:settingsActivity="org.dslul.openboard.inputmethod.latin.settings.SettingsActivity" android:settingsActivity="org.dslul.openboard.inputmethod.latin.settings.SettingsActivity"
android:isDefault="@bool/im_is_default" android:isDefault="@bool/im_is_default"
android:supportsSwitchingToNextInputMethod="true"> android:supportsSwitchingToNextInputMethod="true"
android:supportsInlineSuggestions="true"
tools:targetApi="r">
<subtype android:icon="@drawable/ic_ime_switcher" <subtype android:icon="@drawable/ic_ime_switcher"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:subtypeId="0xc9194f97" android:subtypeId="0xc9194f97"

View file

@ -15,9 +15,12 @@ We don't want the subtypes managed by the system because it doesn't allow for ni
the system picker crashes on some devices for unknown reasons. the system picker crashes on some devices for unknown reasons.
--> -->
<input-method xmlns:android="http://schemas.android.com/apk/res/android" <input-method xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:settingsActivity="org.dslul.openboard.inputmethod.latin.settings.SettingsActivity" android:settingsActivity="org.dslul.openboard.inputmethod.latin.settings.SettingsActivity"
android:isDefault="@bool/im_is_default" android:isDefault="@bool/im_is_default"
android:supportsSwitchingToNextInputMethod="true"> android:supportsSwitchingToNextInputMethod="true"
android:supportsInlineSuggestions="true"
tools:targetApi="r">
<subtype android:icon="@drawable/ic_ime_switcher" <subtype android:icon="@drawable/ic_ime_switcher"
android:imeSubtypeExtraValue="dummy" android:imeSubtypeExtraValue="dummy"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"