ตอบสนอง Use Case ทั่วไปในขณะที่มีระดับการเข้าถึงแพ็กเกจที่จำกัด

เอกสารนี้นำเสนอกรณีการใช้งานทั่วไปหลายรายการที่แอปโต้ตอบกับแอปอื่นๆ แต่ละส่วนมีคำแนะนำเกี่ยวกับวิธีทำให้แอปทำงานได้โดยมีระดับการเข้าถึงแพ็กเกจแบบจำกัด ซึ่งคุณควรพิจารณาหากแอปกำหนดเป้าหมายเป็น Android 11 (API ระดับ 30) ขึ้นไป

เมื่อแอปที่กำหนดเป้าหมายเป็น Android 11 ขึ้นไปใช้ Intent เพื่อเริ่มกิจกรรมในแอปอื่น วิธีที่ใช้ได้ผลที่สุดคือเรียกใช้ Intent และจัดการข้อยกเว้น ActivityNotFoundException หากไม่มีแอปใดพร้อมใช้งาน

หากส่วนหนึ่งของแอปต้องรู้ว่าการเรียกไปยัง startActivity() จะสำเร็จหรือไม่ เช่น การแสดง UI ให้เพิ่มองค์ประกอบลงในองค์ประกอบ <queries> ของไฟล์ Manifest ของแอป โดยปกติแล้วจะเป็นองค์ประกอบ <intent>

เปิด URL

ส่วนนี้จะอธิบายวิธีต่างๆ ในการเปิด URL ในแอปที่กำหนดเป้าหมายเป็น Android 11 ขึ้นไป

เปิด URL ในเบราว์เซอร์หรือแอปอื่น

หากต้องการเปิด URL ให้ใช้ Intent ที่มีการดำเนินการของ Intent ACTION_VIEW ตามที่อธิบายไว้ในคู่มือการโหลด URL ของเว็บ หลังจากเรียก startActivity() โดยใช้ Intent นี้ สิ่งใดสิ่งหนึ่งต่อไปนี้จะเกิดขึ้น

  • URL จะเปิดในแอปเว็บเบราว์เซอร์
  • URL จะเปิดในแอปที่รองรับ URL เป็น Deep Link
  • กล่องโต้ตอบที่มีคำอธิบายจะปรากฏขึ้น ซึ่งช่วยให้ผู้ใช้เลือกแอปที่จะเปิด URL ได้
  • ActivityNotFoundException เกิดขึ้นเนื่องจากไม่มีการติดตั้งแอปในอุปกรณ์ที่เปิด URL ได้ (ซึ่งผิดปกติ)

    เราขอแนะนําให้แอปของคุณจับและจัดการ ActivityNotFoundException หากเกิดขึ้น

เนื่องจากเมธอด startActivity() ไม่จำเป็นต้องใช้ระดับการเข้าถึงแพ็กเกจเพื่อเริ่มกิจกรรมของแอปพลิเคชันอื่น คุณจึงไม่ต้องเพิ่มองค์ประกอบ <queries> ลงในไฟล์ Manifest ของแอปหรือทำการเปลี่ยนแปลงใดๆ กับองค์ประกอบ <queries> ที่มีอยู่ กรณีนี้เกิดขึ้นได้สำหรับทั้ง Intent ที่ชัดแจ้งและโดยนัยที่เปิด URL ขึ้นมา

ตรวจสอบว่าเบราว์เซอร์พร้อมใช้งานหรือไม่

ในบางกรณี แอปอาจต้องตรวจสอบว่ามีเบราว์เซอร์อย่างน้อย 1 รายการที่พร้อมใช้งานในอุปกรณ์ หรือเบราว์เซอร์หนึ่งๆ เป็นเบราว์เซอร์เริ่มต้น ก่อนที่จะพยายามเปิด URL ในกรณีเหล่านั้น ให้ใส่เอลิเมนต์ <intent> ต่อไปนี้ซึ่งเป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" />
</intent>

