iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Build on Google AI

給藥袋裝一張嘴:30 天用 Android 與 Google VLM 實作高齡語音用藥助手系列 第 16 篇

Day 16|長輩看得到也聽得懂!高齡無障礙 UI/UX 與語音互動優化

  • 分享至 

  • xImage
  •  

✏️【本日實作紀錄:高齡無障礙介面與語音互動體驗優化】

進入專案開發下半場(Day 16~30),我們的重點將從「基礎核心功能串接」轉向「使用者體驗(UX)、無障礙設計(Accessibility)與系統穩定度提升」。

對於高齡長者來說,傳統智慧型手機介面常因字體過小、顏色對比不足或按鈕層級複雜而造成使用障礙。今天我們將針對 PrescriptionVLM 的 LINE 聊天室與 Web 儀表板,進行專為銀髮族打造的無障礙介面(Silver Accessibility UI/UX)升級。完成項目包含:

  • LINE Flex Message 高對比大字體視覺卡片 — 替代純文字推播,改用大字級、高對比顏色與醒目 icon 的視覺化卡片呈現藥單摘要。
  • Web 儀表板一鍵切換「長輩關懷大字模式」 — 透過 CSS 與 JavaScript 實作動態縮放字級與高對比視覺模式。
  • 語音播放速率動態調整功能 — 考量長者聽力與理解節奏,提供語音播報調速(如 0.8x 慢速播報)選項。

一、銀髮無障礙設計(Silver UI/UX)的核心思維

根據高齡無障礙設計規範(WCAG 2.1 AAA 級標準),針對高齡使用者設計產品時應遵循以下原則:

  • 高視覺可讀性(Perceivable):內文字體至少 18px 以上,重要提示(如用藥時間、警語)需達 24px 以上,且背景與文字的色彩對比度需達 $4.5:1$ 以上。
  • 直覺操作與反饋(Operable):避免複雜的滑動或多層級選單,點擊區域(Touch Target)至少需達 $48 \times 48\text{ dp}$。
  • 多模態感知支援(Multi-modal):結合視覺(大字卡片)、聽覺(語音朗讀)與提示音,確保視力退化的長輩也能順暢理解。

二、升級 LINE Bot 關懷卡片:Flex Message 大字版(app.py)

原本的推播使用的是純文字訊息,長輩閱讀較為吃力。今天我們改用 LINE Flex Message,將解析成果包裝成專為長輩設計的高對比大字卡片。

1. 匯入 LINE Flex Message 模組

請開啟 app.py,確認頂部 LINE SDK 匯入區塊包含 Flex 相關物件:

from linebot.v3.messaging import (
    Configuration,
    ApiClient,
    MessagingApi,
    PushMessageRequest,
    ReplyMessageRequest,
    TextMessage,
    FlexMessage,
    FlexContainer
)

2. 新增 Flex Message 卡片建構函式

在 app.py 中新增 create_elderly_flex_card 函式:

