แอปที่เปิดตลอดเวลาและโหมดแอมเบียนท์ของระบบ

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

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

หัวข้อสำคัญ

เมื่อแอป Wear OS แสดงแบบเต็มหน้าจอ แอปจะอยู่ในสถานะพลังงาน 2 สถานะต่อไปนี้

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

ระบบปฏิบัติการจะควบคุมการเปลี่ยนสถานะระหว่างสถานะเหล่านี้

แอปที่เปิดตลอดเวลาคือแอปพลิเคชันที่แสดงเนื้อหาทั้งในสถานะโต้ตอบและแอมเบียนท์

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

การเปลี่ยนระบบและลักษณะการทำงานเริ่มต้น

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

  • หมดเวลา #1: สถานะโต้ตอบเป็นสถานะแวดล้อม: หลังจากที่ผู้ใช้ไม่ได้ใช้งาน ระยะหนึ่ง อุปกรณ์จะเข้าสู่สถานะแวดล้อม
  • ระยะหมดเวลา #2: กลับไปที่หน้าปัดนาฬิกา: หลังจากไม่มีการใช้งานเป็นระยะเวลาหนึ่ง ระบบอาจซ่อนแอปปัจจุบันและแสดงหน้าปัดนาฬิกา

ทันทีหลังจากที่ระบบเปลี่ยนไปสู่สถานะรอบข้างเป็นครั้งแรก ลักษณะการทำงานเริ่มต้นจะขึ้นอยู่กับเวอร์ชัน Wear OS และการกำหนดค่าของแอป

  • ใน Wear OS 5 และเวอร์ชันที่ต่ำกว่า ระบบจะแสดงภาพหน้าจอที่เบลอของแอปพลิเคชันที่หยุดชั่วคราว โดยมีเวลาซ้อนทับอยู่ด้านบน สถานะนี้แสดงโดยโหนด "AOD Lite" ในโฟลว์ชาร์ตต่อไปนี้
  • ใน Wear OS 6 ขึ้นไป หากแอปกำหนดเป้าหมายเป็น SDK 36 ขึ้นไป ระบบจะถือว่าแอป เปิดอยู่เสมอ จอแสดงผลจะหรี่ลง แต่แอปพลิเคชันจะยังคง ทำงานและมองเห็นได้ (การอัปเดตอาจเกิดขึ้นไม่บ่อยนัก เช่น ทุกๆ 1 นาที) สถานะนี้แสดงด้วยโหนด "AOD ทั่วโลก" ในโฟลว์ชาร์ตต่อไปนี้

ปรับแต่งลักษณะการทำงานสำหรับสถานะแอมเบียนท์

ไม่ว่าลักษณะการทำงานเริ่มต้นของระบบจะเป็นอย่างไร คุณก็สามารถปรับแต่งลักษณะหรือลักษณะการทำงานของแอปขณะอยู่ในสถานะแอมเบียนท์ได้ใน Wear OS ทุกเวอร์ชันโดย ใช้ LocalAmbientModeManager เพื่อสังเกตการเปลี่ยนสถานะใน Jetpack Compose บน Wear OS สถานะนี้แสดงด้วยโหนด "โหมด Ambiactive" ในโฟลว์ชาร์ตต่อไปนี้

สังเกตสถานะแอมเบียนท์

หากต้องการตอบโต้กับเหตุการณ์โหมดแอมเบียนท์ใน Jetpack Compose บน Wear OS ให้ทำดังนี้

  1. ใช้ LocalAmbientModeManager เพื่อสังเกตการเปลี่ยนแปลงสถานะโหมดแอมเบียนท์ของระบบ และปรับ Composable ตามนั้น กำหนดค่าคอมโพเนนต์ให้ปรับเป็นโหมดประหยัดพลังงานในสถานะ AmbientMode.Ambient และคืนค่าให้แสดงผลแบบอินเทอร์แอกทีฟเต็มรูปแบบในสถานะ AmbientMode.Interactive

    // In a production application, the AmbientModeManager should be instantiated and provided at
    // the highest level of the Compose hierarchy (typically in the host Activity's setContent
    // block) using a CompositionLocalProvider. This ensures proper lifecycle management and
    // broad accessibility.
    
    // For this self-contained demo, AmbientModeManager is created and provided locally:
    val activityAmbientModeManager = rememberAmbientModeManager()
    CompositionLocalProvider(LocalAmbientModeManager provides activityAmbientModeManager) {
        val ambientModeManager = LocalAmbientModeManager.current
        val ambientMode = ambientModeManager?.currentAmbientMode
    
        if (ambientModeManager != null) {
            ambientModeManager.AmbientTickEffect {
                // While device is in ambient mode, update properties every minute or so
                // ...
            }
        }
    
        // ...
            val ambientModeName =
                when (ambientMode) {
                    is AmbientMode.Interactive -> "Interactive"
                    is AmbientMode.Ambient -> "Ambient"
                    else -> "Unknown"
                }
    
            Text(text = "$ambientModeName Mode")
            // ...
    }

