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

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

แต่รู้หรือไม่ว่าจริง ๆ แล้วนักพัฒนาสามารถเขียนโค้ดดังกล่าวได้แบบง่าย ๆ และไม่ต้องขอ Permission ด้วยนะ เพราะ AndroidX Activity หนึ่งใน Library ยอดนิยมสำหรับโปรเจคแอนดรอยด์ส่วนใหญ่ มีสิ่งที่เรียกว่า Visual Media Picker ให้เรียกใช้งานได้นะ

Visual Media Picker

เป็นหนึ่งในชุดคำสั่งที่อยู่ใน AndroidX Activity ที่จะช่วยให้นักพัฒนาสามารถเขียนโค้ดเพื่อให้ผู้ใช้เลือกรูปจากในเครื่องแบบง่าย ๆ และไม่ต้องขอ Permission เพื่อเข้าถึงข้อมูลรูปภาพภายในเครื่องผู้ใช้อีกด้วย โดยมีความสามารถและเงื่อนไขในการเรียกใช้งานดังนี้

  • รองรับทั้งการเลือกไฟล์รูปภาพและไฟล์วีดีโอ
  • สามารถเลือกแค่ไฟล์เดียวหรือจะมากกว่าหนึ่งไฟล์ (Multiple) ก็ได้
  • ต้องใช้ AndroidX Activity 1.6.0 ขึ้นไป (แต่ถ้าจะให้รองรับจนถึง Android 4.4 KitKat จะต้องเป็นเวอร์ชัน 1.7.0 ขึ้นไป)
  • ไม่ต้องขอ Permission สำหรับเข้าถึงข้อมูลรูปภาพหรือไฟล์วีดีโอภายในเครื่อง (Permissionless)
  • รองรับตั้งแต่ API Level 21 เป็นต้นไป
  • โปรเจคต้องกำหนด Target SDK Version 33 เป็นอย่างน้อย

รูปแบบในการทำงาน

เพื่อให้ Visual Media Picker สามารถเรียกใช้งานได้แบบ Permissionless หรือไม่ต้องขอ Permission จากผู้ใช้งาน จึงทำให้การทำงานของ Visual Media Picker แบ่งเงื่อนไขในการทำงานออกเป็น 2 แบบด้วยกัน คือ Photo Picker กับ Storage Access Framework

Photo Picker

เป็นความสามารถที่ถูกเพิ่มเข้ามาในระบบแอนดรอยด์ตั้งแต่ Android 13 เป็นต้นไป เพื่ออำนวยความสะดวกให้กับผู้ใช้และนักพัฒนา โดยจะมีจุดเด่นตรงที่ไม่ต้องขอ Permission จากผู้ใช้ (Permissionless) และมี User Experience ที่ต่อเนื่องไปกับการใช้งานแอป เพราะไม่ต้องสลับแอปเพื่อเลือกรูป โดยที่มีทีมแอนดรอยด์ดูแลโค้ดและปล่อยอัปเดตผ่าน Google Play Services ให้ตลอดเวลา

0:00
/

และนอกจากนี้ Photo Picker จะรองรับบนแอนดรอยด์เวอร์ชันเก่า ๆ ผ่าน Google Play Services อีกด้วย ต่างจาก Android 13 ที่มีมาให้ในระบบแอนดรอยด์ตั้งแต่แรก และรองรับย้อนหลังไปจนถึง Android 4.4 KitKat (API Level 19) เลยทีเดียว โดยที่

  • Android 11 - 12L – รองรับผ่าน Google System Update เพื่อเรียกใช้งานได้จาก Android System เหมือนกับ Android 13
  • Android 4.4 - 10 – เรียกใช้งาน Photo Picker ที่สร้างแยกขึ้นมา (Backported Version) ไว้ใน Google Play Services แทน

ดังนั้นในมุมของนักพัฒนาและผู้ใช้อาจจะมองว่ามันก็คือ Photo Picker เหมือนกัน แต่เบื้องหลังคือ Android System และ Google Play Services จะคอยจัดการกับ Photo Picker ในแอนดรอยด์แต่ละเวอร์ชันให้

Storage Access Framework

เป็นความสามารถที่ถูกเพิ่มเข้ามาตั้งแต่ Android 4.4 KitKat (API Level 19) เพื่อคอยจัดการเกี่ยวกับการเลือกไฟล์ข้อมูลภายในเครื่องจากผู้ใช้ แทนที่จะให้นักพัฒนาแอปเป็นคนเขียนคำสั่งเพื่อจัดการเองทั้งหมด ก็แค่เรียกใช้งาน Storage Access Framework แทน

