10 December 2019

อยากจะเปิดหน้าแชทใน Facebook Messenger ผ่านแอปต้องทำยังไง?

Created on Tuesday, December 10, 2019

วันนี้ขอเป็นบทความง่ายๆ สำหรับนักพัฒนาที่อยากจะทำให้แอปของตัวเองสามารถเปิด Facebook Messenger แล้วเข้าไปที่หน้าแชทที่ต้องการทันที ซึ่งเป็น Requirement ทั่วๆไปอยู่แล้ว

Intent พ่อทุกสถานบัน + Deep Linking

ในการเรียกใช้งานแอปอื่นๆที่อยู่ภายในอุปกรณ์แอนดรอยด์ ได้มีการกำหนดรูปแบบในการเรียกใช้งานไว้แล้ว นั่นก็คือการสร้าง Intent ที่ระบุรายละเอียดของแอปที่ต้องการให้ทำงาน แล้วใช้คำสั่ง startActivity(Intent) จากนั้น Android System ก็จะดูว่าจะต้องเปิดแอปไหนขึ้นมา

แต่การใช้ Intent แบบ Explicit Intent ที่จะต้องกำหนดชื่อ Package Name ของแอป และ Activity Class ของ Facebook Messenger ให้ถูกต้องเป๊ะๆ ดูไม่ค่อยปลอดภัยเท่าไรถ้าในอนาคตแอป FB Messenger มีการอัปเดทใหม่และมีการเปลี่ยนชื่อ Class (แอปพังนั่นเอง)

ดังนั้นการทำ Deep Linking ด้วย Implicit Intent จึงเป็นวิธีที่เหมาะสมกว่า ขอแค่รู้ว่า URL ที่ FB Messenger ได้เขียนรองรับไว้คือ URL อะไร

แล้ว URL สำหรับ Deep Linking ของ FB Messenger คือ?

ถ้าได้ลองหาข้อมูลจาก StackOverflow ก็จะพบว่า Facebook Messenger ได้เตรียม URL สำหรับการทำ Deep Linking ไว้ประมาณนี้

fb-messenger://user/[user_id]
https://m.me/[user_id]

โดยจะมีให้เลือกทั้ง 2 แบบ ต่างกันตรงที่ Scheme และ user_id ก็คือ Facebook User ID ของบุคคลหรือเพจที่ต้องการคุยด้วยนั่นเอง จะสามารถใช้เว็ปอย่าง Find Your Facebook ID เพื่อหา ID ก็ได้นะ

สมมติว่าเจ้าของบล็อกต้องการทำ Deep Linking เพื่อเข้าหน้าแชทกับเพจของเจ้าของบล็อกก็จะได้ออกมาเป็น URL ดังนี้

fb-messenger://user/577361412281379
https://m.me/577361412281379

ควรเลือก URL อันไหนไปสร้างเป็น Implicit Intent?

บ้างก็ใช้ fb-messenger://user บ้างก็ใช้ https://m.me ถึงแม้จะดูเหมือนว่าทั้ง 2 URL นี้ให้ผลลัพธ์ที่เหมือนกัน แต่จริงๆแล้วมีความแตกต่างอยู่นะ

เริ่มจาก https://m.me 

เนื่องจาก URL แบบนี้จะมี Scheme เป็น https ซึ่งจะมีแอปอย่าง Web Browser คอยดัก Scheme แบบนี้อยู่แล้ว เวลาที่เรียกใช้งานด้วย URL แบบนี้จะทำให้ Android System ตัดสินใจไม่ถูกว่าจะต้องใช้แอปไหนเปิด URL ดังกล่าว ก็เลยต้องถามผู้ใช้ว่าอยากจะให้เปิดด้วยแอปตัวไหนทุกครั้ง


ถ้าผู้ใช้อยากจะให้เปิดด้วย FB Messenger ทุกครั้งโดยไม่ต้องถาม ก็ต้องกดเลือกเป็น Always ในหน้านี้ด้วย ซึ่งเอาเข้าจริงก็อยากจะให้เปิดแอปทันทีโดยไม่ต้องให้ผู้ใช้เลือกใช่มั้ยล่ะ?

แปลว่า fb-messenger://user นั้นดีกว่า?

ข้อดีของ URL แบบนี้ก็คือมี Scheme ที่ระบุเฉพาะเจาะจงไปที่แอป FB Messenger โดยตรง (ไม่ค่อยมีแอปไหนดัก Scheme ตัวนี้) จึงทำให้ Android System รู้ได้ทันทีเพราะมีแค่แอปเดียวที่รองรับและเปิด FB Messenger ขึ้นมาทันที (ตราบใดที่ไม่มีแอปอื่นๆในเครื่องที่ดัก Scheme นี้)

