Camera page kept the session running while the host app was
backgrounded — wastes battery, holds the hardware, and blocks
other apps from grabbing the camera. Add per-platform observers
that pause/resume the session on app foreground/background, with
a uniform `pauseForBackground` / `resumeForForeground` pair on the
shared CameraInstance.
Behaviour:
- On background: any in-flight recording is hard-cancelled
(matches every messaging app — the take ends with the app
switch). The session stops so the OS can release the camera.
- On foreground: session restarts iff it had been running.
Emits `sessionInterrupted` (`reason: appBackgrounded`) and
`sessionResumed` events so the Dart side can surface UX
affordances if needed.
iOS — `ios/Classes/Camera/CameraInstance+iOS.swift`:
Subscribes to UIApplication.{willResignActive, didBecomeActive}
notifications. Work hops onto sessionQueue so AV mutations stay
serialised. Storage uses the shared
`CameraInstance.lifecycleCleanup` closure slot — extension
doesn't need to add stored properties.
Android — added `androidx.lifecycle:lifecycle-process:2.7.0`,
observes `ProcessLifecycleOwner.get().lifecycle`. ON_STOP →
`pauseForBackground` (cancels recording + drops
CustomLifecycleOwner to CREATED → CameraX releases camera).
ON_START → `resumeForForeground`. Observer add/remove on main
thread per `ProcessLifecycleOwner` contract.
macOS — `macos/Classes/Camera/CameraInstance+macOS.swift`:
Intentional no-op. macOS desktop background semantics are
softer; the chat composer's Card dialog typically stays
foregrounded. Slot is wired so the shared
`observeLifecycle()` call still compiles.
Verified: all four platforms (iOS / Android / macOS / app tests)
build clean. Pod install picks up the new iOS extension file
once Pods/ is fresh — `flutter clean` if mid-iteration.
74 lines
2.0 KiB
Groovy
74 lines
2.0 KiB
Groovy
group 'io.swipelab.ux'
|
|
version '1.0-SNAPSHOT'
|
|
|
|
buildscript {
|
|
ext.kotlin_version = '1.9.22'
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
}
|
|
|
|
dependencies {
|
|
classpath 'com.android.tools.build:gradle:8.1.0'
|
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
}
|
|
}
|
|
|
|
rootProject.allprojects {
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
}
|
|
}
|
|
|
|
apply plugin: 'com.android.library'
|
|
apply plugin: 'kotlin-android'
|
|
|
|
android {
|
|
namespace 'io.swipelab.ux'
|
|
compileSdk 34
|
|
|
|
defaultConfig {
|
|
minSdk 21
|
|
externalNativeBuild {
|
|
cmake {
|
|
cppFlags ""
|
|
}
|
|
}
|
|
}
|
|
|
|
externalNativeBuild {
|
|
cmake {
|
|
path "src/main/jni/CMakeLists.txt"
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_1_8
|
|
targetCompatibility JavaVersion.VERSION_1_8
|
|
}
|
|
|
|
kotlinOptions {
|
|
jvmTarget = '1.8'
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
// CameraX for scanner preview + frame analysis + ux.camera.
|
|
// 1.4.0+ ships 16-KB-aligned `libimage_processing_util_jni.so`
|
|
// (Android 15 requirement); 1.3.x failed the elf-alignment check.
|
|
def cameraxVersion = '1.4.2'
|
|
implementation "androidx.camera:camera-core:$cameraxVersion"
|
|
implementation "androidx.camera:camera-camera2:$cameraxVersion"
|
|
implementation "androidx.camera:camera-lifecycle:$cameraxVersion"
|
|
implementation "androidx.camera:camera-view:$cameraxVersion"
|
|
implementation "androidx.camera:camera-video:$cameraxVersion"
|
|
// ProcessLifecycleOwner so CameraInstance can release the camera
|
|
// when the host app backgrounds (and re-acquire on foreground).
|
|
// camera-lifecycle pulls in lifecycle-common but not the process
|
|
// observer; this adds it.
|
|
implementation 'androidx.lifecycle:lifecycle-process:2.7.0'
|
|
// Pure-Kotlin/Java QR decoder. ~470 KB jar, no Play Services dep.
|
|
implementation 'com.google.zxing:core:3.5.3'
|
|
}
|