def create_elderly_flex_card(summary, medicines_count, safety_warnings, audio_url):
    """打造專為高齡長輩設計的大字體高對比 Flex Message 卡片 JSON"""
    
    warning_contents = []
    if safety_warnings:
        warning_contents = [
            {
                "type": "box",
                "layout": "vertical",
                "backgroundColor": "#FFE2E2",
                "cornerRadius": "md",
                "paddingAll": "md",
                "margin": "md",
                "contents": [
                    {
                        "type": "text",
                        "text": "⚠️ 用藥安全特別提醒:",
                        "weight": "bold",
                        "color": "#CC0000",
                        "size": "md"
                    },
                    {
                        "type": "text",
                        "text": ";".join(safety_warnings),
                        "color": "#990000",
                        "size": "sm",
                        "wrap": True,
                        "margin": "xs"
                    }
                ]
            }
        ]

    card_json = {
        "type": "bubble",
        "header": {
            "type": "box",
            "layout": "vertical",
            "backgroundColor": "#4F46E5",
            "paddingAll": "lg",
            "contents": [
                {
                    "type": "text",
                    "text": "💊 服藥提醒與藥單摘要",
                    "color": "#FFFFFF",
                    "weight": "bold",
                    "size": "xl"
                }
            ]
        },
        "body": {
            "type": "box",
            "layout": "vertical",
            "spacing": "md",
            "contents": [
                {
                    "type": "text",
                    "text": f"本次藥單共包含 {medicines_count} 種藥品",
                    "weight": "bold",
                    "size": "lg",
                    "color": "#1F2937"
                },
                *warning_contents,
                {
                    "type": "text",
                    "text": "📢 播報摘要:",
                    "weight": "bold",
                    "color": "#4B5563",
                    "margin": "top"
                },
                {
                    "type": "text",
                    "text": summary,
                    "wrap": True,
                    "size": "md",
                    "color": "#111827"
                }
            ]
        },
        "footer": {
            "type": "box",
            "layout": "vertical",
            "contents": [
                {
                    "type": "button",
                    "action": {
                        "type": "message",
                        "label": "✅ 我已按時吃藥",
                        "text": "已吃藥"
                    },
                    "style": "primary",
                    "color": "#059669",
                    "height": "sm"
                }
            ]
        }
    }
    return card_json

3. 更新 LINE 推播邏輯

修改 send_line_notification 函式,改為發送 Flex Message:

# 3. 更新 LINE 推播邏輯

# 修改 send_line_notification 函式,帶入預設值 audio_url="" 防止少傳參數導致 500 錯誤:
def send_line_notification(summary, medicines_count, safety_warnings, audio_url=""):
    if not line_bot_api or not line_user_id:
        logger.warning("未設定 LINE Token 或 User ID,跳過推播")
        return

    try:
        flex_card = create_elderly_flex_card(summary, medicines_count, safety_warnings, audio_url)
        flex_message = FlexMessage(
            alt_text="💊 長輩用藥關懷通知",
            contents=FlexContainer.from_dict(flex_card)
        )
        push_message_request = PushMessageRequest(
            to=line_user_id,
            messages=[flex_message]
        )
        line_bot_api.push_message(push_message_request)
        logger.info("LINE 高齡無障礙 Flex 卡片發送成功")
    except Exception as e:
        logger.error(f"LINE Flex 推播發送失敗: {str(e)}")

三、Web 儀表板:一鍵切換「關懷大字模式」與語音調速(templates/dashboard.html)

接著更新 Web 前端介面,在頂部導覽列加入「放大字級 / 切換高對比」選單,並在音訊播放器旁加入調速按鈕。
更新 templates/dashboard.html 的 與 JavaScript 區塊:

<!-- 頂部導覽列:新增無障礙切換按鈕 -->
<header class="bg-indigo-600 text-white shadow-lg py-4 px-6 mb-8">
    <div class="max-w-7xl mx-auto flex justify-between items-center flex-wrap gap-4">
        <h1 class="text-2xl font-bold flex items-center gap-2" id="header-title">
            <span>💊</span> PrescriptionVLM 關懷儀表板
        </h1>
        <div class="flex items-center gap-3">
            <button onclick="toggleElderlyMode()" class="bg-amber-400 hover:bg-amber-500 text-slate-900 font-bold px-4 py-2 rounded-xl shadow transition text-sm flex items-center gap-1">
                🔍 一鍵切換大字長輩模式
            </button>
            <span class="bg-indigo-500 text-xs px-3 py-1 rounded-full">系統運作中 (Online)</span>
        </div>
    </div>
</header>

前端 JavaScript 中新增動態縮放與語音調速控制:

