You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
242 lines
6.5 KiB
Markdown
242 lines
6.5 KiB
Markdown
dt# Quick Summary - Changes Made (March 1, 2026)
|
|
|
|
## Device Context
|
|
**Important:** These are **AC-powered Android LED kiosk displays**, not battery-powered tablets. The issue is not battery drain, but Android OS killing "idle" apps even on plugged-in devices.
|
|
|
|
## Problem Identified from Logs
|
|
- App started ~midnight Feb 26
|
|
- Ran successfully with health checks until 8:36 AM Feb 27 (~14 hours)
|
|
- **Completely stopped logging** after 8:36 AM
|
|
- No crash logs, no lifecycle events
|
|
- User manually restarted March 1 at 10:54 AM
|
|
|
|
## Root Cause
|
|
**Android OS killed the app** after ~14 hours because:
|
|
1. Android treats long-running apps as "idle" even on AC power
|
|
2. No user interaction for extended periods
|
|
3. Android's background task restrictions kicked in
|
|
4. App wasn't in proper kiosk/foreground mode
|
|
|
|
---
|
|
|
|
## Critical Changes Made
|
|
|
|
### 1. **Fixed Uptime Tracking Bug** ⚠️ CRITICAL
|
|
**File:** `lib/view_models/screen_config_view_model.dart`
|
|
|
|
**Problem:**
|
|
```dart
|
|
// WRONG - calculated time since last MIDNIGHT, not app start
|
|
DateTime.now().difference(lastChecked).inHours
|
|
```
|
|
|
|
**Solution:**
|
|
```dart
|
|
DateTime appStartTime = DateTime.now(); // Track actual start time
|
|
final uptimeHours = DateTime.now().difference(appStartTime).inHours;
|
|
```
|
|
|
|
**Impact:** Now you'll see accurate uptime like:
|
|
```
|
|
Health check performed - App uptime: 14h 23m (started: 2026-02-26 20:00:00)
|
|
```
|
|
|
|
---
|
|
|
|
### 2. **Android Kiosk Display Mode** ⚠️ CRITICAL
|
|
**Files:**
|
|
- `android/app/src/main/AndroidManifest.xml`
|
|
- `android/app/src/main/kotlin/.../MainActivity.kt`
|
|
|
|
**Changes:**
|
|
1. Added window flags to prevent sleep/idle:
|
|
```kotlin
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD)
|
|
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
|
}
|
|
```
|
|
|
|
2. Added activity flags in manifest:
|
|
```xml
|
|
android:keepScreenOn="true"
|
|
android:screenOrientation="portrait"
|
|
```
|
|
|
|
**Impact:** Prevents Android from treating app as "idle" and killing it. No user dialogs needed - works automatically.
|
|
|
|
---
|
|
|
|
### 3. **Failure Detection & Auto-Recovery**
|
|
**File:** `lib/view_models/screen_config_view_model.dart`
|
|
|
|
**Added:**
|
|
- Track consecutive health check failures
|
|
- After 3 failures, automatically restart SignalR connection
|
|
- Log critical warnings
|
|
|
|
```dart
|
|
int healthCheckFailures = 0;
|
|
|
|
try {
|
|
syncHubConnectionState();
|
|
healthCheckFailures = 0; // Reset on success
|
|
} catch (e) {
|
|
healthCheckFailures++;
|
|
if (healthCheckFailures >= 3) {
|
|
// Restart connection
|
|
await queuingViewModel.stopHubConnection();
|
|
await Future.delayed(const Duration(seconds: 2));
|
|
await queuingViewModel.startHubConnection();
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. **Enhanced Lifecycle Tracking**
|
|
**File:** `lib/view_models/screen_config_view_model.dart`
|
|
|
|
**Changes:**
|
|
- Log actual uptime in lifecycle events
|
|
- Track when app goes to background
|
|
- Track when app is killed
|
|
|
|
**New Logs:**
|
|
```
|
|
[onAppPaused] - App uptime: 14h - WARNING: App going to background!
|
|
[onAppDetached] - App uptime: 14h - CRITICAL: App being killed!
|
|
```
|
|
|
|
**Impact:** You'll now see EXACTLY when and why the app dies
|
|
|
|
---
|
|
|
|
### 5. **Clinic Prefix Feature**
|
|
**File:** `lib/models/global_config_model.dart`
|
|
|
|
**Added:**
|
|
```dart
|
|
bool globalClinicPrefixReq = false;
|
|
bool clinicPrefixReq = true;
|
|
|
|
bool isClinicPrefixAdded(String ticketNo) {
|
|
final hasClinicPrefix = RegExp(r'^[A-Za-z]{3} W-').hasMatch(ticketNo);
|
|
return hasClinicPrefix;
|
|
}
|
|
```
|
|
|
|
**Returns true for:** `"XXX W-78"` (3 letters + space + W-)
|
|
**Returns false for:** `"W-A-12"`, `"XX-12"`, `"X-7"`, etc.
|
|
|
|
---
|
|
|
|
## Files Modified
|
|
|
|
1. ✅ `lib/view_models/screen_config_view_model.dart`
|
|
2. ✅ `lib/models/global_config_model.dart`
|
|
3. ✅ `lib/repositories/signalR_repo.dart`
|
|
4. ✅ `android/app/src/main/AndroidManifest.xml`
|
|
5. ✅ `android/app/src/main/kotlin/.../MainActivity.kt`
|
|
6. ✅ `CHANGES_EXPLANATION.md` (full documentation)
|
|
|
|
---
|
|
|
|
## Next Steps - Deploy & Test
|
|
|
|
### 1. Build APK
|
|
```bash
|
|
cd /Volumes/Data/Projects/Flutter/HMG_QLine
|
|
flutter clean
|
|
flutter build apk --release
|
|
```
|
|
|
|
### 2. Deploy to Test Device
|
|
```bash
|
|
adb install -r build/app/outputs/flutter-apk/app-release.apk
|
|
```
|
|
|
|
### 3. First Launch Actions
|
|
- **No user action required!**
|
|
- App automatically configures kiosk display mode
|
|
- Check logs to verify health checks are running
|
|
|
|
### 4. Monitor Logs
|
|
Look for these every 5 minutes:
|
|
```
|
|
[2026-03-01 11:00:00 AM] Health check performed - App uptime: 0h 5m (started: 2026-03-01 10:54:37)
|
|
[2026-03-01 11:00:01 AM] Health check - Hub connected: true, Internet: true
|
|
```
|
|
|
|
Uptime should increment correctly: 0h 5m → 0h 10m → 0h 15m → ... → 14h 0m → 14h 5m
|
|
|
|
### 5. What You Should NOT See
|
|
If app stays alive, you should NOT see:
|
|
- `[onAppPaused]` - means app going to background
|
|
- `[onAppDetached]` - means app being killed
|
|
|
|
---
|
|
|
|
## Expected Timeline
|
|
|
|
| Time | Expected Behavior |
|
|
|------|------------------|
|
|
| **First 5 min** | Health check logs start appearing |
|
|
| **After 1 hour** | Uptime shows ~1h 0m |
|
|
| **After 14 hours** | ⚠️ **CRITICAL CHECKPOINT** - App should still be running (previously died here) |
|
|
| **After 24 hours** | Uptime shows ~24h 0m |
|
|
| **After 48 hours** | Uptime shows ~48h 0m |
|
|
| **After 60+ hours** | Warning log: "App running for 60+ hours" |
|
|
|
|
---
|
|
|
|
## If App Still Dies
|
|
|
|
### Check 1: Last Logs Before Death
|
|
Look at last log entry before silence:
|
|
- If `[onAppPaused]` present → OS killed app (check device settings)
|
|
- If no lifecycle event → app crashed (check for exceptions)
|
|
|
|
### Check 2: Device-Specific Settings
|
|
Some Android devices have aggressive task killers even on AC power:
|
|
- Check "Auto-start" settings (Xiaomi, Oppo, Vivo)
|
|
- Check "Background restrictions" in device settings
|
|
- May need to whitelist app in manufacturer's custom settings
|
|
|
|
### Check 3: Android Version
|
|
- Android 6+ has Doze mode even on AC power
|
|
- Android 9+ has App Standby Buckets
|
|
- May need ADB commands to whitelist:
|
|
```bash
|
|
adb shell dumpsys deviceidle whitelist +com.example.hmg_qline.hmg_qline
|
|
```
|
|
|
|
---
|
|
|
|
## Success Indicators
|
|
|
|
✅ Health check logs every 5 minutes
|
|
✅ Uptime increments correctly
|
|
✅ No `[onAppPaused]` or `[onAppDetached]` logs
|
|
✅ Hub connection stays alive
|
|
✅ App runs past 14-hour mark
|
|
✅ App runs 48+ hours without restart
|
|
|
|
---
|
|
|
|
## Emergency Rollback
|
|
|
|
If new version causes issues:
|
|
1. Keep old APK as backup
|
|
2. Reinstall old version via ADB
|
|
3. Report issue with last 100 lines of logs
|
|
|
|
|
|
|
|
|
|
|