AeroToss: авто-обновление + foreground service + кнопка отмены
1. Авто-обновление: - Discovery пересканируется каждые 5 секунд автоматически - Устройства снова появляются после потери связи 2. Foreground service (Android): - AeroTossService: уведомление + wake lock - Разрешения: FOREGROUND_SERVICE, POST_NOTIFICATIONS, WAKE_LOCK - Приложение работает в фоне, передача не останавливается 3. Кнопка отмены: - HomeScreen: кнопка 'Отменить отправку' при передаче - SendScreen: кнопка 'Отменить отправку' с отменой передачи 4. Разрешения: - Запрос POST_NOTIFICATIONS при первом запуске (Android 13+) - Запрос ACCESS_FINE_LOCATION
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
package com.aerotoss
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
|
||||
class AeroTossService : Service() {
|
||||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
createNotificationChannel()
|
||||
val notification = Notification.Builder(this, CHANNEL_ID)
|
||||
.setContentTitle("AeroToss")
|
||||
.setContentText("Поиск устройств...")
|
||||
.setSmallIcon(android.R.drawable.ic_menu_share)
|
||||
.build()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val pm = getSystemService(POWER_SERVICE) as PowerManager
|
||||
@Suppress("DEPRECATION")
|
||||
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AeroToss::DiscoveryLock")
|
||||
wakeLock?.acquire(60 * 60 * 1000L)
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
wakeLock?.release()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun createNotificationChannel() {
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID,
|
||||
"AeroToss Discovery",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
)
|
||||
val nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
nm.createNotificationChannel(channel)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL_ID = "aerotoss_discovery"
|
||||
const val NOTIFICATION_ID = 1
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.aerotoss
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.aerotoss.core.AeroTossManager
|
||||
import com.aerotoss.discovery.CompositeAndroidDiscovery
|
||||
@@ -25,14 +30,46 @@ class MainActivity : ComponentActivity() {
|
||||
handleAndroidPickResult(uri, applicationContext)
|
||||
}
|
||||
|
||||
private val permissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestMultiplePermissions()
|
||||
) { permissions ->
|
||||
if (permissions.values.all { it }) {
|
||||
startApp()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (manager != null) return
|
||||
|
||||
androidFilePickerLauncher = { mimeTypes, _ ->
|
||||
filePickerLauncher.launch(mimeTypes)
|
||||
}
|
||||
|
||||
val neededPermissions = mutableListOf(
|
||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||
Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
neededPermissions.add(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
|
||||
val notGranted = neededPermissions.filter {
|
||||
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
|
||||
if (notGranted.isNotEmpty()) {
|
||||
permissionLauncher.launch(notGranted.toTypedArray())
|
||||
} else {
|
||||
startApp()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startApp() {
|
||||
if (manager != null) return
|
||||
|
||||
startForegroundService(Intent(this, AeroTossService::class.java))
|
||||
|
||||
val transferManager = AndroidFileTransferManager(applicationContext)
|
||||
val wifiDirectManager = WifiDirectManager(applicationContext)
|
||||
val discoveryManager = CompositeAndroidDiscovery(wifiDirectManager)
|
||||
@@ -51,5 +88,6 @@ class MainActivity : ComponentActivity() {
|
||||
super.onDestroy()
|
||||
manager?.dispose()
|
||||
manager = null
|
||||
stopService(Intent(this, AeroTossService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user