รับเนื้อหาอย่างละเอียด

รูปที่ 1 Unified API มีที่เดียวสำหรับจัดการเนื้อหาที่เข้ามาใหม่ ไม่ว่าจะเป็นกลไก UI ใดก็ตาม เช่น การวางจากเมนูแตะค้างไว้หรือการใช้การลากและวาง

ผู้ใช้ชอบรูปภาพ วิดีโอ และเนื้อหาอื่นๆ ที่สื่อความหมาย แต่การแทรกและย้ายเนื้อหานี้ในแอปอาจไม่ใช่เรื่องง่ายเสมอไป Android 12 (ระดับ API 31) เปิดตัว API แบบรวมที่ช่วยให้แอปยอมรับเนื้อหาจากแหล่งที่มาใดก็ได้ ไม่ว่าจะเป็นคลิปบอร์ด แป้นพิมพ์ หรือการลาก เพื่อให้แอปได้รับข้อมูลอย่างละเอียดได้ง่ายขึ้น

คุณสามารถแนบอินเทอร์เฟซ เช่น OnReceiveContentListener กับคอมโพเนนต์ UI และรับ Callback เมื่อมีการแทรกเนื้อหาผ่านกลไกใดก็ได้ Callback จะกลายเป็นที่เดียวที่โค้ดของคุณจะจัดการการรับเนื้อหาทั้งหมด ตั้งแต่ข้อความธรรมดาและข้อความที่มีการจัดรูปแบบ ไปจนถึงมาร์กอัป รูปภาพ วิดีโอ ไฟล์เสียง และอื่นๆ

เพื่อให้เข้ากันได้แบบย้อนหลังกับ Android เวอร์ชันก่อนหน้า API นี้จึงพร้อมใช้งานใน AndroidX ด้วย โดยเริ่มตั้งแต่ Core 1.7 และ Appcompat 1.4 ซึ่งเราขอแนะนำให้คุณใช้เมื่อใช้ฟังก์ชันนี้

ภาพรวม

สำหรับ API อื่นๆ ที่มีอยู่ กลไก UI แต่ละรายการ เช่น เมนูแตะค้างไว้หรือการลาก จะมี API ที่เกี่ยวข้องของตัวเอง ซึ่งหมายความว่าคุณต้องผสานรวมกับ API แต่ละรายการแยกกัน โดยเพิ่มโค้ดที่คล้ายกันสำหรับกลไกแต่ละรายการที่แทรกเนื้อหา ดังนี้

รูปภาพแสดงการดำเนินการต่างๆ และ API ที่เกี่ยวข้องที่จะใช้
รูปที่ 2 ก่อนหน้านี้ แอปใช้ API ที่แตกต่างกันสำหรับกลไก UI แต่ละรายการในการแทรกเนื้อหา

OnReceiveContentListener API จะรวมเส้นทางโค้ดที่แตกต่างกันเหล่านี้โดยสร้าง API เดียวเพื่อนำไปใช้ คุณจึงมุ่งเน้นที่ตรรกะเฉพาะแอปและปล่อยให้แพลตฟอร์มจัดการส่วนที่เหลือได้

รูปภาพแสดง API แบบรวมที่เรียบง่าย
รูปที่ 3 API แบบรวมช่วยให้คุณใช้ API เดียวที่รองรับกลไก UI ทั้งหมดได้

นอกจากนี้ แนวทางนี้ยังหมายความว่าเมื่อมีการเพิ่มวิธีใหม่ๆ ในการแทรกเนื้อหาลงในแพลตฟอร์ม คุณไม่จำเป็นต้องทำการเปลี่ยนแปลงโค้ดเพิ่มเติมเพื่อเปิดใช้การรองรับในแอป และหากแอปของคุณจำเป็นต้องใช้การปรับแต่งอย่างเต็มรูปแบบสำหรับ Use Case หนึ่งๆ คุณก็ยังคงใช้ API ที่มีอยู่ได้ ซึ่งจะยังคงทำงานในลักษณะเดิมต่อไป

การใช้งาน

API เป็นอินเทอร์เฟซ Listener ที่มีเมธอดเดียวคือ OnReceiveContentListener หากต้องการรองรับแพลตฟอร์ม Android เวอร์ชันเก่า เราขอแนะนำให้ใช้อินเทอร์เฟซ OnReceiveContentListener ที่ตรงกันในไลบรารี AndroidX Core

หากต้องการใช้ API ให้ติดตั้งใช้งาน Listener โดยระบุประเภทเนื้อหาที่แอปของคุณจัดการได้ ดังนี้

Kotlin

object MyReceiver : OnReceiveContentListener {
    val MIME_TYPES = arrayOf("image/*", "video/*")
    
    // ...
    
    override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? {
        TODO("Not yet implemented")
    }
}

Java

public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
     // ...
}

หลังจากระบุประเภท MIME ของเนื้อหาทั้งหมดที่แอปรองรับแล้ว ให้ใช้ Listener ที่เหลือดังนี้

Kotlin

class MyReceiver : OnReceiveContentListener {
    override fun onReceiveContent(view: View, contentInfo: ContentInfoCompat): ContentInfoCompat {
        val split = contentInfo.partition { item: ClipData.Item -> item.uri != null }
        val uriContent = split.first
        val remaining = split.second
        if (uriContent != null) {
            // App-specific logic to handle the URI(s) in uriContent.
        }
        // Return anything that your app didn't handle. This preserves the
        // default platform behavior for text and anything else that you aren't
        // implementing custom handling for.
        return remaining
    }

