Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}

Expand Down
16 changes: 15 additions & 1 deletion res/layout/keyboard.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<juloo.keyboard2.Keyboard2View xmlns:android="http://schemas.android.com/apk/res/android" android:hardwareAccelerated="false" android:background="?attr/colorKeyboard"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:hardwareAccelerated="false" android:background="#00000000">

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm working on a suggestion strip for a future word suggestion feature, in this branch: master...candidates_view

Do you have ideas how to make these compatible ? For example, it has a fixed width instead of being scrollable and it has a fixed number of spots for suggestions (3).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could add a separate layout below the autofill_container HorizontalScrollView I added here that is visible if a suggestion is available. That way, the sizing and positioning should get handled automatically, even if setting the visibility will still need to be done manually (I don't think there is a way around that)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the autofill makes sense after the user has started typing on the keyboard ? Perhaps the autofill container could be hidden in favor of the suggestion strip after the first key was pressed ?

<HorizontalScrollView
android:id="@+id/autofill_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
<LinearLayout
android:id="@+id/autofill_suggestions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="4dp"
android:orientation="horizontal" />
</HorizontalScrollView>
<juloo.keyboard2.Keyboard2View android:id="@+id/keyboard_view" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="?attr/colorKeyboard"/>
</LinearLayout>
2 changes: 1 addition & 1 deletion res/xml/method.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="juloo.keyboard2.SettingsActivity" android:supportsSwitchingToNextInputMethod="true">
<input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="juloo.keyboard2.SettingsActivity" android:supportsInlineSuggestions="true" android:supportsSwitchingToNextInputMethod="true">
<!-- The first entry of the list makes latn_qwerty_us the default layout for
unrecognized locales. -->
<subtype android:label="%s" android:languageTag="en" android:imeSubtypeLocale="en" android:imeSubtypeMode="keyboard" android:isAsciiCapable="true" android:imeSubtypeExtraValue="script=latin,default_layout=latn_qwerty_us"/>
Expand Down
90 changes: 90 additions & 0 deletions srcs/juloo.keyboard2/InlineAutofill.java
Original file line number Diff line number Diff line change
@@ -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);
Comment thread
JFronny marked this conversation as resolved.
Outdated
}

@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<InlineSuggestion> 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);
Comment thread
JFronny marked this conversation as resolved.
Outdated

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;
}
}
47 changes: 36 additions & 11 deletions srcs/juloo.keyboard2/Keyboard2.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,39 @@
package juloo.keyboard2;

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.view.*;
import android.view.inputmethod.EditorInfo;
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.LinearLayout;
import java.util.AbstractMap.SimpleEntry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
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
implements SharedPreferences.OnSharedPreferenceChangeListener
{
private View _keyboardViewParent = null;
private Keyboard2View _keyboardView;
private InlineAutofill _inlineAutofill;
private KeyEventHandler _keyeventhandler;
/** If not 'null', the layout to use instead of [_config.current_layout]. */
private KeyboardData _currentSpecialLayout;
Expand Down Expand Up @@ -117,13 +120,20 @@ public void onCreate()
Config.initGlobalConfig(prefs, getResources(), _keyeventhandler, _foldStateTracker.isUnfolded());
prefs.registerOnSharedPreferenceChangeListener(this);
_config = Config.globalConfig();
_keyboardView = (Keyboard2View)inflate_view(R.layout.keyboard);
_inlineAutofill = new InlineAutofill();
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);
_inlineAutofill.onInflate(_keyboardViewParent);
_keyboardView = _keyboardViewParent.findViewById(R.id.keyboard_view);
}

@Override
public void onDestroy() {
super.onDestroy();
Expand Down Expand Up @@ -250,10 +260,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();
}
Expand Down Expand Up @@ -283,7 +293,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);
}

Expand Down Expand Up @@ -436,7 +446,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:
Expand Down Expand Up @@ -520,4 +530,19 @@ 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)
{
return _inlineAutofill.onCreateInlineSuggestionsRequest(uiExtras);
}

@RequiresApi(api = Build.VERSION_CODES.R)
@Override
public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response)
{
return _inlineAutofill.onInlineSuggestionsResponse(this, response);
}
}