Obsługa klawiatury graficznej

Użytkownicy często chcą się komunikować za pomocą emotikonów, naklejek i innych rodzajów atrakcyjnych treści. We wcześniejszych wersjach Androida klawiatury programowe (nazywane też edytorami metod wprowadzania lub IME) mogły wysyłać do aplikacji tylko emotikony w standardzie Unicode. W przypadku treści multimedialnych aplikacje tworzą interfejsy API przeznaczone specjalnie dla aplikacji, których nie można używać w innych aplikacjach, lub korzystają z obejść obejścia, takich jak wysyłanie obrazów za pomocą prostego udostępniania lub schowka.

Obraz przedstawiający klawiaturę obsługującą wyszukiwanie grafiki
Rysunek 1. Przykład obsługi klawiatury graficznej.

Począwszy od Androida 7.1 (poziom interfejsu API 25), pakiet SDK Androida zawiera interfejs Commit Content API, który zapewnia edytorom IME uniwersalny sposób przesyłania obrazów i innych treści bezpośrednio do edytora tekstu w aplikacji. Ten interfejs API jest też dostępny w bibliotece pomocy do wersji 25.0.0 w bibliotece pomocy do wersji 25.0.0. Zalecamy korzystanie z Biblioteki pomocy, ponieważ zawiera ona metody pomocnicze, które upraszczają implementację.

Dzięki temu interfejsowi API możesz tworzyć aplikacje do obsługi wiadomości, które akceptują treści multimedialne z dowolnej klawiatury oraz klawiatury, z których można przesyłać rozbudowane treści do dowolnej aplikacji. Klawiatura Google i aplikacje takie jak Wiadomości firmy Google obsługują interfejs Commit Content API w Androidzie 7.1, jak widać na ilustracji 1.

Ten dokument pokazuje, jak wdrożyć interfejs Commit Content API zarówno w IME, jak i w aplikacjach.

Jak to działa

Wstawianie obrazu klawiatury wymaga działania z poziomu edytora IME i aplikacji. Poniżej opisano każdy krok procesu wstawiania obrazu:

  1. Gdy użytkownik kliknie EditText, edytor wyśle listę typów treści MIME akceptowanych w EditorInfo.contentMimeTypes.

  2. Edytor IME odczytuje listę obsługiwanych typów i wyświetla w klawiaturze programowej treść, którą może zaakceptować edytor.

  3. Gdy użytkownik wybierze obraz, edytor IME wywołuje metodę commitContent() i wysyła do edytora element InputContentInfo. Wywołanie commitContent() jest analogiczne do wywołania commitText(), ale w przypadku szczegółowych treści. InputContentInfo zawiera identyfikator URI, który identyfikuje treści u dostawcy treści.

Proces ten przedstawiono na rys. 2:

Obraz przedstawiający sekwencję od aplikacji do edytora IME i z powrotem do aplikacji
Rysunek 2. Stosowanie do IME do przepływu aplikacji.

Dodawanie obsługi obrazów do aplikacji

Aby aplikacja mogła akceptować sformatowaną zawartość z edytorów IME, musi ona poinformować edytory IME, jakie typy treści akceptuje, oraz określić metodę wywołania zwrotnego wykonywaną po otrzymaniu treści. Ten przykład pokazuje, jak utworzyć obiekt EditText, który akceptuje obrazy PNG:

Kotlin

var editText: EditText = object : EditText(this) {
    override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection {
        var ic = super.onCreateInputConnection(outAttrs)
        EditorInfoCompat.setContentMimeTypes(outAttrs, arrayOf("image/png"))
        val mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this)
        if (mimeTypes != null) {
            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes)
            ic = InputConnectionCompat.createWrapper(this, ic, outAttrs)
        }
        return ic
    }
}

Java

EditText editText = new EditText(this) {
    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        InputConnection ic = super.onCreateInputConnection(outAttrs);
        EditorInfoCompat.setContentMimeTypes(outAttrs, new String[]{"image/png"});
        String[] mimeTypes = ViewCompat.getOnReceiveContentMimeTypes(this);
        if (mimeTypes != null) {
            EditorInfoCompat.setContentMimeTypes(outAttrs, mimeTypes);
            ic = InputConnectionCompat.createWrapper(this, ic, outAttrs);
        }
        return ic;
    }
};