เรียกสั้น ๆ ว่า SAF

โดย Storage Access Framework จะมี System Picker เพื่อให้แอปเรียกใช้งานผ่าน Intent Action จากนั้นก็จะเชื่อมต่อกับ Document Provider ที่มีอยู่ทั้งหมดภายในเครื่องเพื่อแสดงข้อมูลให้ผู้ใช้เลือกผ่าน System Picker แล้วส่งข้อมูลกลับไปให้แอปต้นทาง

ในการเรียกใช้งาน Storage Access Framework ผ่าน Visual Media Picker จะส่ง Intent Action เป็น Intent.ACTION_OPEN_DOCUMENT

รูปแบบการทำงานจะขึ้นอยู่กับแต่ละเวอร์ชันของแอนดรอยด์และ Google Play Services

อย่างที่บอกไปก่อนหน้านี้ว่า Photo Picker ถูกเพิ่มเข้ามาใน Android 13 และสามารถเรียกใช้ได้ใน Android 4.4 KitKat จนถึง Android 12L ผ่านการอัปเดตของ Google Play Services

นั่นหมายความว่าจะมีโอกาสที่อุปกรณ์แอนดรอยด์เหล่านั้นยังไม่มี Photo Picker ให้ใช้งานด้วยเหตุผลใด ๆ ก็ตาม เช่น ยังไม่ได้รับอัปเดตจาก Google System Updates เป็นเวอร์ชันที่มี Photo Picker หรือเป็นเครื่องที่ไม่มี Google Play Services เป็นต้น โดย Storage Access Framework ก็จะเป็น Fallback สำหรับเงื่อนไขเหล่านี้ทั้งหมดนั่นเอง

ดังนั้นจึงสรุปรูปแบบการทำงานในแต่ละแบบได้ดังนี้

  • Android 13 ขึ้นไป – ใช้ Photo Picker จาก Android System
  • Android 4.4 ถึง 12L แบบมี Photo Picker – ใช้ Photo Picker จาก Android System ที่ได้รับอัปเดตผ่าน Google System Updates
  • Android 4.4 ถึง 12L แบบยังไม่มี Photo Picker – ใช้ Storage Access Framework

วิธีการใช้งาน

เตรียมโปรเจคให้พร้อม

ถ้ายังไม่ได้เพิ่ม AndroidX Activity เข้าไปในโปรเจค ให้ทำการเพิ่มเข้าไปให้เรียบร้อยก่อน

implementation "androidx.activity:activity-ktx:<latest_version>"

และนอกจากนี้นักพัฒนาจะต้องเพิ่ม <service> เพื่อสั่งให้ Google Play Services ติดตั้ง Photo Picker ที่เป็น Backported Version สำหรับ Android 10 หรือต่ำกว่า

<!-- AndroidManifest.xml -->
<manifest ...>
    <application ...>
        <!-- ... -->
        <service
            android:name="com.google.android.gms.metadata.ModuleDependencies"
            android:enabled="false"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
            </intent-filter>

            <meta-data
                android:name="photopicker_activity:0:required"
                android:value="" />
        </service>
    </application>
</manifest>

การเรียกใช้คำสั่งของ Visual Media Picker

สำหรับการเรียกใช้งานก็จะมีคำสั่งเป็น Activity Result API ที่มีลักษณะแบบนี้

private val launcher: ActivityResultLauncher<PickVisualMediaRequest> =
    registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri: Uri? ->
        // Single file result
    }
คำสั่ง registerForActivityResult จะเรียกใช้งานได้เฉพาะข้างใน Activity หรือ Fragment เท่านั้น

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

ส่วนใครที่ยังไม่รู้จัก Activity Result API ก็สามารถไปอ่านย้อนหลังกันได้ในบทความการเรียกใช้งาน Activity ที่มีการส่งข้อมูลกลับด้วย Activity Result API

การเรียกใช้งาน Activity ที่มีการส่งข้อมูลกลับด้วย Activity Result API
Activity Result API เป็นหนึ่งในความสามารถของ AndroidX ที่จะช่วยให้นักพัฒนาเปลี่ยนวิธีการใช้คำสั่ง startActivityForResult ให้ง่ายกว่าที่เคยเป็นมา

กำหนดว่าจะให้ผู้ใช้เลือกไฟล์รูปภาพหรือไฟล์วีดีโอ

