From 384e2fe6db1f1b645a028a58088c8503399f31f1 Mon Sep 17 00:00:00 2001 From: JFronny Date: Thu, 4 Sep 2025 09:12:26 +0200 Subject: [PATCH 1/5] Enable inline autofill --- build.gradle.kts | 1 + res/layout/keyboard.xml | 16 ++++- res/xml/method.xml | 2 +- srcs/juloo.keyboard2/Keyboard2.java | 93 +++++++++++++++++++++++++++-- 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fd27aba86..ea8fbdf1b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,6 +8,7 @@ plugins { dependencies { implementation("androidx.window:window-java:1.3.0") implementation("androidx.core:core:1.16.0") + implementation("androidx.autofill:autofill:1.3.0") testImplementation("junit:junit:4.13.2") } diff --git a/res/layout/keyboard.xml b/res/layout/keyboard.xml index 8af048da5..9e7159edb 100644 --- a/res/layout/keyboard.xml +++ b/res/layout/keyboard.xml @@ -1,2 +1,16 @@ - + + + + + + diff --git a/res/xml/method.xml b/res/xml/method.xml index 26441cb50..5dac6da4f 100644 --- a/res/xml/method.xml +++ b/res/xml/method.xml @@ -1,5 +1,5 @@ - + diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 0297cc43d..c211e8a6e 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -1,24 +1,39 @@ package juloo.keyboard2; +import static juloo.keyboard2.Logs.TAG; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.os.Build.VERSION; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.text.InputType; import android.util.Log; import android.util.LogPrinter; +import android.util.Size; +import android.util.TypedValue; import android.view.*; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InlineSuggestion; +import android.view.inputmethod.InlineSuggestionsRequest; +import android.view.inputmethod.InlineSuggestionsResponse; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; import android.widget.LinearLayout; +import android.widget.inline.InlinePresentationSpec; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.autofill.inline.UiVersions; +import androidx.autofill.inline.v1.InlineSuggestionUi; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; @@ -30,7 +45,10 @@ public class Keyboard2 extends InputMethodService implements SharedPreferences.OnSharedPreferenceChangeListener { + private View _keyboardViewParent = null; private Keyboard2View _keyboardView; + private HorizontalScrollView _autofillContainer; + private LinearLayout _autofillSuggestions; private KeyEventHandler _keyeventhandler; /** If not 'null', the layout to use instead of [_config.current_layout]. */ private KeyboardData _currentSpecialLayout; @@ -117,13 +135,20 @@ public void onCreate() Config.initGlobalConfig(prefs, getResources(), _keyeventhandler, _foldStateTracker.isUnfolded()); prefs.registerOnSharedPreferenceChangeListener(this); _config = Config.globalConfig(); - _keyboardView = (Keyboard2View)inflate_view(R.layout.keyboard); + inflate_keyboardView(); _keyboardView.reset(); Logs.set_debug_logs(getResources().getBoolean(R.bool.debug_logs)); ClipboardHistoryService.on_startup(this, _keyeventhandler); _foldStateTracker.setChangedCallback(() -> { refresh_config(); }); } + private void inflate_keyboardView() { + _keyboardViewParent = inflate_view(R.layout.keyboard); + _autofillContainer = _keyboardViewParent.findViewById(R.id.autofill_container); + _autofillSuggestions = _keyboardViewParent.findViewById(R.id.autofill_suggestions); + _keyboardView = _keyboardViewParent.findViewById(R.id.keyboard_view); + } + @Override public void onDestroy() { super.onDestroy(); @@ -250,10 +275,10 @@ private void refresh_config() // Refreshing the theme config requires re-creating the views if (prev_theme != _config.theme) { - _keyboardView = (Keyboard2View)inflate_view(R.layout.keyboard); + inflate_keyboardView(); _emojiPane = null; _clipboard_pane = null; - setInputView(_keyboardView); + setInputView(_keyboardViewParent); } _keyboardView.reset(); } @@ -283,7 +308,7 @@ public void onStartInputView(EditorInfo info, boolean restarting) _currentSpecialLayout = refresh_special_layout(info); _keyboardView.setKeyboard(current_layout()); _keyeventhandler.started(info); - setInputView(_keyboardView); + setInputView(_keyboardViewParent); Logs.debug_startup_input_view(info, _config); } @@ -436,7 +461,7 @@ public void handle_event_key(KeyValue.Event ev) case SWITCH_BACK_EMOJI: case SWITCH_BACK_CLIPBOARD: - setInputView(_keyboardView); + setInputView(_keyboardViewParent); break; case CHANGE_METHOD_PICKER: @@ -520,4 +545,62 @@ private View inflate_view(int layout) { return View.inflate(new ContextThemeWrapper(this, _config.theme), layout, null); } + + @RequiresApi(api = Build.VERSION_CODES.R) + @Nullable + @Override + public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) + { + Size smallestSize = new Size(0, 0); + Size biggestSize = new Size(Integer.MAX_VALUE, Integer.MAX_VALUE); + + UiVersions.StylesBuilder stylesBuilder = UiVersions.newStylesBuilder(); + + InlineSuggestionUi.Style style = InlineSuggestionUi.newStyleBuilder().build(); + stylesBuilder.addStyle(style); + + Bundle stylesBundle = stylesBuilder.build(); + InlinePresentationSpec spec = + new InlinePresentationSpec.Builder(smallestSize, biggestSize) + .setStyle(stylesBundle) + .build(); + + List specList = new ArrayList<>(); + specList.add(spec); + + InlineSuggestionsRequest.Builder builder = new InlineSuggestionsRequest.Builder(specList); + + return builder.setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED) + .build(); + } + + @RequiresApi(api = Build.VERSION_CODES.R) + @Override + public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) + { + List inlineSuggestions = response.getInlineSuggestions(); + + float height = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics()); + Size autofillSize = new Size(ViewGroup.LayoutParams.WRAP_CONTENT, ((int) height)); + + _autofillSuggestions.removeAllViews(); + + for (InlineSuggestion inlineSuggestion : inlineSuggestions) { + try { + inlineSuggestion.inflate( + this, + autofillSize, + getMainExecutor(), + inlineContentView -> { + _autofillContainer.setVisibility(View.VISIBLE); + _autofillSuggestions.addView(inlineContentView); + }); + } catch (Exception e) { + Log.e(TAG, "onInlineSuggestionsResponse - inlineSuggestion.infLate - " + e); + } + } + return true; + } } From 79925e1bdedbb80f50d229a97daf89f173fddc40 Mon Sep 17 00:00:00 2001 From: JFronny Date: Mon, 29 Sep 2025 10:20:01 +0200 Subject: [PATCH 2/5] Align style with project --- srcs/juloo.keyboard2/Keyboard2.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index c211e8a6e..1f108e184 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -587,8 +587,10 @@ public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse re _autofillSuggestions.removeAllViews(); - for (InlineSuggestion inlineSuggestion : inlineSuggestions) { - try { + for (InlineSuggestion inlineSuggestion : inlineSuggestions) + { + try + { inlineSuggestion.inflate( this, autofillSize, @@ -597,7 +599,9 @@ public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse re _autofillContainer.setVisibility(View.VISIBLE); _autofillSuggestions.addView(inlineContentView); }); - } catch (Exception e) { + } + catch (Exception e) + { Log.e(TAG, "onInlineSuggestionsResponse - inlineSuggestion.infLate - " + e); } } From 2b375ab2dd9edbd1ea96946e87f70ccf2baac4fa Mon Sep 17 00:00:00 2001 From: JFronny Date: Wed, 1 Oct 2025 18:19:39 +0200 Subject: [PATCH 3/5] Apply suggestions from review --- srcs/juloo.keyboard2/InlineAutofill.java | 90 ++++++++++++++++++++++++ srcs/juloo.keyboard2/Keyboard2.java | 72 ++----------------- 2 files changed, 95 insertions(+), 67 deletions(-) create mode 100644 srcs/juloo.keyboard2/InlineAutofill.java diff --git a/srcs/juloo.keyboard2/InlineAutofill.java b/srcs/juloo.keyboard2/InlineAutofill.java new file mode 100644 index 000000000..249050f2b --- /dev/null +++ b/srcs/juloo.keyboard2/InlineAutofill.java @@ -0,0 +1,90 @@ +package juloo.keyboard2; + +import static juloo.keyboard2.Logs.TAG; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.autofill.inline.UiVersions; +import androidx.autofill.inline.v1.InlineSuggestionUi; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.util.Size; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InlineSuggestion; +import android.view.inputmethod.InlineSuggestionsRequest; +import android.view.inputmethod.InlineSuggestionsResponse; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.inline.InlinePresentationSpec; + +import java.util.Collections; +import java.util.List; + +@RequiresApi(api = Build.VERSION_CODES.R) +public class InlineAutofill { + private HorizontalScrollView _autofillContainer; + private LinearLayout _autofillSuggestions; + + public void onInflate(View keyboardViewParent) + { + _autofillContainer = keyboardViewParent.findViewById(R.id.autofill_container); + _autofillSuggestions = keyboardViewParent.findViewById(R.id.autofill_suggestions); + _autofillContainer.setVisibility(View.GONE); + } + + @Nullable + public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) + { + Size smallestSize = new Size(0, 0); + Size biggestSize = new Size(Integer.MAX_VALUE, Integer.MAX_VALUE); + + UiVersions.StylesBuilder stylesBuilder = UiVersions.newStylesBuilder(); + + InlineSuggestionUi.Style style = InlineSuggestionUi.newStyleBuilder().build(); + stylesBuilder.addStyle(style); + + Bundle stylesBundle = stylesBuilder.build(); + InlinePresentationSpec spec = + new InlinePresentationSpec.Builder(smallestSize, biggestSize) + .setStyle(stylesBundle) + .build(); + + return new InlineSuggestionsRequest.Builder(Collections.singletonList(spec)).build(); + } + + public boolean onInlineSuggestionsResponse(Context context, @NonNull InlineSuggestionsResponse response) + { + List inlineSuggestions = response.getInlineSuggestions(); + + float height = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 40, context.getResources().getDisplayMetrics()); + Size autofillSize = new Size(ViewGroup.LayoutParams.WRAP_CONTENT, ((int) height)); + + _autofillSuggestions.removeAllViews(); + if (!inlineSuggestions.isEmpty()) _autofillContainer.setVisibility(View.VISIBLE); + + for (InlineSuggestion inlineSuggestion : inlineSuggestions) + { + try + { + inlineSuggestion.inflate( + context, + autofillSize, + context.getMainExecutor(), + _autofillSuggestions::addView); + } + catch (Exception e) + { + Log.e(TAG, "onInlineSuggestionsResponse - inlineSuggestion.infLate - " + e); + } + } + return true; + } +} \ No newline at end of file diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 1f108e184..423038e6d 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -1,8 +1,6 @@ package juloo.keyboard2; -import static juloo.keyboard2.Logs.TAG; import android.annotation.TargetApi; -import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.inputmethodservice.InputMethodService; @@ -12,13 +10,8 @@ import android.os.Handler; import android.os.IBinder; import android.text.InputType; -import android.util.Log; -import android.util.LogPrinter; -import android.util.Size; -import android.util.TypedValue; import android.view.*; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InlineSuggestion; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; import android.view.inputmethod.InputConnection; @@ -26,20 +19,13 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import android.widget.FrameLayout; -import android.widget.HorizontalScrollView; import android.widget.LinearLayout; -import android.widget.inline.InlinePresentationSpec; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import androidx.autofill.inline.UiVersions; -import androidx.autofill.inline.v1.InlineSuggestionUi; -import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Set; import juloo.keyboard2.prefs.LayoutsPreference; public class Keyboard2 extends InputMethodService @@ -47,8 +33,7 @@ public class Keyboard2 extends InputMethodService { private View _keyboardViewParent = null; private Keyboard2View _keyboardView; - private HorizontalScrollView _autofillContainer; - private LinearLayout _autofillSuggestions; + private InlineAutofill _inlineAutofill; private KeyEventHandler _keyeventhandler; /** If not 'null', the layout to use instead of [_config.current_layout]. */ private KeyboardData _currentSpecialLayout; @@ -135,6 +120,7 @@ public void onCreate() Config.initGlobalConfig(prefs, getResources(), _keyeventhandler, _foldStateTracker.isUnfolded()); prefs.registerOnSharedPreferenceChangeListener(this); _config = Config.globalConfig(); + _inlineAutofill = new InlineAutofill(); inflate_keyboardView(); _keyboardView.reset(); Logs.set_debug_logs(getResources().getBoolean(R.bool.debug_logs)); @@ -144,8 +130,7 @@ public void onCreate() private void inflate_keyboardView() { _keyboardViewParent = inflate_view(R.layout.keyboard); - _autofillContainer = _keyboardViewParent.findViewById(R.id.autofill_container); - _autofillSuggestions = _keyboardViewParent.findViewById(R.id.autofill_suggestions); + _inlineAutofill.onInflate(_keyboardViewParent); _keyboardView = _keyboardViewParent.findViewById(R.id.keyboard_view); } @@ -551,60 +536,13 @@ private View inflate_view(int layout) @Override public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull Bundle uiExtras) { - Size smallestSize = new Size(0, 0); - Size biggestSize = new Size(Integer.MAX_VALUE, Integer.MAX_VALUE); - - UiVersions.StylesBuilder stylesBuilder = UiVersions.newStylesBuilder(); - - InlineSuggestionUi.Style style = InlineSuggestionUi.newStyleBuilder().build(); - stylesBuilder.addStyle(style); - - Bundle stylesBundle = stylesBuilder.build(); - InlinePresentationSpec spec = - new InlinePresentationSpec.Builder(smallestSize, biggestSize) - .setStyle(stylesBundle) - .build(); - - List specList = new ArrayList<>(); - specList.add(spec); - - InlineSuggestionsRequest.Builder builder = new InlineSuggestionsRequest.Builder(specList); - - return builder.setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED) - .build(); + return _inlineAutofill.onCreateInlineSuggestionsRequest(uiExtras); } @RequiresApi(api = Build.VERSION_CODES.R) @Override public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) { - List inlineSuggestions = response.getInlineSuggestions(); - - float height = - TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, 40, getResources().getDisplayMetrics()); - Size autofillSize = new Size(ViewGroup.LayoutParams.WRAP_CONTENT, ((int) height)); - - _autofillSuggestions.removeAllViews(); - - for (InlineSuggestion inlineSuggestion : inlineSuggestions) - { - try - { - inlineSuggestion.inflate( - this, - autofillSize, - getMainExecutor(), - inlineContentView -> { - _autofillContainer.setVisibility(View.VISIBLE); - _autofillSuggestions.addView(inlineContentView); - }); - } - catch (Exception e) - { - Log.e(TAG, "onInlineSuggestionsResponse - inlineSuggestion.infLate - " + e); - } - } - return true; + return _inlineAutofill.onInlineSuggestionsResponse(this, response); } } From 1d164cd240886bad5320bbe6e00d235dbe8680cf Mon Sep 17 00:00:00 2001 From: JFronny Date: Thu, 2 Oct 2025 15:31:37 +0200 Subject: [PATCH 4/5] Always set visibility --- srcs/juloo.keyboard2/InlineAutofill.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/srcs/juloo.keyboard2/InlineAutofill.java b/srcs/juloo.keyboard2/InlineAutofill.java index 249050f2b..e77b0f923 100644 --- a/srcs/juloo.keyboard2/InlineAutofill.java +++ b/srcs/juloo.keyboard2/InlineAutofill.java @@ -35,7 +35,6 @@ public void onInflate(View keyboardViewParent) { _autofillContainer = keyboardViewParent.findViewById(R.id.autofill_container); _autofillSuggestions = keyboardViewParent.findViewById(R.id.autofill_suggestions); - _autofillContainer.setVisibility(View.GONE); } @Nullable @@ -68,7 +67,7 @@ public boolean onInlineSuggestionsResponse(Context context, @NonNull InlineSugge Size autofillSize = new Size(ViewGroup.LayoutParams.WRAP_CONTENT, ((int) height)); _autofillSuggestions.removeAllViews(); - if (!inlineSuggestions.isEmpty()) _autofillContainer.setVisibility(View.VISIBLE); + _autofillContainer.setVisibility(inlineSuggestions.isEmpty() ? View.GONE : View.VISIBLE); for (InlineSuggestion inlineSuggestion : inlineSuggestions) { From b775ef1bbef6a34f18e0b83e34961180f884238e Mon Sep 17 00:00:00 2001 From: JFronny Date: Thu, 2 Oct 2025 15:32:24 +0200 Subject: [PATCH 5/5] Also use background for suggestion pane --- res/layout/keyboard.xml | 4 ++-- srcs/juloo.keyboard2/Keyboard2.java | 1 + srcs/juloo.keyboard2/Keyboard2View.java | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/layout/keyboard.xml b/res/layout/keyboard.xml index 9e7159edb..629ead336 100644 --- a/res/layout/keyboard.xml +++ b/res/layout/keyboard.xml @@ -1,5 +1,5 @@ - + - + diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 423038e6d..3c076ccac 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -132,6 +132,7 @@ private void inflate_keyboardView() { _keyboardViewParent = inflate_view(R.layout.keyboard); _inlineAutofill.onInflate(_keyboardViewParent); _keyboardView = _keyboardViewParent.findViewById(R.id.keyboard_view); + _keyboardViewParent.getBackground().setAlpha(_config.keyboardOpacity); } @Override diff --git a/srcs/juloo.keyboard2/Keyboard2View.java b/srcs/juloo.keyboard2/Keyboard2View.java index b3817d43f..0040ed42a 100644 --- a/srcs/juloo.keyboard2/Keyboard2View.java +++ b/srcs/juloo.keyboard2/Keyboard2View.java @@ -340,7 +340,6 @@ public WindowInsets onApplyWindowInsets(WindowInsets wi) protected void onDraw(Canvas canvas) { // Set keyboard background opacity - getBackground().setAlpha(_config.keyboardOpacity); float y = _tc.margin_top; for (KeyboardData.Row row : _keyboard.rows) {