Dalsze wyjaśnienia:

  • W tym przykładzie korzystamy z biblioteki pomocy, więc istnieją odwołania do metody android.support.v13.view.inputmethod zamiast android.view.inputmethod.

  • W tym przykładzie tworzy się EditText i zastępuje swoją metodę onCreateInputConnection(EditorInfo), aby zmodyfikować InputConnection. InputConnection to kanał komunikacji między edytorem IME a aplikacją, która otrzymuje dane wejściowe.

  • Wywołanie super.onCreateInputConnection() zachowuje wbudowane działanie, czyli wysyłanie i odbieranie tekstu, oraz udostępnia odwołanie do metody InputConnection.

  • setContentMimeTypes() dodaje listę obsługiwanych typów MIME do EditorInfo. Zadzwoń pod numer super.onCreateInputConnection() przed setContentMimeTypes().

  • Element callback jest wykonywany za każdym razem, gdy edytor IME zatwierdzi treść. Metoda onCommitContent() odwołuje się do metody InputContentInfoCompat, która zawiera identyfikator URI treści.

    • Żądaj uprawnień i udostępniaj je, jeśli Twoja aplikacja korzysta z interfejsu API na poziomie 25 lub wyższym, a flaga INPUT_CONTENT_GRANT_READ_URI_PERMISSION jest ustawiona przez IME. W przeciwnym razie masz już dostęp do identyfikatora URI treści, ponieważ został on przyznany przez IME lub dlatego, że dostawca treści nie ogranicza dostępu. Więcej informacji znajdziesz w artykule Dodawanie obsługi obrazów do IME.
  • createWrapper() zawija InputConnection, zmodyfikowany EditorInfo i wywołanie zwrotne do nowego elementu InputConnection, a następnie je zwraca.

Oto zalecane metody:

  • Edytorzy, którzy nie obsługują treści rozszerzonych, nie dzwonią pod numer setContentMimeTypes(), a dla opcji „EditorInfo.contentMimeTypes” pozostawia wartość null.

  • Edytorzy ignorują treść, jeśli typ MIME określony w zasadzie InputContentInfo nie pasuje do żadnego z akceptowanych typów.

  • Treści multimedialne nie mają wpływu na pozycję kursora tekstu. Edytorzy mogą ignorować pozycję kursora podczas pracy z treścią.

  • W metodzie OnCommitContentListener.onCommitContent() edytora możesz zwrócić wartość true asynchronicznie, jeszcze przed wczytaniem treści.

  • W przeciwieństwie do tekstu, który można edytować w edytorze IME przed zatwierdzeniem, treści multimedialne są zatwierdzane od razu. Jeśli chcesz, aby użytkownicy mogli edytować lub usuwać treści, zaimplementuj tę logikę samodzielnie.

Aby przetestować aplikację, upewnij się, że urządzenie lub emulator ma klawiaturę, która może wysyłać szczegółowe treści. Klawiatury Google możesz używać na Androidzie 7.1 lub nowszym.

Dodaj obsługę obrazów do IME

Edytory IME, które chcą wysyłać treści multimedialne do aplikacji, muszą implementować interfejs Commit Content API. Jak widać w tym przykładzie:

  • Zastąp onStartInput() lub onStartInputView() i przeczytaj listę obsługiwanych typów treści w edytorze celów. Poniższy fragment kodu pokazuje, jak sprawdzić, czy edytor docelowy akceptuje obrazy GIF.

Kotlin

override fun onStartInputView(editorInfo: EditorInfo, restarting: Boolean) {
    val mimeTypes: Array<String> = EditorInfoCompat.getContentMimeTypes(editorInfo)

    val gifSupported: Boolean = mimeTypes.any {
        ClipDescription.compareMimeTypes(it, "image/gif")
    }

    if (gifSupported) {
        // The target editor supports GIFs. Enable the corresponding content.
    } else {
        // The target editor doesn't support GIFs. Disable the corresponding
        // content.
    }
}

