Resim klavyesi desteği

Kullanıcılar genellikle emoji, çıkartma ve diğer zengin içerik türlerini kullanarak iletişim kurmak isterler. Android'in önceki sürümlerinde, giriş yöntemi düzenleyicileri veya IME'ler olarak da bilinen sanal klavyeler uygulamalara yalnızca Unicode emojileri gönderebiliyordu. Zengin içerikler için uygulamalar, diğer uygulamalarda kullanılamayan, uygulamaya özel API'ler oluşturdu veya basit paylaşım işlemi ya da pano aracılığıyla resim gönderme gibi geçici çözümler kullandı.

Görsel aramayı destekleyen bir klavyeyi gösteren resim
Şekil 1. Resim klavyesi desteği örneği.

Android 7.1'den (API düzeyi 25) başlayarak Android SDK'da, IME'lerin resimleri ve diğer zengin içeriği doğrudan uygulamadaki bir metin düzenleyiciye göndermesi için evrensel bir yol sunan Commit Content API bulunur. API, 25.0.0 düzeltmesinden itibaren v13 Destek Kitaplığı'nda da mevcuttur. Uygulamayı basitleştiren yardımcı yöntemler içerdiğinden Destek Kitaplığı'nı kullanmanızı öneririz.

Bu API'yi kullanarak herhangi bir klavyeden zengin içeriği kabul eden mesajlaşma uygulamaları ve herhangi bir uygulamaya zengin içerik gönderebilen klavyeler oluşturabilirsiniz. Google Klavye ve Google'dan Mesajlar gibi uygulamalar, Android 7.1'de Commit Content API'yi destekler. Şekil 1'de gösterildiği gibi.

Bu dokümanda, Commit Content API'nin hem IME'lerde hem de uygulamalarda nasıl uygulanacağı gösterilmektedir.

Nasıl çalışır?

Klavyeden resim ekleme için IME'den ve uygulamadan katılım gerekir. Aşağıdaki sırada, resim ekleme sürecindeki her adım açıklanmaktadır:

  1. Kullanıcı bir EditText simgesine dokunduğunda düzenleyici, EditorInfo.contentMimeTypes'te kabul ettiği MIME içerik türlerinin listesini gönderir.

  2. IME, desteklenen türlerin listesini okur ve düzenleyicinin kabul edebileceği içeriği klavyede görüntüler.

  3. Kullanıcı bir resim seçtiğinde, IME commitContent() numarasını arar ve düzenleyiciye bir InputContentInfo gönderir. commitContent() çağrısı, commitText() çağrısına benzer ancak zengin içerik içindir. InputContentInfo, bir içerik sağlayıcıdaki içeriği tanımlayan bir URI içerir.

Bu süreç Şekil 2'de gösterilmektedir:

Uygulamadan IME'ye ve Uygulamaya geri dönme sürecini gösteren resim
Şekil 2. IME'ye uygulama akışı.

Uygulamalara resim desteği ekleme

Uygulamaların, IME'lerden zengin içerikleri kabul etmek için hangi içerik türlerini kabul ettiğini IME'lere bildirmesi ve içerik alındığında yürütülecek bir geri çağırma yöntemini belirtmesi gerekir. Aşağıdaki örnekte, PNG resimleri kabul eden bir EditText öğesinin nasıl oluşturulacağı gösterilmektedir:

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;
    }
};

Aşağıda daha ayrıntılı bir açıklama bulabilirsiniz:

Önerilen uygulamalar şunlardır:

  • Zengin içeriği desteklemeyen düzenleyiciler setContentMimeTypes() çağrısı yapmaz ve EditorInfo.contentMimeTypes değerlerini null değerine ayarlı olarak bırakır.

  • InputContentInfo politikasında belirtilen MIME türü, kabul ettikleri türlerin hiçbiriyle eşleşmiyorsa düzenleyiciler içeriği yoksayar.

  • Zengin içerik, metin imlecinin konumunu etkilemez ve etkilenmez. Düzenleyiciler, içerikle çalışırken imleç konumunu yoksayabilir.

  • Düzenleyicinin OnCommitContentListener.onCommitContent() yönteminde, içeriği yüklemeden önce bile true öğesini eşzamansız olarak döndürebilirsiniz.

  • Taahhütte bulunmadan önce IME'de düzenlenebilen metnin aksine zengin içerik hemen aktarılır. Kullanıcıların içeriği düzenlemesine veya silmesine izin vermek istiyorsanız mantığı kendiniz uygulayın.

Uygulamanızı test etmek için cihazınızda veya emülatörünüzde zengin içerik gönderebilen bir klavye bulunduğundan emin olun. Google Klavye'yi Android 7.1 veya sonraki sürümlerde kullanabilirsiniz.

IME'lere resim desteği ekleme

Uygulamalara zengin içerik göndermek isteyen IME'ler, aşağıdaki örnekte gösterildiği gibi Commit Content API'yi uygulamalıdır:

  • onStartInput() veya onStartInputView() değerlerini geçersiz kılın ve hedef düzenleyiciden desteklenen içerik türlerinin listesini okuyun. Aşağıdaki kod snippet'i, hedef düzenleyicinin GIF resimleri kabul edip etmediğini nasıl kontrol edeceğinizi gösterir.

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.
    }
}

  • Kullanıcı bir resim seçtiğinde içeriği uygulamaya kaydedin. Düzenleyicinin odağını kaybetmesine neden olabileceğinden metin oluşturulurken commitContent() çağrısı yapmaktan kaçının. Aşağıdaki kod snippet'inde bir GIF resminin nasıl kaydedileceği gösterilmektedir.

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);
}

Bir IME yazarı olarak, içerik URI'si isteklerine yanıt vermek için büyük olasılıkla kendi içerik sağlayıcınızı uygulamanız gerekir. Bunun istisnası, IME'nizin MediaStore gibi mevcut içerik sağlayıcıların içeriklerini desteklemesidir. İçerik sağlayıcıları oluşturma hakkında bilgi için içerik sağlayıcı ve dosya sağlayıcı dokümanlarına bakın.

Kendi içerik sağlayıcınızı oluşturuyorsanız android:exported öğesini false değerine ayarlayarak dışa aktarmamanızı öneririz. Bunun yerine, android:grantUriPermission öğesini true değerine ayarlayarak sağlayıcıda izin vermeyi etkinleştirin. Ardından, IME'niz içerik kaydedildiğinde içerik URI'sine erişim izni verebilir. Bunu iki şekilde yapabilirsiniz:

  • Android 7.1 (API düzeyi 25) ve sonraki sürümlerde commitContent() çağrısı yapılırken işaret parametresini INPUT_CONTENT_GRANT_READ_URI_PERMISSION olarak ayarlayın. Ardından, uygulamanın aldığı InputContentInfo nesnesi requestPermission() ve releasePermission() çağrıları yaparak geçici okuma izinleri isteyebilir ve yayınlayabilir.

  • Android 7.0 (API düzeyi 24) ve önceki sürümlerde INPUT_CONTENT_GRANT_READ_URI_PERMISSION göz ardı edildiği için içeriğe manuel olarak izin verin. Bunu yapmanın bir yolu grantUriPermission() kullanmaktır ancak kendi gereksinimlerinizi karşılayan kendi mekanizmanızı da uygulayabilirsiniz.

IME'nizi test etmek için cihazınızda veya emülatörünüzde zengin içerik alabilen bir uygulamanın bulunduğundan emin olun. Google Messenger uygulamasını Android 7.1 veya sonraki sürümlerde kullanabilirsiniz.