Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
97 changes: 92 additions & 5 deletions srcs/juloo.keyboard2/Keyboard2.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -520,4 +545,66 @@ 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<InlinePresentationSpec> specList = new ArrayList<>();
specList.add(spec);

InlineSuggestionsRequest.Builder builder = new InlineSuggestionsRequest.Builder(specList);
Comment thread
JFronny marked this conversation as resolved.
Outdated

return builder.setMaxSuggestionCount(InlineSuggestionsRequest.SUGGESTION_COUNT_UNLIMITED)
Comment thread
JFronny marked this conversation as resolved.
Outdated
.build();
}

@RequiresApi(api = Build.VERSION_CODES.R)
@Override
public boolean onInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response)
{
List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
Comment thread
JFronny marked this conversation as resolved.
Outdated

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);
Comment thread
JFronny marked this conversation as resolved.
Outdated
_autofillSuggestions.addView(inlineContentView);
});
}
catch (Exception e)
{
Log.e(TAG, "onInlineSuggestionsResponse - inlineSuggestion.infLate - " + e);
}
}
return true;
}
}