Update sample IME code to support showing autofill inline suggestion

Test: manual
Bug: 146587788

Change-Id: I0e5fa3d811b8951fe471f76ce14a5bdbddd81581
This commit is contained in:
Feng Cao
2019-12-19 16:24:21 -08:00
parent f95737eb5c
commit 77ddcd1689
4 changed files with 133 additions and 1 deletions

View File

@@ -6,5 +6,6 @@ android_app {
sdk_version: "current",
static_libs: [
"androidx.annotation_annotation",
"android-support-annotations",
],
}

View File

@@ -22,7 +22,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/KeyboardArea">
<View style="@style/KeyboardRow.Header"/>
<LinearLayout
android:id="@+id/suggestion_view"
style="@style/KeyboardRow.Header">
</LinearLayout>
<LinearLayout style="@style/KeyboardRow">
<TextView

View File

@@ -17,15 +17,33 @@
package com.example.android.autofillkeyboard;
import android.inputmethodservice.InputMethodService;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.annotation.GuardedBy;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inline.InlinePresentationSpec;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/** The {@link InputMethodService} implementation for Autofill keyboard. */
public class AutofillImeService extends InputMethodService {
private InputView mInputView;
private Decoder mDecoder;
private LinearLayout mSuggestionStrip;
@Override
public View onCreateInputView() {
@@ -45,6 +63,7 @@ public class AutofillImeService extends InputMethodService {
mInputView.removeAllViews();
Keyboard keyboard = Keyboard.qwerty(this);
mInputView.addView(keyboard.inflateKeyboardView(LayoutInflater.from(this), mInputView));
mSuggestionStrip = mInputView.findViewById(R.id.suggestion_view);
}
@Override
@@ -56,7 +75,108 @@ public class AutofillImeService extends InputMethodService {
outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_CONTENT;
}
/***************** Inline Suggestions Demo Code *****************/
private static final String TAG = "AutofillImeService";
private Handler mMainHandler = new Handler();
@GuardedBy("this")
private List<View> mSuggestionViews = new ArrayList<>();
@GuardedBy("this")
private List<Size> mSuggestionViewSizes = new ArrayList<>();
@GuardedBy("this")
private boolean mSuggestionViewVisible = false;
@Override
public InlineSuggestionsRequest onCreateInlineSuggestionsRequest() {
Log.d(TAG, "onCreateInlineSuggestionsRequest() called");
final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
new Size(400, 100)).build());
presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
new Size(400, 100)).build());
return new InlineSuggestionsRequest.Builder(presentationSpecs)
.setMaxSuggestionCount(2)
.build();
}
@Override
public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
Log.d(TAG, "onInlineSuggestionsResponse() called");
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
onInlineSuggestionsResponseInternal(response);
});
return true;
}
private synchronized void updateInlineSuggestionVisibility(boolean visible, boolean force) {
Log.d(TAG, "updateInlineSuggestionVisibility() called, visible=" + visible + ", force="
+ force);
mMainHandler.post(() -> {
Log.d(TAG, "updateInlineSuggestionVisibility() running");
if (visible == mSuggestionViewVisible && !force) {
return;
} else if (visible) {
mSuggestionStrip.removeAllViews();
final int size = mSuggestionViews.size();
for (int i = 0; i < size; i++) {
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(
mSuggestionViewSizes.get(i).getWidth(),
mSuggestionViewSizes.get(i).getHeight());
mSuggestionStrip.addView(mSuggestionViews.get(i), layoutParams);
}
mSuggestionViewVisible = true;
} else {
mSuggestionStrip.removeAllViews();
mSuggestionViewVisible = false;
}
});
}
private synchronized void updateSuggestionViews(View[] suggestionViews, Size[] sizes) {
Log.d(TAG, "updateSuggestionViews() called");
mSuggestionViews = Arrays.asList(suggestionViews);
mSuggestionViewSizes = Arrays.asList(sizes);
updateInlineSuggestionVisibility(true, true);
}
private void onInlineSuggestionsResponseInternal(InlineSuggestionsResponse response) {
Log.d(TAG, "onInlineSuggestionsResponseInternal() called. Suggestion="
+ response.getInlineSuggestions().size());
final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
final int totalSuggestionsCount = inlineSuggestions.size();
final AtomicInteger suggestionsCount = new AtomicInteger(totalSuggestionsCount);
final View[] suggestionViews = new View[totalSuggestionsCount];
final Size[] sizes = new Size[totalSuggestionsCount];
for (int i=0; i<totalSuggestionsCount; i++) {
final int index = i;
InlineSuggestion inlineSuggestion = inlineSuggestions.get(index);
Size size = inlineSuggestion.getInfo().getPresentationSpec().getMaxSize();
inlineSuggestion.inflate(this, size,
AsyncTask.THREAD_POOL_EXECUTOR,
suggestionView -> {
Log.d(TAG, "new inline suggestion view ready");
suggestionViews[index] = suggestionView;
sizes[index] = size;
suggestionView.setOnTouchListener((v, event) -> {
Toast.makeText(AutofillImeService.this, "hello",
Toast.LENGTH_LONG).show();
return false;
});
if (suggestionsCount.decrementAndGet() == 0) {
updateSuggestionViews(suggestionViews, sizes);
}
});
}
}
void handle(String data) {
Log.d(TAG, "handle() called: [" + data + "]");
mDecoder.decode(data);
updateInlineSuggestionVisibility(mDecoder.isEmpty(), false);
}
}

View File

@@ -41,4 +41,12 @@ final class Decoder {
mInputConnection.commitText(data, 1);
}
}
boolean isEmpty() {
if (mInputConnection.getTextBeforeCursor(1, 0).length() == 0
&& mInputConnection.getTextAfterCursor(1, 0).length() == 0) {
return true;
}
return false;
}
}