จะต้องกำหนดค่าผ่านคลาส PickVisualMediaRequest แบบนี้

private val launcher: ActivityResultLauncher<PickVisualMediaRequest> =
    registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri: Uri? ->
        // Single file result
    }

private fun chooseImageFile() {
    val request = PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)
    launcher.launch(request)
}

โดยที่

  • ImageOnly – เลือกได้เฉพาะไฟล์รูปภาพเท่านั้น
  • VideoOnly – เลือกได้เฉพาะไฟล์วีดีโอเท่านั้น
  • ImageAndVideo – เลือกได้ทั้งไฟล์รูปภาพและไฟล์วีดีโอ

ให้เลือกได้แค่หนึ่งไฟล์​ VS ให้เลือกได้มากกว่าหนึ่งไฟล์

การใช้งาน Visual Media Picker จะกำหนดได้ว่าอยากให้ผู้ใช้เลือกแค่ไฟล์เดียว หรือเลือกได้มากกว่านั้น โดยทั้งคู่จะใช้คำสั่งที่แตกต่างกันเล็กน้อย

// Pick one file
val launcher = registerForActivityResult(
    ActivityResultContracts.PickVisualMedia()
) { uri: Uri? ->
    // Single file result
}

// Pick multiple files
val launcher = registerForActivityResult(
    ActivityResultContracts.PickMultipleVisualMedia()
) { uris: List<Uri>? ->
    // Multiple files result
}
  • PickVisualMedia – เลือกได้แค่ไฟล์เดียว
  • PickMultipleVisualMedia – เลือกได้หลายไฟล์

ข้อจำกัดที่ควรรู้

จำนวนไฟล์ที่สามารถเลือกได้สูงสุด

ในกรณีที่ใช้ PickMultipleVisualMedia จะสามารถกำหนดได้ด้วยว่าจะให้เลือกไฟล์ได้สูงสุดเท่าไร

// Limit 5 files
val launcher = registerForActivityResult(
    ActivityResultContracts.PickMultipleVisualMedia(5)
) { uris: List<Uri>? ->
    // Multiple file result
}

ในกรณีที่ไม่ได้กำหนดค่า Maximum จะอ้างอิงจำนวนไฟล์ที่สามารถเลือกได้สูงสุดตามรูปแบบในการทำงานดังนี้

  • Photo Picker – ขึ้นอยู่กับคำสั่ง MediaStore.getPickImagesMaxLimit()
  • Storage Access Framework – เลือกได้ไม่จำกัด (ถ้าจะพูดให้ถูกก็คือ Integer.MAX_VALUE)

ด้วยเหตุนี้จึงทำให้การกำหนดจำนวนไฟล์สูงสุดที่ผู้ใช้สามารถเลือกได้จึงเป็นเรื่องยากเล็กน้อย ถ้าเป็นตัวเลขจำนวนไม่เยอะ อย่างเช่น 2-5 ไฟล์ ก็อาจจะกำหนดได้ แต่ถ้ามากกว่านั้นก็อาจจะขึ้นอยู่กับว่าใช้ Photo Picker หรือไม่

Photo Picker ยังไม่รองรับไฟล์ที่อยู่บน Cloud Storage (ในตอนนี้)

ในกรณีที่เก็บไฟล์ภาพหรือไฟล์วีดีโอไว้บน Cloud Storage อย่าง Google Drive จะยังเลือกผ่าน Photo Picker ไม่ได้ โดยทีมแอนดรอยด์จะทำเพิ่มและปล่อยอัปเดตผ่าน Google Play Services ภายในสิ้นปีนี้

ในขณะที่ Storage Access Framework จะมี Cloud Storage (ที่มี Document Provider) ให้เลือกตามปกติ

จากการทดสอบพบว่า Google Photos ไม่มี Document Provider สำหรับ Storage Access Framework

สรุป

Visual Media Picker นั้นช่วยอำนวยความสะดวกให้นักพัฒนาที่ต้องการทำให้แอปสามารถเลือกไฟล์รูปภาพหรือไฟล์วีดีโอในเครื่องของผู้ใช้ได้ง่ายมาก แทนที่นักพัฒนาจะต้องลงมือเขียนด้วยตัวเองทั้งหมด และรองรับ Photo Picker บนเวอร์ชันใหม่ ในขณะที่รองรับกับแอนดรอยด์เวอร์ชันเก่าผ่าน Storage Access Storage

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

แหล่งข้อมูลอ้างอิง