โฟลว์ชาร์ตลักษณะการทำงานของ Ambient

โฟลว์ชาร์ตต่อไปนี้แสดงวิธีที่ระบบกำหนดลักษณะการทำงานในโหมดแอมเบียนท์ โดยอิงตามเวอร์ชัน Wear OS ของอุปกรณ์ targetSdkVersion ของแอป และดูว่าแอปสังเกตการเปลี่ยนสถานะแอมเบียนท์หรือไม่ (โดยใช้ LocalAmbientModeManager หรือ AmbientLifecycleCallback)

โฟลว์ชาร์ตแสดงตรรกะการตัดสินใจสำหรับโหมดแอมเบียนท์ของ Wear OS โดยจะแสดงให้เห็นว่าเวอร์ชันระบบปฏิบัติการของอุปกรณ์และการกำหนดค่าของแอปจะกำหนดผลลัพธ์ 1 ใน 3 อย่าง ได้แก่ ภาพซ้อนทับเบลอ, AOD ทั่วโลก หรือโหมด Ambiactive ที่แอปจัดการ
รูปที่ 1: โฟลว์ชาร์ตที่แสดงตรรกะการตัดสินใจสำหรับโหมดแอมเบียนท์ของ Wear OS

ควบคุมระยะเวลาเปิดหน้าจอ

ส่วนต่อไปนี้อธิบายวิธีจัดการระยะเวลาที่แอปจะแสดงบนหน้าจอ

ป้องกันไม่ให้กลับไปที่หน้าปัดด้วยกิจกรรมที่กำลังดำเนินอยู่หรือการอัปเดตสด

