feat: анимации с реанимации
This commit is contained in:
@@ -27,6 +27,7 @@ import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.FlutterEngineCache
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import android.media.MediaCodecInfo
|
||||
import android.net.Uri
|
||||
@@ -189,37 +190,46 @@ class MainActivity : FlutterActivity() {
|
||||
"ru.komet.app/upload_service",
|
||||
).setMethodCallHandler { call, result ->
|
||||
val ctx = this
|
||||
when (call.method) {
|
||||
"start" -> {
|
||||
val filename = call.argument<String>("filename") ?: "Файл"
|
||||
val intent = Intent(ctx, UploadForegroundService::class.java).apply {
|
||||
action = UploadForegroundService.ACTION_START
|
||||
putExtra(UploadForegroundService.EXTRA_FILENAME, filename)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
fun uploadIntent(call: MethodCall, action: String) =
|
||||
Intent(ctx, UploadForegroundService::class.java).apply {
|
||||
this.action = action
|
||||
putExtra(UploadForegroundService.EXTRA_TITLE, call.argument<String>("title"))
|
||||
putExtra(UploadForegroundService.EXTRA_BODY, call.argument<String>("body") ?: "")
|
||||
putExtra(UploadForegroundService.EXTRA_PROGRESS, call.argument<Int>("progress") ?: 0)
|
||||
putExtra(
|
||||
UploadForegroundService.EXTRA_INDETERMINATE,
|
||||
call.argument<Boolean>("indeterminate") ?: true,
|
||||
)
|
||||
}
|
||||
|
||||
fun launch(intent: Intent, asForeground: Boolean) {
|
||||
try {
|
||||
if (asForeground && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
startForegroundService(intent)
|
||||
} else {
|
||||
startService(intent)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w("UploadService", "${intent.action} failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
when (call.method) {
|
||||
"start" -> {
|
||||
launch(uploadIntent(call, UploadForegroundService.ACTION_START), true)
|
||||
result.success(null)
|
||||
}
|
||||
"update" -> {
|
||||
val filename = call.argument<String>("filename") ?: "Файл"
|
||||
val progress = call.argument<Int>("progress") ?: 0
|
||||
val speed = call.argument<Long>("speed") ?: 0L
|
||||
val intent = Intent(ctx, UploadForegroundService::class.java).apply {
|
||||
action = UploadForegroundService.ACTION_UPDATE
|
||||
putExtra(UploadForegroundService.EXTRA_FILENAME, filename)
|
||||
putExtra(UploadForegroundService.EXTRA_PROGRESS, progress)
|
||||
putExtra(UploadForegroundService.EXTRA_SPEED, speed)
|
||||
}
|
||||
startService(intent)
|
||||
launch(uploadIntent(call, UploadForegroundService.ACTION_UPDATE), false)
|
||||
result.success(null)
|
||||
}
|
||||
"stop" -> {
|
||||
startService(Intent(ctx, UploadForegroundService::class.java).apply {
|
||||
action = UploadForegroundService.ACTION_STOP
|
||||
})
|
||||
launch(
|
||||
Intent(ctx, UploadForegroundService::class.java).apply {
|
||||
action = UploadForegroundService.ACTION_STOP
|
||||
},
|
||||
false,
|
||||
)
|
||||
result.success(null)
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
|
||||
class UploadForegroundService : Service() {
|
||||
@@ -15,11 +16,14 @@ class UploadForegroundService : Service() {
|
||||
const val ACTION_START = "ru.komet.app.UPLOAD_START"
|
||||
const val ACTION_UPDATE = "ru.komet.app.UPLOAD_UPDATE"
|
||||
const val ACTION_STOP = "ru.komet.app.UPLOAD_STOP"
|
||||
const val EXTRA_FILENAME = "filename"
|
||||
const val EXTRA_PROGRESS = "progress" // 0-100
|
||||
const val EXTRA_SPEED = "speed" // bytes/sec (Long)
|
||||
const val EXTRA_TITLE = "title"
|
||||
const val EXTRA_BODY = "body"
|
||||
const val EXTRA_PROGRESS = "progress"
|
||||
const val EXTRA_INDETERMINATE = "indeterminate"
|
||||
}
|
||||
|
||||
private var inForeground = false
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onCreate() {
|
||||
@@ -29,67 +33,96 @@ class UploadForegroundService : Service() {
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
when (intent?.action) {
|
||||
ACTION_START -> {
|
||||
val filename = intent.getStringExtra(EXTRA_FILENAME) ?: "Файл"
|
||||
val notification = buildNotification(filename, 0, 0, indeterminate = true)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
ACTION_START, ACTION_UPDATE -> {
|
||||
val notification = buildNotification(
|
||||
title = intent.getStringExtra(EXTRA_TITLE)
|
||||
?: applicationInfo.loadLabel(packageManager).toString(),
|
||||
body = intent.getStringExtra(EXTRA_BODY) ?: "",
|
||||
progress = intent.getIntExtra(EXTRA_PROGRESS, 0),
|
||||
indeterminate = intent.getBooleanExtra(EXTRA_INDETERMINATE, true),
|
||||
)
|
||||
if (inForeground) {
|
||||
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
|
||||
.notify(NOTIFICATION_ID, notification)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
goForeground(notification)
|
||||
}
|
||||
}
|
||||
ACTION_UPDATE -> {
|
||||
val filename = intent.getStringExtra(EXTRA_FILENAME) ?: "Файл"
|
||||
val progress = intent.getIntExtra(EXTRA_PROGRESS, 0)
|
||||
val speed = intent.getLongExtra(EXTRA_SPEED, 0L)
|
||||
val nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
nm.notify(NOTIFICATION_ID, buildNotification(filename, progress, speed))
|
||||
}
|
||||
ACTION_STOP -> {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
stopSelf()
|
||||
}
|
||||
ACTION_STOP -> stopEverything()
|
||||
}
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
private fun createChannel() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"Загрузка файлов",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
).apply { setShowBadge(false) }
|
||||
(getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
|
||||
.createNotificationChannel(channel)
|
||||
override fun onTaskRemoved(rootIntent: Intent?) {
|
||||
stopEverything()
|
||||
super.onTaskRemoved(rootIntent)
|
||||
}
|
||||
|
||||
private fun goForeground(notification: Notification) {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
}
|
||||
inForeground = true
|
||||
} catch (e: Exception) {
|
||||
Log.w("UploadService", "startForeground failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopEverything() {
|
||||
if (inForeground) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
inForeground = false
|
||||
}
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
private fun createChannel() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
getString(R.string.upload_channel_name),
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
).apply {
|
||||
setShowBadge(false)
|
||||
setSound(null, null)
|
||||
enableVibration(false)
|
||||
enableLights(false)
|
||||
}
|
||||
manager.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
private fun buildNotification(
|
||||
filename: String,
|
||||
title: String,
|
||||
body: String,
|
||||
progress: Int,
|
||||
speedBps: Long,
|
||||
indeterminate: Boolean = false
|
||||
indeterminate: Boolean,
|
||||
): Notification {
|
||||
val body = when {
|
||||
indeterminate -> "Подготовка..."
|
||||
speedBps > 0 -> "$progress% · ${formatSpeed(speedBps)}"
|
||||
else -> "$progress%"
|
||||
}
|
||||
return NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
val open = PendingIntent.getActivity(
|
||||
this,
|
||||
0,
|
||||
Intent(this, MainActivity::class.java)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT,
|
||||
)
|
||||
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_upload)
|
||||
.setContentTitle(filename)
|
||||
.setContentTitle(title)
|
||||
.setContentText(body)
|
||||
.setContentIntent(open)
|
||||
.setProgress(100, progress, indeterminate)
|
||||
.setOngoing(true)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setSilent(true)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun formatSpeed(bps: Long): String = when {
|
||||
bps < 1_024L -> "$bps Б/с"
|
||||
bps < 1_048_576L -> "${bps / 1024} КБ/с"
|
||||
else -> "${"%.1f".format(bps / 1_048_576.0)} МБ/с"
|
||||
.setOnlyAlertOnce(true)
|
||||
.setDefaults(0)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
builder.setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_DEFERRED)
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="upload_channel_name">Отправка медиа</string>
|
||||
</resources>
|
||||
@@ -1,4 +1,5 @@
|
||||
<resources>
|
||||
<string name="nfc_service_description">Komet contact exchange</string>
|
||||
<string name="nfc_aid_group_description">Komet contact exchange</string>
|
||||
<string name="upload_channel_name">Sending media</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user