fix(mobile,Android): throttle detail progress notifications & wait on foregroundInfo (#907)
This commit is contained in:
parent
cfa04fadd1
commit
dcefd53bfe
2 changed files with 60 additions and 18 deletions
|
@ -50,6 +50,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
|||
private var timeBackupStarted: Long = 0L
|
||||
private var notificationBuilder: NotificationCompat.Builder? = null
|
||||
private var notificationDetailBuilder: NotificationCompat.Builder? = null
|
||||
private var fgFuture: ListenableFuture<Void>? = null
|
||||
|
||||
override fun startWork(): ListenableFuture<ListenableWorker.Result> {
|
||||
|
||||
|
@ -112,6 +113,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
|||
Handler(Looper.getMainLooper()).postAtFrontOfQueue {
|
||||
backgroundChannel.invokeMethod("systemStop", null)
|
||||
}
|
||||
waitOnSetForegroundAsync()
|
||||
// cannot await/get(block) on resolvableFuture as its already cancelled (would throw CancellationException)
|
||||
// instead, wait for 5 seconds until forcefully stopping backup work
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
|
@ -119,6 +121,17 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
|||
}, 5000)
|
||||
}
|
||||
|
||||
private fun waitOnSetForegroundAsync() {
|
||||
val fgFuture = this.fgFuture
|
||||
if (fgFuture != null && !fgFuture.isCancelled() && !fgFuture.isDone()) {
|
||||
try {
|
||||
fgFuture.get(500, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
catch (e: Exception) {
|
||||
// ignored, there is nothing to be done
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopEngine(result: Result?) {
|
||||
if (result != null) {
|
||||
|
@ -128,6 +141,7 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
|||
engine?.destroy()
|
||||
engine = null
|
||||
clearBackgroundNotification()
|
||||
waitOnSetForegroundAsync()
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, r: MethodChannel.Result) {
|
||||
|
@ -207,8 +221,8 @@ class BackupWorker(ctx: Context, params: WorkerParameters) : ListenableWorker(ct
|
|||
|
||||
private fun showInfo(notification: Notification, isDetail: Boolean = false) {
|
||||
val id = if(isDetail) NOTIFICATION_DETAIL_ID else NOTIFICATION_ID
|
||||
if (isIgnoringBatteryOptimizations) {
|
||||
setForegroundAsync(ForegroundInfo(id, notification))
|
||||
if (isIgnoringBatteryOptimizations && !isDetail) {
|
||||
fgFuture = setForegroundAsync(ForegroundInfo(id, notification))
|
||||
} else {
|
||||
notificationManager.notify(id, notification)
|
||||
}
|
||||
|
|
|
@ -43,8 +43,9 @@ class BackgroundService {
|
|||
bool _errorGracePeriodExceeded = true;
|
||||
int _uploadedAssetsCount = 0;
|
||||
int _assetsToUploadCount = 0;
|
||||
int _lastDetailProgressUpdate = 0;
|
||||
String _lastPrintedProgress = "";
|
||||
late final _Throttle _throttleNotificationUpdates =
|
||||
_Throttle(_updateDetailProgress, const Duration(milliseconds: 400));
|
||||
|
||||
bool get isBackgroundInitialized {
|
||||
return _isBackgroundInitialized;
|
||||
|
@ -447,21 +448,20 @@ class BackgroundService {
|
|||
}
|
||||
|
||||
void _onProgress(int sent, int total) {
|
||||
final int now = Timeline.now;
|
||||
// limit updates to 10 per second (or Android drops important notifications)
|
||||
if (now > _lastDetailProgressUpdate + 100000) {
|
||||
final String msg = _humanReadableBytesProgress(sent, total);
|
||||
// only update if message actually differs (to stop many useless notification updates on large assets or slow connections)
|
||||
if (msg != _lastPrintedProgress) {
|
||||
_lastDetailProgressUpdate = now;
|
||||
_lastPrintedProgress = msg;
|
||||
_updateNotification(
|
||||
progress: sent,
|
||||
max: total,
|
||||
isDetail: true,
|
||||
content: msg,
|
||||
);
|
||||
}
|
||||
_throttleNotificationUpdates(sent, total);
|
||||
}
|
||||
|
||||
void _updateDetailProgress(int sent, int total) {
|
||||
final String msg = _humanReadableBytesProgress(sent, total);
|
||||
// only update if message actually differs (to stop many useless notification updates on large assets or slow connections)
|
||||
if (msg != _lastPrintedProgress) {
|
||||
_lastPrintedProgress = msg;
|
||||
_updateNotification(
|
||||
progress: sent,
|
||||
max: total,
|
||||
isDetail: true,
|
||||
content: msg,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -532,6 +532,34 @@ class BackgroundService {
|
|||
}
|
||||
}
|
||||
|
||||
class _Throttle {
|
||||
_Throttle(this._fun, Duration interval) : _interval = interval.inMicroseconds;
|
||||
final void Function(int, int) _fun;
|
||||
final int _interval;
|
||||
int _invokedAt = 0;
|
||||
Timer? _timer;
|
||||
int _progress = 0;
|
||||
int _total = 0;
|
||||
|
||||
void call(int progress, int total) {
|
||||
final time = Timeline.now;
|
||||
_progress = progress;
|
||||
_total = total;
|
||||
if (time > _invokedAt + _interval) {
|
||||
_timer?.cancel();
|
||||
_onTimeElapsed();
|
||||
} else {
|
||||
_timer ??= Timer(Duration(microseconds: _interval), _onTimeElapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void _onTimeElapsed() {
|
||||
_invokedAt = Timeline.now;
|
||||
_fun(_progress, _total);
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// entry point called by Kotlin/Java code; needs to be a top-level function
|
||||
@pragma('vm:entry-point')
|
||||
void _nativeEntry() {
|
||||
|
|
Loading…
Reference in a new issue