หลังจากผ่านไประยะหนึ่งในสถานะรอบข้าง (หมดเวลา #2) โดยปกติแล้วระบบจะ กลับไปที่หน้าปัด ผู้ใช้สามารถกำหนดค่าระยะหมดเวลาได้ ในการตั้งค่าระบบ ในกรณีการใช้งานบางอย่าง เช่น ผู้ใช้ติดตามการออกกำลังกาย แอปอาจต้องแสดงต่อไปนานขึ้น

ใน Wear OS 5 ขึ้นไป คุณป้องกันปัญหานี้ได้โดยการใช้กิจกรรมที่กำลังดำเนินอยู่ หากแอปแสดงข้อมูลเกี่ยวกับงานของผู้ใช้ที่กำลังดำเนินการอยู่ เช่น เซสชันการออกกำลังกาย คุณสามารถใช้ Ongoing Activity API เพื่อให้แอปแสดงอยู่จนกว่างานจะสิ้นสุด หากผู้ใช้กลับไปที่หน้าปัดด้วยตนเอง สัญญาณบอกสถานะกิจกรรมต่อเนื่องจะช่วยให้ผู้ใช้กลับไปที่แอปได้ด้วยการแตะเพียงครั้งเดียว

หรือใน Wear OS 7 ขึ้นไป คุณสามารถใช้การอัปเดตแบบเรียลไทม์แทน กิจกรรมที่กำลังดำเนินอยู่ได้ เพื่อความเข้ากันได้แบบย้อนหลัง เราจะยังคงรองรับ กิจกรรมที่กำลังดำเนินอยู่บนอุปกรณ์ที่ใช้ Wear OS 6 หรือต่ำกว่า

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

val activityIntent =
    Intent(this, AlwaysOnActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
    }

val pendingIntent =
    PendingIntent.getActivity(
        this,
        0,
        activityIntent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
    )

val notificationBuilder =
    NotificationCompat.Builder(this, CHANNEL_ID)
        // ...
        // ...
        .setOngoing(true)

// ...

val ongoingActivity =
    OngoingActivity.Builder(applicationContext, NOTIFICATION_ID, notificationBuilder)
        // ...
        // ...
        .setTouchIntent(pendingIntent)
        .build()

ongoingActivity.apply(applicationContext)

val notification = notificationBuilder.build()

เปิดหน้าจอไว้และป้องกันสถานะแอมเบียนท์

ในบางกรณีที่พบได้น้อย คุณอาจต้องป้องกันไม่ให้อุปกรณ์เข้าสู่สถานะแอมเบียนท์โดยสมบูรณ์ กล่าวคือ เพื่อหลีกเลี่ยงการหมดเวลา #1 โดยใช้ตัวแก้ไข keepScreenOn ซึ่งจะทำหน้าที่เป็น Wake Lock เพื่อให้อุปกรณ์อยู่ในสถานะโต้ตอบ โปรดใช้ด้วยความระมัดระวังอย่างยิ่งเนื่องจากจะ ส่งผลต่อระยะเวลาการใช้งานแบตเตอรี่อย่างมาก

คำแนะนำสำหรับโหมดแอมเบียนท์

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

  • ลดความรกสายตาและประหยัดพลังงาน UI ที่สะอาดและเรียบง่ายจะส่งสัญญาณ ให้ผู้ใช้ทราบว่าแอปอยู่ในสถานะใช้พลังงานต่ำและประหยัด แบตเตอรี่ได้อย่างมากด้วยการจำกัดพิกเซลสว่าง
    • ให้หน้าจอเป็นสีดำอย่างน้อย 85% (อัตราส่วนพิกเซลที่เปิดอยู่ 15% หรือน้อยกว่า)
    • แสดงเฉพาะข้อมูลที่สำคัญที่สุด และย้ายรายละเอียดรองไปยัง จอแสดงผลแบบอินเทอร์แอกทีฟ
    • ใช้เส้นขอบสำหรับไอคอนหรือปุ่มขนาดใหญ่แทนการเติมสีทึบ ปรับคอนทราสต์ของเส้นขอบเพื่อให้องค์ประกอบยังคงอ่านได้โดยไม่เสี่ยงต่อ การกะพริบหรือการเบิร์นอินของหน้าจอ
    • หลีกเลี่ยงการใช้สีทึบเป็นบล็อกขนาดใหญ่และแบรนด์หรือ ภาพพื้นหลังที่ใช้งานไม่ได้
  • จัดการข้อมูลแบบไดนามิกและภาพเคลื่อนไหวที่ล้าสมัย
    • ระบบจะเรียกใช้AmbientTickEffectการเรียกกลับเป็นระยะๆ เท่านั้น โดยปกติจะเรียกใช้ 1 ครั้งต่อนาทีเพื่อประหยัดพลังงาน ข้อจำกัดนี้ทำให้ข้อมูลที่มีการเปลี่ยนแปลงบ่อยครั้ง เช่น นาฬิกาจับเวลา อัตราการเต้นของหัวใจ หรือระยะทางการออกกำลังกาย จะล้าสมัยระหว่างการอัปเดต หากต้องการหลีกเลี่ยงการแสดงข้อมูลที่ไม่ถูกต้องและทำให้เข้าใจผิด ให้แทนที่ค่าแบบเรียลไทม์เหล่านี้ด้วยเนื้อหาตัวยึดตำแหน่งแบบคงที่ เช่น --
    • นำตัวบ่งชี้ความคืบหน้าที่ต้องมีการอัปเดตบ่อยครั้งออกเพื่อให้มีประโยชน์ สำหรับบริบทหรือสถานการณ์ปัจจุบันของผู้ใช้ เช่น วงแหวนความคืบหน้าสำหรับ เซสชันสื่อแบบสั้น คุณสามารถเก็บตัวบ่งชี้ความคืบหน้าไว้ในกรณีที่การอัปเดตทุกนาทีหนึ่งครั้งยังคงมีประโยชน์ เช่น สำหรับตัวจับเวลาที่ยาวนาน
    • หยุดชั่วคราวหรือนำภาพเคลื่อนไหวทั้งหมดในโหมดแอมเบียนท์ออก รวมถึงตัวบ่งชี้ความคืบหน้าแบบเคลื่อนไหวและข้อความมาร์คีที่เลื่อน
  • คงเลย์เอาต์ให้สอดคล้องกัน
    • จัดวางองค์ประกอบในตำแหน่งเดียวกันในโหมดอินเทอร์แอกทีฟและแอมเบียนท์ เพื่อสร้างการเปลี่ยนผ่านที่ราบรื่น องค์ประกอบ UI แบบอินเทอร์แอกทีฟ เช่น ปุ่ม จะยังคงมองเห็นได้เนื่องจากอุปกรณ์ Wear OS สมัยใหม่รองรับการป้อนข้อมูลด้วยการสัมผัส ในระหว่างเซสชันโดยรอบ
    • แสดงเวลาปัจจุบันเสมอ ใน Jetpack Compose สำหรับ Wear OS, TimeText (รวมอยู่ใน ScreenScaffold โดยค่าเริ่มต้น) จะ รับรู้ถึงโหมดแอมเบียนท์และอัปเดตโดยอัตโนมัติทุกๆ 1 นาทีในโหมดแอมเบียนท์
  • คำนึงถึงบริบท
    • หากผู้ใช้อยู่ในหน้าจอการตั้งค่าหรือการกำหนดค่าเมื่ออุปกรณ์เข้าสู่โหมดแอมเบียนท์ ให้พิจารณาแสดงหน้าจอที่เกี่ยวข้องมากขึ้นจากแอปแทนมุมมองการตั้งค่า
  • จัดการข้อกำหนดเฉพาะของอุปกรณ์
    • เมื่อ currentAmbientMode แสดงผล AmbientMode.Ambient ให้ทำดังนี้
      • หาก isLowBitAmbientSupported เป็น true แสดงว่าจอแสดงผลรองรับบิตต่อสีในโหมดแอมเบียนท์น้อยกว่า ปิดใช้การป้องกันรอยหยัก (เช่น ในการดำเนินการวาดภาพ Canvas หรือ Paint ที่กำหนดเอง) และหลีกเลี่ยงการไล่ระดับสี เมื่อเป็นไปได้
      • หาก isBurnInProtectionRequired เป็น true ระบบจะเลื่อนเนื้อหาที่แสดงเป็นระยะๆ เพื่อป้องกันการเกิดภาพเบิร์นอิน อย่าเลื่อนองค์ประกอบ UI ในแอปด้วยตนเอง แต่ให้หลีกเลี่ยงพื้นที่สีขาวทึบและวางองค์ประกอบ UI ที่สำคัญให้ห่างจากขอบหน้าจออย่างน้อย 10 พิกเซล เพื่อไม่ให้องค์ประกอบเหล่านั้นถูกตัดออกเมื่อระบบเลื่อนการแสดงผล (เลย์เอาต์ Compose มาตรฐานของ Wear เช่น ScreenScaffold มีการเว้นขอบที่เพียงพออยู่แล้ว)

การแก้ไขข้อบกพร่องและการทดสอบ

คำสั่ง adb เหล่านี้อาจมีประโยชน์เมื่อพัฒนาหรือทดสอบลักษณะการทำงานของแอป เมื่ออุปกรณ์อยู่ในโหมดแอมเบียนท์

# put device in ambient mode if the always on display is enabled in settings
# (and not disabled by other settings, such as theatre mode)
$ adb shell input keyevent KEYCODE_SLEEP

# put device in interactive mode
$ adb shell input keyevent KEYCODE_WAKEUP

ตัวอย่าง: แอปออกกำลังกาย

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

นักพัฒนาแอปควรทำดังนี้เพื่อให้บรรลุเป้าหมายนี้

  1. ใช้ LocalAmbientModeManager เพื่อจัดการการเปลี่ยนแปลง UI ระหว่างสถานะโต้ตอบและแอมเบียนท์ เช่น การหรี่หน้าจอและนำข้อมูลที่ไม่จำเป็นออก
  2. สร้างเลย์เอาต์ใหม่ที่ใช้พลังงานต่ำสำหรับสถานะแอมเบียนท์ตาม แนวทางปฏิบัติแนะนำ
  3. ใช้ Ongoing Activity API (หรือการอัปเดตแบบเรียลไทม์ใน Wear OS 7 ขึ้นไป) ตลอดระยะเวลาการออกกำลังกายเพื่อป้องกันไม่ให้ระบบกลับไปที่หน้าปัดนาฬิกา

ดูการใช้งานที่สมบูรณ์ได้ในตัวอย่างการออกกำลังกายที่อิงตาม Compose ใน GitHub ตัวอย่างนี้ยังแสดงการใช้ LocalAmbientModeManagerที่ประกอบได้เพื่อลดความซับซ้อนในการจัดการโหมดแอมเบียนท์ ใน Compose