# Live Updates บน Android

Pushwoosh รองรับ Android Live Updates ผ่านโมดูล `pushwoosh-liveupdates` (SDK 6.9.0 และใหม่กว่า) Live Update คือการแจ้งเตือนแบบต่อเนื่องในรูปแบบความคืบหน้า ซึ่งระบบจะโปรโมตบนหน้าจอล็อก ในลิ้นชักการแจ้งเตือน และเป็นชิปสถานะในแถบสถานะ เพื่อให้ผู้ใช้สามารถติดตามกิจกรรมได้โดยไม่ต้องเปิดแอปของคุณ

วงจรชีวิตทั้งหมดถูกขับเคลื่อนจากเซิร์ฟเวอร์: แบ็กเอนด์ของคุณจะส่ง push เมื่อกิจกรรมเริ่มต้น, ส่ง push เพิ่มเติมเมื่อมีความคืบหน้า และส่ง push สุดท้ายเมื่อกิจกรรมสิ้นสุด SDK จะแสดงผลแต่ละรายการโดยอัตโนมัติ

## Live Updates คืออะไร

Live Updates ถูกนำมาใช้ใน Android 16 (API 36) เพื่อเป็นวิธีการแสดงกิจกรรมที่ผู้ใช้เป็นผู้ริเริ่มและมีความสำคัญต่อเวลาตั้งแต่ต้นจนจบ โดยสร้างขึ้นบนพื้นฐานของการแจ้งเตือนที่เน้นความคืบหน้าของแพลตฟอร์มและ `Notification.ProgressStyle` API ในเชิงแนวคิดแล้ว นี่คือส่วนของ Android ที่เทียบเท่ากับ Live Activities ของ iOS

หน้านี้ครอบคลุมเฉพาะการผสานรวมกับ Pushwoosh เท่านั้น สำหรับพฤติกรรมของแพลตฟอร์ม กฎการโปรโมต และคำแนะนำด้านการออกแบบ โปรดดูเอกสารอย่างเป็นทางการของ Android:

- [การแจ้งเตือนที่เน้นความคืบหน้า](https://developer.android.com/about/versions/16/features/progress-centric-notifications)
- [สร้างการแจ้งเตือน live update (Views)](https://developer.android.com/develop/ui/views/notifications/live-update)
- [สร้างการแจ้งเตือน live update (Compose)](https://developer.android.com/develop/ui/compose/notifications/live-update)

## ควรใช้ Live Updates เมื่อใด

Live Updates มีไว้สำหรับกิจกรรมที่กำลังดำเนินอยู่ ผู้ใช้เป็นผู้ริเริ่ม และมีความสำคัญต่อเวลา — สิ่งที่มีจุดเริ่มต้นและจุดสิ้นสุดที่ชัดเจนซึ่งผู้ใช้ให้ความสนใจอย่างจริงจังในขณะนั้น สถานการณ์ทั่วไปสำหรับลูกค้า Pushwoosh:

- **การจัดส่งอาหาร** — รับออเดอร์แล้ว, กำลังเตรียม, กำลังจัดส่ง, กำลังจะถึง
- **การเรียกรถและแท็กซี่** — ได้คนขับแล้ว, กำลังเดินทาง, กำลังจะถึง, การเดินทางกำลังดำเนินอยู่
- **การติดตามคำสั่งซื้อและการจัดส่ง** — สถานะสดของคำสั่งซื้อที่กำลังอยู่ในระหว่างการขนส่ง
- **กีฬาสดและสื่อ** — คะแนนและเวลาการแข่งขันขณะที่เกมดำเนินไป
- **ฟิตเนส** — การออกกำลังกายหรือการวิ่งที่กำลังดำเนินอยู่พร้อมเวลาที่ผ่านไปและความคืบหน้า
- **ฟินเทค** — ขั้นตอนการทำธุรกรรมหรือการยืนยันที่กำลังเคลื่อนผ่านขั้นตอนต่างๆ

เนื่องจากวงจรชีวิตถูกขับเคลื่อนโดยเหตุการณ์จริงที่แบ็กเอนด์ของคุณรู้อยู่แล้ว (สถานะคำสั่งซื้อเปลี่ยนแปลง, พนักงานจัดส่งเคลื่อนที่) Live Update จึงมักจะเป็นการเรียก API เพียงครั้งเดียวที่เชื่อมต่อกับโฟลว์เหตุการณ์ที่มีอยู่ของคุณ — ไม่ใช่สิ่งที่คนส่งด้วยตนเอง

<Aside type="caution">
อย่าใช้ Live Updates สำหรับโปรโมชั่น ข้อความแชท หรือข้อมูลทั่วไป และอย่าโพสต์ Live Update ที่ผู้ใช้ปิดไปแล้วซ้ำอีกครั้ง Android อาจเพิกถอนความสามารถของแอปในการโพสต์การแจ้งเตือนที่ได้รับการโปรโมต โปรดปฏิบัติตาม [คำแนะนำของ Android เกี่ยวกับการใช้งานที่เหมาะสม](https://developer.android.com/develop/ui/views/notifications/live-update#best-practices)
</Aside>

## ข้อกำหนด

- Android 16 (API 36) หรือใหม่กว่า บนอุปกรณ์รุ่นเก่า โมดูลจะยังคงไม่ทำงาน และการเรียก Live Update API ทุกครั้งจะเป็น no-op ที่ปลอดภัย
- Pushwoosh Android SDK 6.9.0 หรือใหม่กว่า

## เพิ่มโมดูล pushwoosh-liveupdates

เพิ่ม dependency ไปยัง **app/build.gradle** ของคุณ:

```groovy
dependencies {
    implementation 'com.pushwoosh:pushwoosh-liveupdates:<latest-version>'
}
```

แทนที่ `<latest-version>` ด้วยเวอร์ชันปัจจุบันจาก [Maven Central](https://mvnrepository.com/artifact/com.pushwoosh/pushwoosh-liveupdates)

โมดูลจะถูกค้นพบโดยอัตโนมัติเมื่อเริ่มต้น มันจะประกาศสิทธิ์ `POST_PROMOTED_NOTIFICATIONS` ที่จำเป็น, ลงทะเบียนช่องทางการแจ้งเตือนของตัวเอง และดักจับ push ของ Live Update ก่อนเส้นทางการแจ้งเตือนเริ่มต้น ไม่จำเป็นต้องมีโค้ดเริ่มต้นเพิ่มเติม — SDK จะตั้งค่าแฟล็ก ongoing และ promoted, ดาวน์โหลดไอคอนขนาดใหญ่, แมปปุ่มการกระทำ และโพสต์การแจ้งเตือนให้คุณ

## ส่ง Live Update

คุณส่ง Live Updates ผ่าน [Messaging API v2](/th/developer/api-reference/messaging-api-v2/) โดยการเพิ่มอ็อบเจกต์ `live_update` ไปยังบล็อกเนื้อหา `android` ใช้คำขอแบบ `transactional` — Live Update จะกำหนดเป้าหมายไปยังผู้ใช้เฉพาะที่กิจกรรมนั้นติดตามอยู่ ฟิลด์ `schedule` เป็นสิ่งจำเป็น; `{ "after": "0s" }` จะส่งทันที วงจรชีวิตมีการดำเนินการสามอย่าง ตั้งค่าใน `live_update.op`:

- `OPERATION_START` — push แรกสำหรับกิจกรรม โพสต์การแจ้งเตือนที่กำลังดำเนินอยู่
- `OPERATION_UPDATE` — push ต่อมาสำหรับกิจกรรมเดียวกัน รีเฟรชในตำแหน่งเดิมอย่างเงียบๆ
- `OPERATION_END` — push สุดท้าย ปิดการแจ้งเตือน

push ทั้งหมดที่อยู่ในกิจกรรมเดียวกันต้องใช้ `live_update.id` เดียวกัน id นั้นจะผูกการอัปเดตเข้าด้วยกันและยังเป็นสิ่งที่คุณใช้เพื่อปิดการอัปเดตจากแอป

แต่ละ push จะอธิบายการแจ้งเตือนอย่างสมบูรณ์ — ไม่มีอะไรถูกส่งต่อมาจาก push ก่อนหน้า ส่งทุกฟิลด์ที่คุณต้องการเก็บไว้ซ้ำ เช่น segments และไอคอนขนาดใหญ่ กับแต่ละ `OPERATION_UPDATE`; ฟิลด์ที่ละไว้จะถูกแสดงผลว่าไม่มีอยู่

### พารามิเตอร์ Live Update

คีย์เหล่านี้จะอยู่ภายในอ็อบเจกต์ `live_update` ของบล็อกเนื้อหา `android` หัวข้อ, เนื้อหา และไอคอนขนาดใหญ่จะใช้ฟิลด์ push มาตรฐานของ Android (`title`, `body`, `custom_icon`) ซึ่งส่งมาพร้อมกับ `live_update`

| พารามิเตอร์ | ประเภท | คำอธิบาย |
|---|---|---|
| `op` | string | การดำเนินการของวงจรชีวิต: `OPERATION_START`, `OPERATION_UPDATE`, หรือ `OPERATION_END` จำเป็น |
| `id` | string | id กิจกรรมที่คงที่ซึ่งใช้ร่วมกันโดย push ทั้งหมดของ Live Update หนึ่งรายการ จำเป็น |
| `progress` | int | ค่าความคืบหน้า วัดจากความยาวรวมของ segment |
| `progress_indeterminate` | bool | แสดงแอนิเมชันที่ไม่แน่นอนแทนค่าที่ระบุ |
| `progress_bar` | bool | แสดงแถบความคืบหน้าเลยหรือไม่ ค่าเริ่มต้นคือ `true` |
| `segments` | array | segment ความคืบหน้าที่เรียงลำดับ แต่ละอันคือ `{ "color": "#RRGGBB", "length": N }` |
| `extras` | object | ข้อมูลใดๆ ที่ส่งผ่านไปยัง style provider แบบกำหนดเอง |
| `when` | int64 | จุดยึดเวลาของส่วนหัว ในหน่วยมิลลิวินาทีของ epoch |
| `chronometer` | bool | แสดงเวลาของส่วนหัวเป็นตัวจับเวลาที่กำลังทำงาน |
| `chronometer_count_down` | bool | ตัวจับเวลาที่กำลังทำงานจะนับถอยหลังแทนที่จะนับขึ้น |
| `show_when` | bool | แสดงคอลัมน์เวลาของส่วนหัวเลยหรือไม่ ค่าเริ่มต้นคือ `true` |

<Aside>
ค่า `op` ต้องเป็นหนึ่งในชื่อ enum ที่แน่นอน `OPERATION_START`, `OPERATION_UPDATE`, หรือ `OPERATION_END` — รูปแบบย่อจะถูกละเว้น แต่ละฟิลด์จะใช้ประเภท JSON ดั้งเดิมของมัน: `segments` เป็นอาร์เรย์ JSON และ `extras` เป็นอ็อบเจกต์ JSON ไม่ใช่สตริงที่เข้ารหัส
</Aside>

ฟิลด์เวลาทั้งสี่จะทำงานร่วมกันดังนี้: เมื่อตั้งค่า `show_when` เป็น `false` เวลาจะถูกซ่อน; มิฉะนั้น `when` จะเป็นจุดยึด, `chronometer` จะเปลี่ยนเป็นตัวนับสด และ `chronometer_count_down` จะทำให้ตัวนับนั้นทำงานถอยหลัง

### Start push

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "title": "Order #4521",
                "body": "We are preparing your order",
                "custom_icon": "https://example.com/restaurant.png",
                "live_update": {
                  "op": "OPERATION_START",
                  "id": "order_4521",
                  "progress": 1,
                  "segments": [
                    { "color": "#34A853", "length": 3 },
                    { "color": "#FBBC05", "length": 4 },
                    { "color": "#4285F4", "length": 3 }
                  ],
                  "extras": { "eta": "18:40" }
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

### Update push

ส่ง `OPERATION_UPDATE` ที่มี `id` เดียวกันเมื่อใดก็ตามที่กิจกรรมมีความคืบหน้า ส่ง segments และไอคอนซ้ำ — การอัปเดตที่ละเว้นสิ่งเหล่านี้จะแสดงผลโดยไม่มีสิ่งเหล่านั้น

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "title": "Order #4521",
                "body": "Your courier is on the way",
                "custom_icon": "https://example.com/restaurant.png",
                "live_update": {
                  "op": "OPERATION_UPDATE",
                  "id": "order_4521",
                  "progress": 7,
                  "segments": [
                    { "color": "#34A853", "length": 3 },
                    { "color": "#FBBC05", "length": 4 },
                    { "color": "#4285F4", "length": 3 }
                  ]
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

### End push

push สุดท้ายต้องการเพียง operation และ id เท่านั้น

```bash
curl -X POST https://api.pushwoosh.com/messaging/v2/notify \
  -H "Authorization: Token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "transactional": {
      "application": "XXXXX-XXXXX",
      "platforms": ["ANDROID"],
      "users": { "list": ["customer-42"] },
      "payload": {
        "content": {
          "localized_content": {
            "default": {
              "android": {
                "live_update": {
                  "op": "OPERATION_END",
                  "id": "order_4521"
                }
              }
            }
          }
        }
      },
      "schedule": { "after": "0s" },
      "message_type": "MESSAGE_TYPE_TRANSACTIONAL"
    }
  }'
```

## ปรับแต่งรูปลักษณ์

SDK มาพร้อมกับสไตล์ความคืบหน้าเริ่มต้นที่สร้างจาก `progress`, `progress_indeterminate` และ `segments` หากต้องการควบคุมแถบความคืบหน้าอย่างเต็มที่ ให้ implement `LiveUpdateProgressStyleProvider` และสร้าง `Notification.ProgressStyle` ด้วยตัวคุณเอง

provider เป็นจุดปรับแต่งเพียงจุดเดียว SDK ยังคงเป็นเจ้าของในการตั้งค่าช่องทาง, แฟล็ก ongoing และ promoted, ไอคอนขนาดใหญ่, ปุ่มการกระทำ และเวลาของส่วนหัว — provider แบบกำหนดเองสามารถกำหนดรูปแบบแถบความคืบหน้าได้เท่านั้น ดังนั้นจึงไม่สามารถทำลายคุณสมบัติการโปรโมตได้ มันต้องเป็น stateless: สร้างสไตล์ที่ส่งคืนจาก `LiveUpdateState` ที่ให้มาเท่านั้น หากเกิดข้อผิดพลาด SDK จะกลับไปใช้สไตล์เริ่มต้นและการแจ้งเตือนจะยังคงถูกโพสต์

<Tabs>
<TabItem label="Java">
```java
import android.app.Notification;
import androidx.annotation.NonNull;
import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider;
import com.pushwoosh.liveupdates.LiveUpdateSegment;
import com.pushwoosh.liveupdates.LiveUpdateState;
import java.util.List;

public class OrderStyleProvider implements LiveUpdateProgressStyleProvider {
    @NonNull
    @Override
    public Notification.ProgressStyle createStyle(@NonNull LiveUpdateState state) {
        Notification.ProgressStyle style = new Notification.ProgressStyle();
        if (state.getProgress() != null) {
            style.setProgress(state.getProgress());
        }
        style.setProgressIndeterminate(state.isProgressIndeterminate());

        List<LiveUpdateSegment> segments = state.getSegments();
        int boundary = 0;
        for (int i = 0; i < segments.size(); i++) {
            LiveUpdateSegment seg = segments.get(i);
            style.addProgressSegment(
                new Notification.ProgressStyle.Segment(seg.getLength()).setColor(seg.getColor()));
            boundary += seg.getLength();
            if (i < segments.size() - 1) {
                style.addProgressPoint(new Notification.ProgressStyle.Point(boundary));
            }
        }
        return style;
    }
}
```
</TabItem>
<TabItem label="Kotlin">
```kotlin
import android.app.Notification
import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider
import com.pushwoosh.liveupdates.LiveUpdateState

class OrderStyleProvider : LiveUpdateProgressStyleProvider {
    override fun createStyle(state: LiveUpdateState): Notification.ProgressStyle {
        val style = Notification.ProgressStyle()
        state.progress?.let { style.setProgress(it) }
        style.setProgressIndeterminate(state.isProgressIndeterminate)

        val segments = state.segments
        var boundary = 0
        segments.forEachIndexed { i, seg ->
            style.addProgressSegment(
                Notification.ProgressStyle.Segment(seg.length).setColor(seg.color))
            boundary += seg.length
            if (i < segments.size - 1) {
                style.addProgressPoint(Notification.ProgressStyle.Point(boundary))
            }
        }
        return style
    }
}
```
</TabItem>
</Tabs>

ลงทะเบียน provider ด้วยแท็ก `<meta-data>` ใน **AndroidManifest.xml** คลาสต้องมี public constructor ที่ไม่มีอาร์กิวเมนต์

```xml
<meta-data
    android:name="com.pushwoosh.LIVE_UPDATE_STYLE_PROVIDER"
    android:value="com.example.OrderStyleProvider" />
```

ใช้ `LiveUpdateState.getExtras()` เพื่ออ่าน JSON ที่คุณส่งใน `live_update.extras` และปรับสไตล์ให้เข้ากับข้อมูลธุรกิจของคุณเอง

## จัดการ Live Updates จากแอปของคุณ

เซิร์ฟเวอร์จะขับเคลื่อนทุก `OPERATION_START`, `OPERATION_UPDATE` และ `OPERATION_END` ดังนั้นจึงไม่มี API ฝั่งแอปเพื่อโพสต์หรือรีเฟรช Live Update facade `PushwooshLiveUpdates` ครอบคลุมเฉพาะสิ่งที่เซิร์ฟเวอร์ไม่สามารถทำได้ — การปิดการอัปเดตในเครื่องและการตรวจสอบว่ารายการใดกำลังแสดงอยู่บนหน้าจอ

```java
import com.pushwoosh.liveupdates.PushwooshLiveUpdates;

// ปิด Live Update ที่ระบุเมื่อผู้ใช้เสร็จสิ้นกิจกรรมในแอป
// โดยไม่ต้องรอ push "end" สุดท้ายจากเซิร์ฟเวอร์
PushwooshLiveUpdates.endLiveUpdate("order_4521");

// แสดงรายการ id กิจกรรมที่กำลังแสดงโดยแอปนี้
List<String> active = PushwooshLiveUpdates.getActiveIds();

// ล้างทุกอย่างที่แอปนี้กำลังแสดงอยู่ เช่น เมื่อออกจากระบบ
PushwooshLiveUpdates.endAllLiveUpdates();
```

ทุกเมธอดปลอดภัยที่จะเรียกจากเธรดใดก็ได้และเป็น no-op บนอุปกรณ์ที่ต่ำกว่า Android 16

## ลิงก์ที่เกี่ยวข้อง

- [ภาพรวม Pushwoosh Android SDK](/th/developer/pushwoosh-sdk/android-sdk/)
- [เอกสารอ้างอิง Pushwoosh Android SDK API](https://pushwoosh.github.io/pushwoosh-android-sdk/)
- [Messaging API](/th/developer/api-reference/messaging-api-v2/)
- [การแจ้งเตือนที่เน้นความคืบหน้าของ Android](https://developer.android.com/about/versions/16/features/progress-centric-notifications)