    companion object {
        val MIME_TYPES = arrayOf("image/*", "video/*")
    }
}

Java

 public class MyReceiver implements OnReceiveContentListener {
     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};

     @Override
     public ContentInfoCompat onReceiveContent(View view, ContentInfoCompat contentInfo) {
         Pair<ContentInfoCompat, ContentInfoCompat> split = contentInfo.partition(
                 item -> item.getUri() != null);
         ContentInfo uriContent = split.first;
         ContentInfo remaining = split.second;
         if (uriContent != null) {
             // App-specific logic to handle the URI(s) in uriContent.
         }
         // Return anything that your app didn't handle. This preserves the
         // default platform behavior for text and anything else that you aren't
         // implementing custom handling for.
         return remaining;
     }
 }

หากแอปของคุณรองรับการแชร์ด้วย Intent อยู่แล้ว คุณสามารถนำตรรกะเฉพาะแอปมาใช้ซ้ำเพื่อจัดการ URI เนื้อหาได้ แสดงผลข้อมูลที่เหลือเพื่อมอบสิทธิ์การจัดการข้อมูลนั้นให้กับแพลตฟอร์ม

หลังจากติดตั้งใช้งาน Listener แล้ว ให้ตั้งค่า Listener ในองค์ประกอบ UI ที่เหมาะสมในแอป ดังนี้

Kotlin

class MyActivity : Activity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ...
        val myInput = findViewById(R.id.my_input)
        ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, MyReceiver())
    }
}

Java

public class MyActivity extends Activity {
     @Override
     public void onCreate(Bundle savedInstanceState) {
         // ...

         AppCompatEditText myInput = findViewById(R.id.my_input);
         ViewCompat.setOnReceiveContentListener(myInput, MyReceiver.MIME_TYPES, new MyReceiver());
     }
}

สิทธิ์ URI

แพลตฟอร์มจะให้และยกเลิกสิทธิ์อ่านโดยอัตโนมัติสำหรับ URI เนื้อหาในเพย์โหลดที่ส่งไปยัง OnReceiveContentListener

โดยปกติแล้ว แอปจะประมวลผล URI เนื้อหาในบริการหรือกิจกรรม สำหรับการประมวลผลที่ใช้เวลานาน ให้ใช้ WorkManager เมื่อใช้ฟีเจอร์นี้ ให้ขยายสิทธิ์ไปยังบริการหรือกิจกรรมเป้าหมายโดยส่งเนื้อหาโดยใช้ Intent.setClipData และตั้งค่าแฟล็ก FLAG_GRANT_READ_URI_PERMISSION

หรือจะใช้เทรดเบื้องหลังภายในบริบทปัจจุบันเพื่อประมวลผลเนื้อหาก็ได้ ในกรณีนี้ คุณต้องเก็บข้อมูลอ้างอิงไปยังออบเจ็กต์ payload ที่ Listener ได้รับไว้เพื่อช่วยให้มั่นใจว่าแพลตฟอร์มจะไม่เพิกถอนสิทธิ์ก่อนเวลาอันควร

มุมมองที่กำหนดเอง

หากแอปใช้คลาสย่อย View ที่กำหนดเอง โปรดตรวจสอบว่าไม่มีการข้าม OnReceiveContentListener

หากคลาส View ลบล้างเมธอด onCreateInputConnection ให้ใช้ Jetpack API InputConnectionCompat.createWrapper เพื่อกำหนดค่า InputConnection

หากคลาส View ลบล้างเมธอด onTextContextMenuItem ให้มอบสิทธิ์แก่ super เมื่อรายการเมนูเป็น R.id.paste หรือ R.id.pasteAsPlainText

การเปรียบเทียบกับ API รูปภาพแป้นพิมพ์

คุณอาจคิดว่า OnReceiveContentListener API เป็นเวอร์ชันถัดไปของ keyboard image API ที่มีอยู่ API แบบรวมนี้รองรับฟังก์ชันการทำงานของ API รูปภาพแป้นพิมพ์ รวมถึงฟีเจอร์เพิ่มเติมบางอย่าง ความเข้ากันได้ของอุปกรณ์และฟีเจอร์จะแตกต่างกันไปตามการใช้ไลบรารี Jetpack หรือ API แบบเนทีฟจาก Android SDK

ตารางที่ 1 ฟีเจอร์และระดับ API ที่รองรับสำหรับ Jetpack
การดำเนินการหรือฟีเจอร์ รองรับโดย API รูปภาพแป้นพิมพ์ รองรับโดย Unified API
แทรกจากแป้นพิมพ์ ใช่ (ระดับ API 13 ขึ้นไป) ใช่ (ระดับ API 13 ขึ้นไป)
แทรกโดยใช้การวางจากเมนูแตะค้างไว้ ไม่ ใช่
แทรกโดยใช้การลากและวาง ไม่ ใช่ (ระดับ API 24 ขึ้นไป)
ตารางที่ 2 ฟีเจอร์และระดับ API ที่รองรับสำหรับ API แบบเนทีฟ
การดำเนินการหรือฟีเจอร์ รองรับโดย API รูปภาพแป้นพิมพ์ รองรับโดย Unified API
แทรกจากแป้นพิมพ์ ใช่ (ระดับ API 25 ขึ้นไป) ใช่ (Android 12 ขึ้นไป)
แทรกโดยใช้การวางจากเมนูแตะค้างไว้ ไม่
แทรกโดยใช้การลากและวาง ไม่