เมื่อคุณเรียกใช้ queryIntentActivities() และส่ง Intent ของเว็บเป็นอาร์กิวเมนต์ รายการที่แสดงผลจะรวมแอปเบราว์เซอร์ที่ใช้ได้อยู่ด้วยในบางกรณี รายการนี้จะไม่รวมแอปเบราว์เซอร์หากผู้ใช้กำหนดค่า URL ให้เปิดในแอปที่ไม่ใช่เบราว์เซอร์โดยค่าเริ่มต้น

เปิด URL ในแท็บที่กำหนดเอง

แท็บที่กำหนดเองช่วยให้แอปปรับแต่งรูปลักษณ์ของเบราว์เซอร์ได้ คุณสามารถเปิด URL ในแท็บที่กำหนดเองได้โดยไม่ต้องเพิ่มหรือเปลี่ยนแปลงองค์ประกอบ <queries> ในไฟล์ Manifest ของแอป

อย่างไรก็ตาม คุณอาจต้องตรวจสอบว่าอุปกรณ์มีเบราว์เซอร์ที่รองรับแท็บที่กำหนดเองหรือไม่ หรือเลือกเบราว์เซอร์ที่เฉพาะเจาะจงที่จะเปิดด้วยแท็บที่กำหนดเองโดยใช้ CustomTabsClient.getPackageName() ในกรณีดังกล่าว ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>

อนุญาตให้แอปที่ไม่ใช่เบราว์เซอร์จัดการ URL

แม้ว่าแอปจะเปิด URL ได้โดยใช้แท็บที่กำหนดเอง แต่เราขอแนะนำให้คุณอนุญาตให้แอปที่ไม่ใช่เบราว์เซอร์เปิด URL หากเป็นไปได้ หากต้องการระบุความสามารถนี้ในแอป ให้ลองเรียกใช้ startActivity() โดยใช้ Intent ที่กําหนด Flag ของ Intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER หากระบบแสดง ActivityNotFoundException แอปของคุณก็จะเปิด URL ในแท็บที่กำหนดเองได้

หาก Intent มี Flag นี้ การเรียกใช้ startActivity() จะทำให้ระบบส่ง ActivityNotFoundException เมื่อเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้เกิดขึ้น

  • การโทรจะเปิดแอปเบราว์เซอร์โดยตรง
  • การเรียกใช้นี้จะแสดงกล่องโต้ตอบการแยกแยะให้กับผู้ใช้ซึ่งมีตัวเลือกเดียวคือแอปเบราว์เซอร์

ข้อมูลโค้ดต่อไปนี้แสดงวิธีอัปเดตตรรกะเพื่อใช้FLAG_ACTIVITY_REQUIRE_NON_BROWSER Flag ของ Intent

Kotlin

