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

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

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

หัวข้อสำคัญ

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

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

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

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

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

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

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

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

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

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

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

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

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

วิธีตอบสนองต่อเหตุการณ์โหมดแอมเบียนท์ใน Jetpack Compose บน Wear OS

  1. ใช้ LocalAmbientModeManager เพื่อสังเกตการเปลี่ยนแปลงสถานะโหมดแอมเบียนท์ ของระบบ และปรับคอมโพสได้ตามความเหมาะสม กำหนดค่าคอมโพเนนต์ให้ปรับเป็นโหมดพลังงานต่ำในสถานะ 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")
            // ...
    }

สำหรับนักพัฒนาแอปที่ใช้ Jetpack Compose ให้ใช้ LocalAmbientModeManager ซึ่ง จะช่วยลดความซับซ้อนในการใช้งานรูปแบบนี้

โฟลว์ชาร์ตลักษณะการทำงานของแอมเบียนท์

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

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

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

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

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

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

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

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

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

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%
    • แสดงเฉพาะข้อมูลที่สำคัญที่สุด โดยย้ายรายละเอียดรองไปไว้ในจอแสดงผลแบบโต้ตอบ
    • ใช้เส้นขอบสำหรับไอคอนหรือปุ่มขนาดใหญ่แทนการเติมสีทึบ
    • หลีกเลี่ยงบล็อกสีทึบขนาดใหญ่ รวมถึงการสร้างแบรนด์หรือรูปภาพพื้นหลังที่ไม่มีฟังก์ชัน
  • จัดการข้อมูลแบบไดนามิกที่ล้าสมัย
    • ระบบจะเรียกใช้ AmbientTickEffect Callback เป็นระยะๆ เท่านั้น ซึ่งโดยปกติจะเรียกใช้ทุกๆ 1 นาที เพื่อประหยัดพลังงาน ข้อจำกัดนี้ทำให้ข้อมูลที่เปลี่ยนแปลงบ่อยๆ เช่น นาฬิกาจับเวลา อัตราการเต้นของหัวใจ หรือระยะทางการออกกำลังกาย ล้าสมัยระหว่างการอัปเดต โปรดแทนที่ค่าแบบสดเหล่านี้ด้วยเนื้อหาตัวยึดตำแหน่งแบบคงที่ เช่น -- เพื่อหลีกเลี่ยงการแสดงข้อมูลที่ทำให้เข้าใจผิดและไม่ถูกต้อง
  • รักษาเลย์เอาต์ที่สอดคล้องกัน
    • เก็บองค์ประกอบไว้ในตำแหน่งเดียวกันในโหมด โต้ตอบและ แอมเบียนท์เพื่อสร้างการเปลี่ยนผ่านที่ราบรื่น
    • แสดงเวลาเสมอ
  • คำนึงถึงบริบท
    • หากผู้ใช้อยู่ในหน้าจอการตั้งค่าหรือการกำหนดค่าเมื่ออุปกรณ์เข้าสู่โหมดแอมเบียนท์ ให้พิจารณาแสดงหน้าจอที่เกี่ยวข้องมากขึ้นจากแอปแทนมุมมองการตั้งค่า
  • จัดการข้อกำหนดเฉพาะของอุปกรณ์
    • ในออบเจ็กต์ AmbientMode ที่แสดงผลจาก currentAmbientMode ให้ทำดังนี้
      • หาก isLowBitAmbientSupported เป็น true ให้ปิดใช้การป้องกันการเกิดรอยหยักหากเป็นไปได้
      • หาก isBurnInProtectionRequired เป็น true ให้เลื่อนองค์ประกอบ UI เล็กน้อยเป็นระยะๆ และหลีกเลี่ยงพื้นที่สีขาวทึบเพื่อป้องกันการเกิดภาพค้างบนหน้าจอ

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

คำสั่ง 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