// 切換高齡大字視覺模式
let isElderlyMode = false;
function toggleElderlyMode() {
    isElderlyMode = !isElderlyMode;
    const body = document.body;
    
    if (isElderlyMode) {
        body.classList.add('text-lg');
        document.querySelectorAll('h2').forEach(el => el.classList.add('text-2xl'));
        document.querySelectorAll('p').forEach(el => el.classList.add('text-base', 'font-medium'));
        alert("已啟動【長輩大字模式】:放大字體與提升色彩對比!");
    } else {
        body.classList.remove('text-lg');
        document.querySelectorAll('h2').forEach(el => el.classList.remove('text-2xl'));
        document.querySelectorAll('p').forEach(el => el.classList.remove('text-base', 'font-medium'));
    }
}
// 動態調整音訊播放速率 (例如設定為 0.8 倍速慢速播放)
function setAudioSpeed(audioId, speed) {
    const audio = document.getElementById(audioId);
    if (audio) {
        audio.playbackRate = speed;
    }
}

在動態渲染音訊播放器時,加入倍速切換按鈕:

<div>
    <h4 class="text-sm font-semibold text-slate-500 mb-1">🎧 語音檔播放(支援慢速播報):</h4>
    <div class="flex items-center gap-2">
        <audio id="audio-${item.id}" controls class="w-full h-8">
            <source src="${item.audio_url}" type="audio/mpeg">
        </audio>
        <button onclick="setAudioSpeed('audio-${item.id}', 0.8)" class="bg-slate-200 hover:bg-slate-300 text-xs px-2.5 py-1.5 rounded-lg whitespace-nowrap font-bold">
            🐢 0.8x 慢速
        </button>
        <button onclick="setAudioSpeed('audio-${item.id}', 1.0)" class="bg-slate-200 hover:bg-slate-300 text-xs px-2.5 py-1.5 rounded-lg whitespace-nowrap font-bold">
            ▶️ 1.0x 常速
        </button>
    </div>
</div>

四、測試與成果驗證

1. 重新構建並啟動 Docker 容器

在 Terminal 執行重新編譯:

docker rm -f prescription_service
docker build -t prescription-vlm:v1.0 .
docker run -d -p 5000:5000 --env-file .env --name prescription_service prescription-vlm:v1.0

2. 驗證無障礙功能

  1. LINE 介面測試:使用 API 上傳圖片解析後,檢查 LINE 聊天室收到的訊息是否已升級為高對比藍底標題、紅底警示盒與綠色「✅ 我已按時吃藥」大按鈕。
  2. Web 儀表板測試:開啟 http://127.0.0.1:5000/dashboard,點擊右上角「🔍 一鍵切換大字長輩模式」,確認全頁字級放大,並測試語音播報的 0.8x 慢速 按鈕是否讓播報音調更清晰平緩。
    https://ithelp.ithome.com.tw/upload/images/20260929/20183730l7vFXPMt8u.png

💡 避坑小提示(Troubleshooting):

若在執行 curl 測試時遇到 503 UNAVAILABLE (This model is currently experiencing high demand) 錯誤,代表 Google 雲端 API 處於尖峰流量狀態,此為外部伺服器瞬時負載現象,並非程式碼錯誤。稍候數秒重試或程式內建重試(Retry)即可順利完成解析。

五、版本控制與提交 GitHub

測試通過後,將 Day 16 的修改提交至 GitHub:

git add .
git commit -m "保留雙引號 改填寫自己要記錄的標記 ex.鐵人賽第十六天"
git push

六、本日小結與明日預告

今天我們針對高齡族群的使用情境進行了完整的介面與互動優化,包含 LINE 視覺化 Flex 大字卡片、Web 一鍵大字模式 以及 0.8x 語音慢速播放功能,讓系統不僅技術到位,在體驗層面上更具備社會關懷的人本溫度。

明天(Day 17),我們將進一步強化系統的安全與穩定性,導入 靜態型別檢查(Mypy/Pydantic)與輸入資料防禦校驗,確保多模態 VLM 在面對模糊、破損藥袋圖片時依然能穩定運作不崩潰!


上一篇
Day 15|一目瞭然的遠端關懷!用 Tailwind CSS 與 Chart.js 打造 Web 服藥追蹤儀表板
下一篇
Day 17|打造堅固的防禦牆!用 Pydantic 與 Mypy 實作 API 輸入校驗與強型別防衛
系列文
給藥袋裝一張嘴:30 天用 Android 與 Google VLM 實作高齡語音用藥助手 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言