try {
    val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply {
        // The URL should either launch directly in a non-browser app (if it's
        // the default) or in the disambiguation dialog.
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // Only browser apps are available, or a browser is the default.
    // So you can open the URL directly in your app, for example in a
    // Custom Tab.
    openInCustomTabs(url)
}

Java

try {
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    // The URL should either launch directly in a non-browser app (if it's the
    // default) or in the disambiguation dialog.
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Only browser apps are available, or a browser is the default.
    // So you can open the URL directly in your app, for example in a
    // Custom Tab.
    openInCustomTabs(url);
}

หลีกเลี่ยงกล่องโต้ตอบการชี้แจง

หากต้องการหลีกเลี่ยงการแสดงกล่องโต้ตอบที่มีคำอธิบายที่ผู้ใช้อาจเห็นเมื่อเปิด URL และอยากจัดการ URL ด้วยตนเองในสถานการณ์เหล่านี้แทน ให้ใช้ Intent ที่ตั้งค่าสถานะความตั้งใจ FLAG_ACTIVITY_REQUIRE_DEFAULT

หาก Intent มี Flag นี้ การเรียก startActivity() จะทําให้ระบบแสดง ActivityNotFoundException เมื่อการเรียกจะแสดงกล่องโต้ตอบการแยกแยะให้กับผู้ใช้

หาก Intent มีทั้งแฟล็กนี้และแฟล็กความตั้งใจ FLAG_ACTIVITY_REQUIRE_NON_BROWSER การเรียกใช้ไปยัง startActivity() จะทำให้ระบบส่ง ActivityNotFoundException เมื่อเงื่อนไขข้อใดข้อหนึ่งต่อไปนี้เกิดขึ้น

  • การเรียกใช้จะเปิดแอปเบราว์เซอร์โดยตรง
  • ซึ่งจะทำให้ผู้ใช้เห็นกล่องโต้ตอบที่มีคำอธิบายที่ชัดเจน

ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ Flag FLAG_ACTIVITY_REQUIRE_NON_BROWSER และ FLAG_ACTIVITY_REQUIRE_DEFAULT ร่วมกัน

Kotlin

val url = URL_TO_LOAD
try {
    // For this intent to be invoked, the system must directly launch a
    // non-browser app.
    val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply {
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER or
                FLAG_ACTIVITY_REQUIRE_DEFAULT
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling this URL.
    openInCustomTabs(url)
}

Java

String url = URL_TO_LOAD;
try {
    // For this intent to be invoked, the system must directly launch a
    // non-browser app.
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER |
            FLAG_ACTIVITY_REQUIRE_DEFAULT);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling this URL.
    openInCustomTabs(url);
}

เปิดไฟล์

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

หากแอปต้องทราบล่วงหน้าว่าแอปอื่นเปิดไฟล์หนึ่งๆ ได้หรือไม่ ให้ใส่องค์ประกอบ <intent> ไว้ในข้อมูลโค้ดต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest ระบุประเภทไฟล์หากคุณทราบอยู่แล้วว่าไฟล์เป็นประเภทใด ณ เวลาคอมไพล์

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
  <data android:mimeType="application/pdf" />
</intent>

จากนั้นคุณจะตรวจสอบได้ว่ามีแอปให้ใช้งานหรือไม่โดยโทรหา resolveActivity() เพื่อแจ้งความตั้งใจของคุณ

ให้สิทธิ์เข้าถึง URI

หมายเหตุ: แอปที่กำหนดเป้าหมายเป็น Android 11 (API ระดับ 30) ขึ้นไปและแนะนำสำหรับแอปทั้งหมด ไม่ว่า SDK เป้าหมายจะเป็นเวอร์ชันใดและส่งออกผู้ให้บริการเนื้อหาหรือไม่ ก็จำเป็นต้องประกาศสิทธิ์การเข้าถึง URI ตามที่อธิบายไว้ในส่วนนี้

สําหรับแอปที่กําหนดเป้าหมายเป็น Android 11 ขึ้นไปเพื่อเข้าถึง URI ของเนื้อหา Intent ของแอปต้องประกาศสิทธิ์เข้าถึง URI โดยการตั้งค่า Flag ของ Intent ต่อไปนี้อย่างน้อย 1 รายการ หรือทั้ง 2 รายการ FLAG_GRANT_READ_URI_PERMISSION และ FLAG_GRANT_WRITE_URI_PERMISSION

ใน Android 11 ขึ้นไป สิทธิ์การเข้าถึง URI จะให้ความสามารถต่อไปนี้แก่แอปที่ได้รับ Intent

  • อ่านจากหรือเขียนลงในข้อมูลที่ URI ของเนื้อหาแสดง โดยขึ้นอยู่กับสิทธิ์ URI ที่ระบุ
  • ดูข้อมูลแอปที่มีผู้ให้บริการเนื้อหาที่ตรงกับหน่วยงาน URI แอปที่มีผู้ให้บริการเนื้อหาอาจแตกต่างจากแอปที่ส่ง Intent

ข้อมูลโค้ดต่อไปนี้แสดงวิธีเพิ่ม Flag Intent สิทธิ์ URI เพื่อให้แอปอื่นที่กำหนดเป้าหมายเป็น Android 11 ขึ้นไปดูข้อมูลใน URI เนื้อหาได้

Kotlin

val shareIntent = Intent(Intent.ACTION_VIEW).apply {
    flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    data = CONTENT_URI_TO_SHARE_WITH_OTHER_APP
}

Java

Intent shareIntent = new Intent(Intent.ACTION_VIEW);
shareIntent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setData(CONTENT_URI_TO_SHARE_WITH_OTHER_APP);

เชื่อมต่อกับบริการ

หากแอปต้องโต้ตอบกับบริการที่ไม่แสดงโดยอัตโนมัติ คุณสามารถประกาศการดำเนินการ Intent ที่เหมาะสมภายในองค์ประกอบ <queries> ส่วนต่อไปนี้แสดงตัวอย่างการใช้บริการที่เข้าถึงโดยทั่วไป

เชื่อมต่อกับเครื่องมือการอ่านออกเสียงข้อความ

หากแอปของคุณโต้ตอบกับเครื่องมืออ่านออกเสียงข้อความ (TTS) ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.TTS_SERVICE" />
</intent>

เชื่อมต่อกับบริการการรู้จำคำพูด

หากแอปของคุณโต้ตอบกับบริการจดจำคำพูด ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.speech.RecognitionService" />
</intent>

เชื่อมต่อกับบริการเบราว์เซอร์สื่อ

หากแอปของคุณเป็นแอปเบราว์เซอร์สื่อไคลเอ็นต์ ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.media.browse.MediaBrowserService" />
</intent>

ระบุฟังก์ชันการทำงานที่กำหนดเอง

หากแอปต้องดำเนินการที่ปรับแต่งได้หรือแสดงข้อมูลที่ปรับแต่งได้โดยอิงตามการโต้ตอบกับแอปอื่นๆ คุณสามารถแสดงลักษณะการทำงานที่กำหนดเองได้โดยใช้ลายเซ็นตัวกรอง Intent เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest ส่วนต่อไปนี้มีคำแนะนำโดยละเอียดสำหรับสถานการณ์ทั่วไปหลายประการ

ค้นหาแอป SMS

หากแอปต้องการข้อมูลเกี่ยวกับชุดแอป SMS ที่ติดตั้งในอุปกรณ์ เช่น เพื่อตรวจสอบว่าแอปใดเป็นตัวแฮนเดิล SMS เริ่มต้นของอุปกรณ์ ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.SENDTO"/>
  <data android:scheme="smsto" android:host="*" />
</intent>

สร้าง Sharesheet ที่กำหนดเอง

ใช้ชีตที่ระบบมีให้ทุกครั้งที่เป็นไปได้ หรือรวมองค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.SEND" />
  <!-- Replace with the MIME type that your app works with, if needed. -->
  <data android:mimeType="image/jpeg" />
</intent>

กระบวนการสร้างชีตการแชร์ในตรรกะของแอป เช่น การเรียกใช้ queryIntentActivities() จะไม่มีการเปลี่ยนแปลงเมื่อเทียบกับ Android เวอร์ชันที่เก่ากว่า Android 11

แสดงการดำเนินการการเลือกข้อความที่กำหนดเอง

เมื่อผู้ใช้เลือกข้อความในแอป แถบเครื่องมือการเลือกข้อความจะแสดงชุดการดำเนินการที่เป็นไปได้กับข้อความที่เลือก หากแถบเครื่องมือนี้แสดงการดำเนินการที่กำหนดเองจากแอปอื่นๆ ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.PROCESS_TEXT" />
  <data android:mimeType="text/plain" />
</intent>

แสดงแถวข้อมูลที่กำหนดเองสำหรับรายชื่อติดต่อ

แอปสามารถเพิ่มแถวข้อมูลที่กำหนดเองลงในผู้ให้บริการรายชื่อติดต่อ แอปรายชื่อติดต่อต้องทําสิ่งต่อไปนี้ได้จึงจะแสดงข้อมูลที่กําหนดเองได้

  1. อ่านไฟล์ contacts.xml จากแอปอื่นๆ
  2. โหลดไอคอนที่สอดคล้องกับประเภท MIME ที่กําหนดเอง

หากแอปเป็นแอปรายชื่อติดต่อ ให้ใส่องค์ประกอบ <intent> ต่อไปนี้เป็นส่วนหนึ่งขององค์ประกอบ <queries> ในไฟล์ Manifest

<!-- Place inside the <queries> element. -->
<!-- Lets the app read the contacts.xml file from other apps. -->
<intent>
  <action android:name="android.accounts.AccountAuthenticator" />
</intent>
<!-- Lets the app load an icon corresponding to the custom MIME type. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <data android:scheme="content" android:host="com.android.contacts"
        android:mimeType="vnd.android.cursor.item/*" />
</intent>