Java

@Override
public void onStartInputView(EditorInfo info, boolean restarting) {
    String[] mimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);

    boolean gifSupported = false;
    for (String mimeType : mimeTypes) {
        if (ClipDescription.compareMimeTypes(mimeType, "image/gif")) {
            gifSupported = true;
        }
    }

    if (gifSupported) {
        // The target editor supports GIFs. Enable the corresponding content.
    } else {
        // The target editor doesn't support GIFs. Disable the corresponding
        // content.
    }
}

  • Zatwierdź treść do aplikacji, gdy użytkownik wybierze obraz. Unikaj wywoływania funkcji commitContent() podczas tworzenia tekstu, ponieważ może to spowodować utratę uwagi edytora. Poniższy fragment kodu pokazuje, jak zatwierdzić obraz GIF.

Kotlin

// Commits a GIF image.

// @param contentUri = Content URI of the GIF image to be sent.
// @param imageDescription = Description of the GIF image to be sent.

fun commitGifImage(contentUri: Uri, imageDescription: String) {
    val inputContentInfo = InputContentInfoCompat(
            contentUri,
            ClipDescription(imageDescription, arrayOf("image/gif")),
            null
    )
    val inputConnection = currentInputConnection
    val editorInfo = currentInputEditorInfo
    var flags = 0
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        flags = flags or InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION
    }
    InputConnectionCompat.commitContent(inputConnection, editorInfo, inputContentInfo, flags, null)
}

Java

// Commits a GIF image.

// @param contentUri = Content URI of the GIF image to be sent.
// @param imageDescription = Description of the GIF image to be sent.

public static void commitGifImage(Uri contentUri, String imageDescription) {
    InputContentInfoCompat inputContentInfo = new InputContentInfoCompat(
            contentUri,
            new ClipDescription(imageDescription, new String[]{"image/gif"}),
            null
    );
    InputConnection inputConnection = getCurrentInputConnection();
    EditorInfo editorInfo = getCurrentInputEditorInfo();
    Int flags = 0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        flags |= InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION;
    }
    InputConnectionCompat.commitContent(
            inputConnection, editorInfo, inputContentInfo, flags, null);
}

Jako autor IME prawdopodobnie musisz wdrożyć własnego dostawcę treści, aby odpowiadać na żądania URI treści. Wyjątkiem jest sytuacja, gdy edytor IME obsługuje treści od obecnych dostawców treści, takich jak MediaStore. Informacje na temat tworzenia dostawców treści znajdziesz w dokumentacji dostawcy treści i dostawcy plików.

Jeśli tworzysz własnego dostawcę treści, nie eksportuj go przez ustawienie parametru android:exported na false. Zamiast tego włącz przyznawanie uprawnień u dostawcy, ustawiając parametr android:grantUriPermission na true. Gdy treść zostanie zatwierdzona, edytor IME może przyznać Ci uprawnienia dostępu do identyfikatora URI treści. Można to zrobić na dwa sposoby:

  • W Androidzie w wersji 7.1 (poziom interfejsu API 25) i nowszych przy wywoływaniu funkcji commitContent() ustaw parametr flagi na INPUT_CONTENT_GRANT_READ_URI_PERMISSION. Następnie obiekt InputContentInfo odebrany przez aplikację może prosić o tymczasowe uprawnienia do odczytu i zwalniać je, wywołując metody requestPermission() i releasePermission().

  • W Androidzie 7.0 (poziom interfejsu API 24) i starszych funkcja INPUT_CONTENT_GRANT_READ_URI_PERMISSION jest ignorowana, więc ręcznie przyznaj uprawnienia do treści. Możesz to zrobić na przykład za pomocą narzędzia grantUriPermission(), ale możesz też wdrożyć własny mechanizm, który spełnia Twoje wymagania.

Aby przetestować edytor IME, upewnij się, że urządzenie lub emulator ma aplikację, która może pobierać treści multimedialne. Aplikacja Google Messenger działa na Androidzie 7.1 lub nowszym.