แต่ข้อเสียที่ไม่ควรมองข้ามไปเลยก็คือ "กรณีที่อุปกรณ์แอนดรอยด์ไม่ได้ติดตั้ง FB Messenger" นั้นไว้ ซึ่งนักพัฒนาจะต้องเขียนโค้ดจัดการในกรณีนี้ด้วยทุกครั้ง เพราะเมื่อ Android System พบว่าไม่มีแอปไหนที่รองรับ Scheme นั้นๆ ก็จะโยนเป็น Exception กลับมาให้ทันที แล้วก็บู้ม! แอปพังเป็นโกโก้ครั้นช์

FATAL EXCEPTION: main
Process: com.akexorcist.sleepingforless, PID: 4448
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.akexorcist.sleepingforless/com.akexorcist.sleepingforless.MainActivity}: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=fb-messenger://user/577361412281379 }
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
    ...
 Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=fb-messenger://user/577361412281379 }
    at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1809)
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1523)
    ...

ควรใช้ทั้ง 2 แบบ แล้วจัดการให้เหมาะสมกับทุกกรณี

ให้เริ่มด้วยการใช้ fb-messenger://user ก่อน เพราะมีข้อดีคือสามารถเปิดไปที่แอป FB Messenger ได้ทันที แล้วทำการเช็คก่อนว่าในอุปกรณ์แอนดรอยด์นั้นๆมีการติดตั้งแอป FB Messenger หรือไม่ ถ้าไม่มีก็ค่อยเปลี่ยนไปใช้เป็น https://m.me แทน


แต่ปัญหาต่อมาก็คือการเช็คว่ามีแอป FB Messenger ไม่ใช่วิธีที่ยืดหยุ่นซักเท่าไร โดยเฉพาะกรณีที่ผู้ใช้ติดตั้ง FB Messenger Lite หรือ Alternative Chat App ที่รองรับ Facebook

ดังนั้นเพื่อให้รองรับกรณีดังกล่าวด้วย เจ้าของบล็อกจึงแนะนำให้เช็คไปเลยว่า อุปกรณ์แอนดรอยด์นั้นๆรองรับ fb-messenger://user หรือไม่


เมื่อเขียนออกมาเป็นโค้ด ก็จะได้คำสั่งออกมาเป็นแบบนี้

val context = ... 
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("fb-messenger://user/$facebookId"))
val activities = context.packageManager.queryIntentActivities(intent, 0)
if (activities.isNotEmpty()) {
    // Some apps are supported in this URL
} else {
    // No app supported
}

จากนั้นก็จะได้ออกมาเป็นโค้ดที่รองรับกับ URL ทั้ง 2 แบบและรองรับกับอุปกรณ์แอนดรอยด์ในหลายๆกรณี ที่เป็นคำสั่งง่ายๆแบบนี้นั่นเอง

private fun openMessenger(context: Context, facebookId: String) {
    var intent = Intent(Intent.ACTION_VIEW, Uri.parse("fb-messenger://user/$facebookId"))
    val activities = context.packageManager.queryIntentActivities(intent, 0)
    if (activities.isEmpty()) {
        intent = Intent(Intent.ACTION_VIEW, Uri.parse("http://m.me/$facebookId"))
    }
    context.startActivity(intent)
}

เรื่องน่ารู้

ถ้าอุปกรณ์แอนดรอยด์ได้ติดตั้งแอปที่รองรับ fb-messenger://user มากกว่า 1 แอป สุดท้ายแล้ว Android System ก็จะแสดง Dialog ขึ้นมาถามอยู่ดีว่าจะให้เปิดแอปไหน ไม่ต่างอะไรกับ https://m.me เพียงแค่มีโอกาสเกิดขึ้นได้น้อยกว่า

สรุป

เมื่อใดก็ตามที่อยากจะเปิดแอปอื่นๆโดยส่งข้อมูลบางอย่างไปด้วยได้ ขอให้นึกถึง Deep Linking เป็นอย่างแรก เพราะเป็นวิธียอดนิยมที่แอปส่วนใหญ่ใช้เพื่อรองรับกับการทำงานร่วมกับหน้าเว็ปอยู่แล้ว โดยบางแอปก็อาจจะมี Scheme ที่แตกต่างกันไปเพื่อไม่ให้ทับซ้อนกับแอปอื่นๆอย่างที่ FB Messenger ทำนั่นเอง

และสิ่งที่ผู้ที่หลงเข้ามาอ่านจะต้องรู้ก็คือ Scheme ที่แอปนั้นๆรองรับ โดยจะหาข้อมูลจากอินเตอร์เน็ตก็ได้ แต่ถ้าไม่ได้จริงๆก็อาจจะต้องส่องใน Android Manifest ของแอปนั้นๆแทน เพราะจะมีรายละเอียดบอกไว้อยู่ว่า Activity ไหนๆที่รองรับ Deep Linking และรองรับ URL อะไรบ้าง