android 14: use work manager instead of foreground service as datasync
This commit is contained in:
parent
c94fc7245e
commit
01d2e21369
41 changed files with 214 additions and 377 deletions
|
@ -204,6 +204,7 @@ dependencies {
|
||||||
implementation 'androidx.media:media:1.6.0'
|
implementation 'androidx.media:media:1.6.0'
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
|
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
|
||||||
|
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
||||||
|
|
||||||
implementation 'com.caverock:androidsvg-aar:1.4'
|
implementation 'com.caverock:androidsvg-aar:1.4'
|
||||||
implementation 'com.commonsware.cwac:document:0.5.0'
|
implementation 'com.commonsware.cwac:document:0.5.0'
|
||||||
|
|
|
@ -40,11 +40,6 @@ This change eventually prevents building the app with Flutter v3.7.11.
|
||||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||||
<!-- to analyze media in a service -->
|
<!-- to analyze media in a service -->
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<!--
|
|
||||||
TODO TLAD [Android 14 (API 34)] request FOREGROUND_SERVICE_DATA_SYNC permission
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
|
||||||
cf https://developer.android.com/about/versions/14/changes/fgs-types-required
|
|
||||||
-->
|
|
||||||
<!-- to fetch map tiles -->
|
<!-- to fetch map tiles -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<!-- from Android 12 (API 31), users can optionally grant access to the media management special permission -->
|
<!-- from Android 12 (API 31), users can optionally grant access to the media management special permission -->
|
||||||
|
@ -251,12 +246,6 @@ This change eventually prevents building the app with Flutter v3.7.11.
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".AnalysisService"
|
|
||||||
android:description="@string/analysis_service_description"
|
|
||||||
android:exported="false"
|
|
||||||
android:foregroundServiceType="dataSync" />
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".ScreenSaverService"
|
android:name=".ScreenSaverService"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|
|
@ -1,258 +0,0 @@
|
||||||
package deckers.thibault.aves
|
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.*
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.NotificationChannelCompat
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import app.loup.streams_channel.StreamsChannel
|
|
||||||
import deckers.thibault.aves.MainActivity.Companion.OPEN_FROM_ANALYSIS_SERVICE
|
|
||||||
import deckers.thibault.aves.channel.calls.*
|
|
||||||
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
|
|
||||||
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
|
|
||||||
import deckers.thibault.aves.utils.FlutterUtils
|
|
||||||
import deckers.thibault.aves.utils.LogUtils
|
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
|
||||||
import io.flutter.plugin.common.MethodCall
|
|
||||||
import io.flutter.plugin.common.MethodChannel
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
|
|
||||||
class AnalysisService : Service() {
|
|
||||||
private var flutterEngine: FlutterEngine? = null
|
|
||||||
private var backgroundChannel: MethodChannel? = null
|
|
||||||
private var serviceLooper: Looper? = null
|
|
||||||
private var serviceHandler: ServiceHandler? = null
|
|
||||||
private val analysisServiceBinder = AnalysisServiceBinder()
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
Log.i(LOG_TAG, "Create analysis service")
|
|
||||||
runBlocking {
|
|
||||||
FlutterUtils.initFlutterEngine(this@AnalysisService, SHARED_PREFERENCES_KEY, CALLBACK_HANDLE_KEY) {
|
|
||||||
flutterEngine = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
initChannels(this)
|
|
||||||
|
|
||||||
HandlerThread("Analysis service handler", Process.THREAD_PRIORITY_BACKGROUND).apply {
|
|
||||||
start()
|
|
||||||
serviceLooper = looper
|
|
||||||
serviceHandler = ServiceHandler(looper)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(LOG_TAG, "failed to initialize service", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
Log.i(LOG_TAG, "Destroy analysis service")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent) = analysisServiceBinder
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
val channel = NotificationChannelCompat.Builder(CHANNEL_ANALYSIS, NotificationManagerCompat.IMPORTANCE_LOW)
|
|
||||||
.setName(getText(R.string.analysis_channel_name))
|
|
||||||
.setShowBadge(false)
|
|
||||||
.build()
|
|
||||||
NotificationManagerCompat.from(this).createNotificationChannel(channel)
|
|
||||||
startForeground(NOTIFICATION_ID, buildNotification())
|
|
||||||
|
|
||||||
val msgData = Bundle()
|
|
||||||
intent?.extras?.let {
|
|
||||||
msgData.putAll(it)
|
|
||||||
}
|
|
||||||
serviceHandler?.obtainMessage()?.let { msg ->
|
|
||||||
msg.arg1 = startId
|
|
||||||
msg.data = msgData
|
|
||||||
serviceHandler?.sendMessage(msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return START_NOT_STICKY
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun detachAndStop() {
|
|
||||||
analysisServiceBinder.detach()
|
|
||||||
stopSelf()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initChannels(context: Context) {
|
|
||||||
val engine = flutterEngine
|
|
||||||
engine ?: throw Exception("Flutter engine is not initialized")
|
|
||||||
|
|
||||||
val messenger = engine.dartExecutor
|
|
||||||
|
|
||||||
// channels for analysis
|
|
||||||
|
|
||||||
// dart -> platform -> dart
|
|
||||||
// - need Context
|
|
||||||
MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(context))
|
|
||||||
MethodChannel(messenger, GeocodingHandler.CHANNEL).setMethodCallHandler(GeocodingHandler(context))
|
|
||||||
MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(context))
|
|
||||||
MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(context))
|
|
||||||
MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(context))
|
|
||||||
|
|
||||||
// result streaming: dart -> platform ->->-> dart
|
|
||||||
// - need Context
|
|
||||||
StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(context, args) }
|
|
||||||
StreamsChannel(messenger, MediaStoreStreamHandler.CHANNEL).setStreamHandlerFactory { args -> MediaStoreStreamHandler(context, args) }
|
|
||||||
|
|
||||||
// channel for service management
|
|
||||||
backgroundChannel = MethodChannel(messenger, BACKGROUND_CHANNEL).apply {
|
|
||||||
setMethodCallHandler { call, result -> onMethodCall(call, result) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
|
||||||
when (call.method) {
|
|
||||||
"initialized" -> {
|
|
||||||
Log.d(LOG_TAG, "background channel is ready")
|
|
||||||
result.success(null)
|
|
||||||
}
|
|
||||||
"updateNotification" -> {
|
|
||||||
val title = call.argument<String>("title")
|
|
||||||
val message = call.argument<String>("message")
|
|
||||||
val notification = buildNotification(title, message)
|
|
||||||
NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification)
|
|
||||||
result.success(null)
|
|
||||||
}
|
|
||||||
"refreshApp" -> {
|
|
||||||
analysisServiceBinder.refreshApp()
|
|
||||||
result.success(null)
|
|
||||||
}
|
|
||||||
"stop" -> {
|
|
||||||
detachAndStop()
|
|
||||||
result.success(null)
|
|
||||||
}
|
|
||||||
else -> result.notImplemented()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildNotification(title: String? = null, message: String? = null): Notification {
|
|
||||||
val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
} else {
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
}
|
|
||||||
val stopServiceIntent = Intent(this, AnalysisService::class.java).let {
|
|
||||||
it.putExtra(KEY_COMMAND, COMMAND_STOP)
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
PendingIntent.getForegroundService(this, STOP_SERVICE_REQUEST, it, pendingIntentFlags)
|
|
||||||
} else {
|
|
||||||
PendingIntent.getService(this, STOP_SERVICE_REQUEST, it, pendingIntentFlags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val openAppIntent = Intent(this, MainActivity::class.java).let {
|
|
||||||
PendingIntent.getActivity(this, OPEN_FROM_ANALYSIS_SERVICE, it, pendingIntentFlags)
|
|
||||||
}
|
|
||||||
val stopAction = NotificationCompat.Action.Builder(
|
|
||||||
R.drawable.ic_outline_stop_24,
|
|
||||||
getString(R.string.analysis_notification_action_stop),
|
|
||||||
stopServiceIntent
|
|
||||||
).build()
|
|
||||||
val icon = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) R.drawable.ic_notification else R.mipmap.ic_launcher_round
|
|
||||||
return NotificationCompat.Builder(this, CHANNEL_ANALYSIS)
|
|
||||||
.setContentTitle(title ?: getText(R.string.analysis_notification_default_title))
|
|
||||||
.setContentText(message)
|
|
||||||
.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
|
|
||||||
.setSmallIcon(icon)
|
|
||||||
.setContentIntent(openAppIntent)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
||||||
.addAction(stopAction)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class ServiceHandler(looper: Looper) : Handler(looper) {
|
|
||||||
override fun handleMessage(msg: Message) {
|
|
||||||
val data = msg.data
|
|
||||||
when (data.getString(KEY_COMMAND)) {
|
|
||||||
COMMAND_START -> {
|
|
||||||
runBlocking {
|
|
||||||
FlutterUtils.runOnUiThread {
|
|
||||||
val entryIds = data.getIntArray(KEY_ENTRY_IDS)?.toList()
|
|
||||||
backgroundChannel?.invokeMethod(
|
|
||||||
"start", hashMapOf(
|
|
||||||
"entryIds" to entryIds,
|
|
||||||
"force" to data.getBoolean(KEY_FORCE),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
COMMAND_STOP -> {
|
|
||||||
// unconditionally stop the service
|
|
||||||
runBlocking {
|
|
||||||
FlutterUtils.runOnUiThread {
|
|
||||||
backgroundChannel?.invokeMethod("stop", null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
detachAndStop()
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOG_TAG = LogUtils.createTag<AnalysisService>()
|
|
||||||
private const val BACKGROUND_CHANNEL = "deckers.thibault/aves/analysis_service_background"
|
|
||||||
const val SHARED_PREFERENCES_KEY = "analysis_service"
|
|
||||||
const val CALLBACK_HANDLE_KEY = "callback_handle"
|
|
||||||
|
|
||||||
const val NOTIFICATION_ID = 1
|
|
||||||
const val STOP_SERVICE_REQUEST = 1
|
|
||||||
const val CHANNEL_ANALYSIS = "analysis"
|
|
||||||
|
|
||||||
const val KEY_COMMAND = "command"
|
|
||||||
const val COMMAND_START = "start"
|
|
||||||
const val COMMAND_STOP = "stop"
|
|
||||||
const val KEY_ENTRY_IDS = "entry_ids"
|
|
||||||
const val KEY_FORCE = "force"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AnalysisServiceBinder : Binder() {
|
|
||||||
private val listeners = hashSetOf<AnalysisServiceListener>()
|
|
||||||
|
|
||||||
fun startListening(listener: AnalysisServiceListener) = listeners.add(listener)
|
|
||||||
|
|
||||||
fun stopListening(listener: AnalysisServiceListener) = listeners.remove(listener)
|
|
||||||
|
|
||||||
fun refreshApp() {
|
|
||||||
val localListeners = listeners.toSet()
|
|
||||||
for (listener in localListeners) {
|
|
||||||
try {
|
|
||||||
listener.refreshApp()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(LOG_TAG, "failed to notify listener=$listener", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun detach() {
|
|
||||||
val localListeners = listeners.toSet()
|
|
||||||
for (listener in localListeners) {
|
|
||||||
try {
|
|
||||||
listener.detachFromActivity()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(LOG_TAG, "failed to detach listener=$listener", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOG_TAG = LogUtils.createTag<AnalysisServiceBinder>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AnalysisServiceListener {
|
|
||||||
fun refreshApp()
|
|
||||||
fun detachFromActivity()
|
|
||||||
}
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
package deckers.thibault.aves
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationChannelCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.ForegroundInfo
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import app.loup.streams_channel.StreamsChannel
|
||||||
|
import deckers.thibault.aves.channel.calls.DeviceHandler
|
||||||
|
import deckers.thibault.aves.channel.calls.GeocodingHandler
|
||||||
|
import deckers.thibault.aves.channel.calls.MediaStoreHandler
|
||||||
|
import deckers.thibault.aves.channel.calls.MetadataFetchHandler
|
||||||
|
import deckers.thibault.aves.channel.calls.StorageHandler
|
||||||
|
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
|
||||||
|
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
|
||||||
|
import deckers.thibault.aves.utils.FlutterUtils
|
||||||
|
import deckers.thibault.aves.utils.LogUtils
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.resumeWithException
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
class AnalysisWorker(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters) {
|
||||||
|
private var workCont: Continuation<Any?>? = null
|
||||||
|
private var flutterEngine: FlutterEngine? = null
|
||||||
|
private var backgroundChannel: MethodChannel? = null
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
createNotificationChannel()
|
||||||
|
setForeground(createForegroundInfo())
|
||||||
|
suspendCoroutine { cont ->
|
||||||
|
workCont = cont
|
||||||
|
onStart()
|
||||||
|
}
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onStart() {
|
||||||
|
Log.i(LOG_TAG, "Start analysis worker")
|
||||||
|
runBlocking {
|
||||||
|
FlutterUtils.initFlutterEngine(applicationContext, SHARED_PREFERENCES_KEY, CALLBACK_HANDLE_KEY) {
|
||||||
|
flutterEngine = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
initChannels(applicationContext)
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
FlutterUtils.runOnUiThread {
|
||||||
|
backgroundChannel?.invokeMethod(
|
||||||
|
"start", hashMapOf(
|
||||||
|
"entryIds" to inputData.getIntArray(KEY_ENTRY_IDS)?.toList(),
|
||||||
|
"force" to inputData.getBoolean(KEY_FORCE, false),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(LOG_TAG, "failed to initialize worker", e)
|
||||||
|
workCont?.resumeWithException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initChannels(context: Context) {
|
||||||
|
val engine = flutterEngine
|
||||||
|
engine ?: throw Exception("Flutter engine is not initialized")
|
||||||
|
|
||||||
|
val messenger = engine.dartExecutor
|
||||||
|
|
||||||
|
// channels for analysis
|
||||||
|
|
||||||
|
// dart -> platform -> dart
|
||||||
|
// - need Context
|
||||||
|
MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(context))
|
||||||
|
MethodChannel(messenger, GeocodingHandler.CHANNEL).setMethodCallHandler(GeocodingHandler(context))
|
||||||
|
MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(context))
|
||||||
|
MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(context))
|
||||||
|
MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(context))
|
||||||
|
|
||||||
|
// result streaming: dart -> platform ->->-> dart
|
||||||
|
// - need Context
|
||||||
|
StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(context, args) }
|
||||||
|
StreamsChannel(messenger, MediaStoreStreamHandler.CHANNEL).setStreamHandlerFactory { args -> MediaStoreStreamHandler(context, args) }
|
||||||
|
|
||||||
|
// channel for service management
|
||||||
|
backgroundChannel = MethodChannel(messenger, BACKGROUND_CHANNEL).apply {
|
||||||
|
setMethodCallHandler { call, result -> onMethodCall(call, result) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
when (call.method) {
|
||||||
|
"initialized" -> {
|
||||||
|
Log.d(LOG_TAG, "Analysis background channel is ready")
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
"updateNotification" -> {
|
||||||
|
val title = call.argument<String>("title")
|
||||||
|
val message = call.argument<String>("message")
|
||||||
|
setForegroundAsync(createForegroundInfo(title, message))
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
"stop" -> {
|
||||||
|
workCont?.resume(null)
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNotificationChannel() {
|
||||||
|
val channel = NotificationChannelCompat.Builder(NOTIFICATION_CHANNEL, NotificationManagerCompat.IMPORTANCE_LOW)
|
||||||
|
.setName(applicationContext.getText(R.string.analysis_channel_name))
|
||||||
|
.setShowBadge(false)
|
||||||
|
.build()
|
||||||
|
NotificationManagerCompat.from(applicationContext).createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createForegroundInfo(title: String? = null, message: String? = null): ForegroundInfo {
|
||||||
|
val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
} else {
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
}
|
||||||
|
val openAppIntent = Intent(applicationContext, MainActivity::class.java).let {
|
||||||
|
PendingIntent.getActivity(applicationContext, MainActivity.OPEN_FROM_ANALYSIS_SERVICE, it, pendingIntentFlags)
|
||||||
|
}
|
||||||
|
val stopAction = NotificationCompat.Action.Builder(
|
||||||
|
R.drawable.ic_outline_stop_24,
|
||||||
|
applicationContext.getString(R.string.analysis_notification_action_stop),
|
||||||
|
WorkManager.getInstance(applicationContext).createCancelPendingIntent(id)
|
||||||
|
).build()
|
||||||
|
val icon = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) R.drawable.ic_notification else R.mipmap.ic_launcher_round
|
||||||
|
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
|
||||||
|
.setContentTitle(title ?: applicationContext.getText(R.string.analysis_notification_default_title))
|
||||||
|
.setContentText(message)
|
||||||
|
.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
|
||||||
|
.setSmallIcon(icon)
|
||||||
|
.setContentIntent(openAppIntent)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
|
.addAction(stopAction)
|
||||||
|
.build()
|
||||||
|
return ForegroundInfo(NOTIFICATION_ID, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOG_TAG = LogUtils.createTag<AnalysisWorker>()
|
||||||
|
private const val BACKGROUND_CHANNEL = "deckers.thibault/aves/analysis_service_background"
|
||||||
|
const val SHARED_PREFERENCES_KEY = "analysis_service"
|
||||||
|
const val CALLBACK_HANDLE_KEY = "callback_handle"
|
||||||
|
|
||||||
|
const val NOTIFICATION_CHANNEL = "analysis"
|
||||||
|
const val NOTIFICATION_ID = 1
|
||||||
|
|
||||||
|
const val KEY_ENTRY_IDS = "entry_ids"
|
||||||
|
const val KEY_FORCE = "force"
|
||||||
|
}
|
||||||
|
}
|
|
@ -170,7 +170,6 @@ open class MainActivity : FlutterFragmentActivity() {
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
Log.i(LOG_TAG, "onStop")
|
Log.i(LOG_TAG, "onStop")
|
||||||
analysisHandler.detachFromActivity()
|
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,32 +1,30 @@
|
||||||
package deckers.thibault.aves.channel.calls
|
package deckers.thibault.aves.channel.calls
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import androidx.core.app.ComponentActivity
|
||||||
import android.content.ServiceConnection
|
import androidx.work.ExistingWorkPolicy
|
||||||
import android.os.Build
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
import android.os.IBinder
|
import androidx.work.WorkInfo
|
||||||
import android.util.Log
|
import androidx.work.WorkManager
|
||||||
import deckers.thibault.aves.AnalysisService
|
import androidx.work.workDataOf
|
||||||
import deckers.thibault.aves.AnalysisServiceBinder
|
import deckers.thibault.aves.AnalysisWorker
|
||||||
import deckers.thibault.aves.AnalysisServiceListener
|
import deckers.thibault.aves.utils.FlutterUtils
|
||||||
import deckers.thibault.aves.utils.ContextUtils.isMyServiceRunning
|
|
||||||
import deckers.thibault.aves.utils.LogUtils
|
|
||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class AnalysisHandler(private val activity: Activity, private val onAnalysisCompleted: () -> Unit) : MethodChannel.MethodCallHandler, AnalysisServiceListener {
|
|
||||||
|
class AnalysisHandler(private val activity: ComponentActivity, private val onAnalysisCompleted: () -> Unit) : MethodChannel.MethodCallHandler {
|
||||||
private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"registerCallback" -> ioScope.launch { Coresult.safe(call, result, ::registerCallback) }
|
"registerCallback" -> ioScope.launch { Coresult.safe(call, result, ::registerCallback) }
|
||||||
"startService" -> Coresult.safe(call, result, ::startAnalysis)
|
"startAnalysis" -> Coresult.safe(call, result, ::startAnalysis)
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,9 +36,9 @@ class AnalysisHandler(private val activity: Activity, private val onAnalysisComp
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
activity.getSharedPreferences(AnalysisService.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
activity.getSharedPreferences(AnalysisWorker.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
.putLong(AnalysisService.CALLBACK_HANDLE_KEY, callbackHandle)
|
.putLong(AnalysisWorker.CALLBACK_HANDLE_KEY, callbackHandle)
|
||||||
.apply()
|
.apply()
|
||||||
result.success(true)
|
result.success(true)
|
||||||
}
|
}
|
||||||
|
@ -55,20 +53,18 @@ class AnalysisHandler(private val activity: Activity, private val onAnalysisComp
|
||||||
// can be null or empty
|
// can be null or empty
|
||||||
val entryIds = call.argument<List<Int>>("entryIds")
|
val entryIds = call.argument<List<Int>>("entryIds")
|
||||||
|
|
||||||
if (!activity.isMyServiceRunning(AnalysisService::class.java)) {
|
WorkManager.getInstance(activity).enqueueUniqueWork(
|
||||||
val intent = Intent(activity, AnalysisService::class.java)
|
ANALYSIS_WORK_NAME,
|
||||||
.putExtra(AnalysisService.KEY_COMMAND, AnalysisService.COMMAND_START)
|
ExistingWorkPolicy.KEEP,
|
||||||
.putExtra(AnalysisService.KEY_ENTRY_IDS, entryIds?.toIntArray())
|
OneTimeWorkRequestBuilder<AnalysisWorker>().apply {
|
||||||
.putExtra(AnalysisService.KEY_FORCE, force)
|
setInputData(
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
workDataOf(
|
||||||
// Foreground services cannot start from background, but the service here may start fine
|
AnalysisWorker.KEY_ENTRY_IDS to entryIds?.toIntArray(),
|
||||||
// while the current lifecycle state (via `ProcessLifecycleOwner.get().lifecycle.currentState`)
|
AnalysisWorker.KEY_FORCE to force,
|
||||||
// is only `INITIALIZED`, so we should not preemptively return when the state is below `STARTED`.
|
)
|
||||||
activity.startForegroundService(intent)
|
)
|
||||||
} else {
|
}.build()
|
||||||
activity.startService(intent)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
attachToActivity()
|
attachToActivity()
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
@ -76,44 +72,22 @@ class AnalysisHandler(private val activity: Activity, private val onAnalysisComp
|
||||||
private var attached = false
|
private var attached = false
|
||||||
|
|
||||||
fun attachToActivity() {
|
fun attachToActivity() {
|
||||||
if (activity.isMyServiceRunning(AnalysisService::class.java)) {
|
if (!attached) {
|
||||||
val intent = Intent(activity, AnalysisService::class.java)
|
|
||||||
activity.bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
|
||||||
attached = true
|
attached = true
|
||||||
}
|
WorkManager.getInstance(activity).getWorkInfosForUniqueWorkLiveData(ANALYSIS_WORK_NAME).observe(activity) { list ->
|
||||||
}
|
if (list.any { it.state == WorkInfo.State.SUCCEEDED }) {
|
||||||
|
runBlocking {
|
||||||
override fun detachFromActivity() {
|
FlutterUtils.runOnUiThread {
|
||||||
if (attached) {
|
onAnalysisCompleted()
|
||||||
attached = false
|
}
|
||||||
activity.unbindService(connection)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun refreshApp() {
|
|
||||||
if (attached) {
|
|
||||||
onAnalysisCompleted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val connection = object : ServiceConnection {
|
|
||||||
var binder: AnalysisServiceBinder? = null
|
|
||||||
|
|
||||||
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
|
||||||
Log.i(LOG_TAG, "Analysis service connected")
|
|
||||||
binder = service as AnalysisServiceBinder
|
|
||||||
binder?.startListening(this@AnalysisHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceDisconnected(name: ComponentName) {
|
|
||||||
Log.i(LOG_TAG, "Analysis service disconnected")
|
|
||||||
binder?.stopListening(this@AnalysisHandler)
|
|
||||||
binder = null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG_TAG = LogUtils.createTag<AnalysisHandler>()
|
|
||||||
const val CHANNEL = "deckers.thibault/aves/analysis"
|
const val CHANNEL = "deckers.thibault/aves/analysis"
|
||||||
|
private const val ANALYSIS_WORK_NAME = "analysis_work"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
<string name="search_shortcut_short_label">البحث</string>
|
<string name="search_shortcut_short_label">البحث</string>
|
||||||
<string name="videos_shortcut_short_label">الفيديوهات</string>
|
<string name="videos_shortcut_short_label">الفيديوهات</string>
|
||||||
<string name="analysis_channel_name">فحص الوسائط</string>
|
<string name="analysis_channel_name">فحص الوسائط</string>
|
||||||
<string name="analysis_service_description">فحص الصور والفيديوهات</string>
|
|
||||||
<string name="analysis_notification_default_title">يتم فحص الوسائط</string>
|
<string name="analysis_notification_default_title">يتم فحص الوسائط</string>
|
||||||
<string name="analysis_notification_action_stop">إيقاف</string>
|
<string name="analysis_notification_action_stop">إيقاف</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">گەڕان</string>
|
<string name="search_shortcut_short_label">گەڕان</string>
|
||||||
<string name="videos_shortcut_short_label">ڤیدیۆ</string>
|
<string name="videos_shortcut_short_label">ڤیدیۆ</string>
|
||||||
<string name="analysis_channel_name">گەڕان بۆ فایل</string>
|
<string name="analysis_channel_name">گەڕان بۆ فایل</string>
|
||||||
<string name="analysis_service_description">گەڕان بۆ وێنە و ڤیدیۆ</string>
|
|
||||||
<string name="analysis_notification_default_title">گەڕان بۆ فایلەکان</string>
|
<string name="analysis_notification_default_title">گەڕان بۆ فایلەکان</string>
|
||||||
<string name="analysis_notification_action_stop">وەستاندن</string>
|
<string name="analysis_notification_action_stop">وەستاندن</string>
|
||||||
</resources>
|
</resources>
|
|
@ -5,7 +5,6 @@
|
||||||
<string name="search_shortcut_short_label">Hledat</string>
|
<string name="search_shortcut_short_label">Hledat</string>
|
||||||
<string name="videos_shortcut_short_label">Videa</string>
|
<string name="videos_shortcut_short_label">Videa</string>
|
||||||
<string name="analysis_channel_name">Prohledat média</string>
|
<string name="analysis_channel_name">Prohledat média</string>
|
||||||
<string name="analysis_service_description">Prohledat obrázky a videa</string>
|
|
||||||
<string name="analysis_notification_default_title">Prohledávání médií</string>
|
<string name="analysis_notification_default_title">Prohledávání médií</string>
|
||||||
<string name="analysis_notification_action_stop">Zastavit</string>
|
<string name="analysis_notification_action_stop">Zastavit</string>
|
||||||
<string name="app_widget_label">Fotorámeček</string>
|
<string name="app_widget_label">Fotorámeček</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Suche</string>
|
<string name="search_shortcut_short_label">Suche</string>
|
||||||
<string name="videos_shortcut_short_label">Videos</string>
|
<string name="videos_shortcut_short_label">Videos</string>
|
||||||
<string name="analysis_channel_name">Analyse von Medien</string>
|
<string name="analysis_channel_name">Analyse von Medien</string>
|
||||||
<string name="analysis_service_description">Bilder & Videos scannen</string>
|
|
||||||
<string name="analysis_notification_default_title">Medien scannen</string>
|
<string name="analysis_notification_default_title">Medien scannen</string>
|
||||||
<string name="analysis_notification_action_stop">Abbrechen</string>
|
<string name="analysis_notification_action_stop">Abbrechen</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Sicherer Modus</string>
|
<string name="safe_mode_shortcut_short_label">Sicherer Modus</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Αναζήτηση</string>
|
<string name="search_shortcut_short_label">Αναζήτηση</string>
|
||||||
<string name="videos_shortcut_short_label">Βίντεο</string>
|
<string name="videos_shortcut_short_label">Βίντεο</string>
|
||||||
<string name="analysis_channel_name">Σάρωση πολυμέσων</string>
|
<string name="analysis_channel_name">Σάρωση πολυμέσων</string>
|
||||||
<string name="analysis_service_description">Σάρωση εικόνων & Βίντεο</string>
|
|
||||||
<string name="analysis_notification_default_title">Σάρωση στοιχείων</string>
|
<string name="analysis_notification_default_title">Σάρωση στοιχείων</string>
|
||||||
<string name="analysis_notification_action_stop">Διακοπή</string>
|
<string name="analysis_notification_action_stop">Διακοπή</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Búsqueda</string>
|
<string name="search_shortcut_short_label">Búsqueda</string>
|
||||||
<string name="videos_shortcut_short_label">Vídeos</string>
|
<string name="videos_shortcut_short_label">Vídeos</string>
|
||||||
<string name="analysis_channel_name">Explorar medios</string>
|
<string name="analysis_channel_name">Explorar medios</string>
|
||||||
<string name="analysis_service_description">Explorar imágenes & videos</string>
|
|
||||||
<string name="analysis_notification_default_title">Explorando medios</string>
|
<string name="analysis_notification_default_title">Explorando medios</string>
|
||||||
<string name="analysis_notification_action_stop">Anular</string>
|
<string name="analysis_notification_action_stop">Anular</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Modo seguro</string>
|
<string name="safe_mode_shortcut_short_label">Modo seguro</string>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<string name="search_shortcut_short_label">Bilatu</string>
|
<string name="search_shortcut_short_label">Bilatu</string>
|
||||||
<string name="videos_shortcut_short_label">Bideoak</string>
|
<string name="videos_shortcut_short_label">Bideoak</string>
|
||||||
<string name="app_widget_label">Argazki-markoa</string>
|
<string name="app_widget_label">Argazki-markoa</string>
|
||||||
<string name="analysis_service_description">Irudiak eta bideoak eskaneatu</string>
|
|
||||||
<string name="wallpaper">Horma-papera</string>
|
<string name="wallpaper">Horma-papera</string>
|
||||||
<string name="analysis_channel_name">Media eskaneatu</string>
|
<string name="analysis_channel_name">Media eskaneatu</string>
|
||||||
<string name="analysis_notification_action_stop">Gelditu</string>
|
<string name="analysis_notification_action_stop">Gelditu</string>
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="videos_shortcut_short_label">ویدئو ها</string>
|
<string name="videos_shortcut_short_label">ویدئو ها</string>
|
||||||
<string name="analysis_channel_name">کنکاش رسانه</string>
|
<string name="analysis_channel_name">کنکاش رسانه</string>
|
||||||
<string name="analysis_service_description">کنکاش تصاویر و ویدئو ها</string>
|
|
||||||
<string name="search_shortcut_short_label">جستجو</string>
|
<string name="search_shortcut_short_label">جستجو</string>
|
||||||
<string name="wallpaper">کاغذدیواری</string>
|
<string name="wallpaper">کاغذدیواری</string>
|
||||||
<string name="analysis_notification_default_title">در حال کنکاش رسانهها</string>
|
<string name="analysis_notification_default_title">در حال کنکاش رسانهها</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Recherche</string>
|
<string name="search_shortcut_short_label">Recherche</string>
|
||||||
<string name="videos_shortcut_short_label">Vidéos</string>
|
<string name="videos_shortcut_short_label">Vidéos</string>
|
||||||
<string name="analysis_channel_name">Analyse des images</string>
|
<string name="analysis_channel_name">Analyse des images</string>
|
||||||
<string name="analysis_service_description">Analyse des images & vidéos</string>
|
|
||||||
<string name="analysis_notification_default_title">Analyse des images</string>
|
<string name="analysis_notification_default_title">Analyse des images</string>
|
||||||
<string name="analysis_notification_action_stop">Annuler</string>
|
<string name="analysis_notification_action_stop">Annuler</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Mode sans échec</string>
|
<string name="safe_mode_shortcut_short_label">Mode sans échec</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Procura</string>
|
<string name="search_shortcut_short_label">Procura</string>
|
||||||
<string name="videos_shortcut_short_label">Vídeos</string>
|
<string name="videos_shortcut_short_label">Vídeos</string>
|
||||||
<string name="analysis_channel_name">Escaneo multimedia</string>
|
<string name="analysis_channel_name">Escaneo multimedia</string>
|
||||||
<string name="analysis_service_description">Escanealas imaxes e os vídeos</string>
|
|
||||||
<string name="analysis_notification_default_title">Escaneando medios</string>
|
<string name="analysis_notification_default_title">Escaneando medios</string>
|
||||||
<string name="analysis_notification_action_stop">Pare</string>
|
<string name="analysis_notification_action_stop">Pare</string>
|
||||||
</resources>
|
</resources>
|
|
@ -8,5 +8,4 @@
|
||||||
<string name="analysis_channel_name">मीडिया जाँचे</string>
|
<string name="analysis_channel_name">मीडिया जाँचे</string>
|
||||||
<string name="app_name">ऐवीज</string>
|
<string name="app_name">ऐवीज</string>
|
||||||
<string name="videos_shortcut_short_label">वीडियो</string>
|
<string name="videos_shortcut_short_label">वीडियो</string>
|
||||||
<string name="analysis_service_description">छवि & वीडियो जाँचे</string>
|
|
||||||
</resources>
|
</resources>
|
|
@ -8,6 +8,5 @@
|
||||||
<string name="app_widget_label">Fotó keret</string>
|
<string name="app_widget_label">Fotó keret</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Biztonsági üzemmód</string>
|
<string name="safe_mode_shortcut_short_label">Biztonsági üzemmód</string>
|
||||||
<string name="analysis_channel_name">Tartalom keresése</string>
|
<string name="analysis_channel_name">Tartalom keresése</string>
|
||||||
<string name="analysis_service_description">Képek és videók keresése</string>
|
|
||||||
<string name="analysis_notification_default_title">Média beolvasása</string>
|
<string name="analysis_notification_default_title">Média beolvasása</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Cari</string>
|
<string name="search_shortcut_short_label">Cari</string>
|
||||||
<string name="videos_shortcut_short_label">Video</string>
|
<string name="videos_shortcut_short_label">Video</string>
|
||||||
<string name="analysis_channel_name">Pindai media</string>
|
<string name="analysis_channel_name">Pindai media</string>
|
||||||
<string name="analysis_service_description">Pindai gambar & video</string>
|
|
||||||
<string name="analysis_notification_default_title">Memindai media</string>
|
<string name="analysis_notification_default_title">Memindai media</string>
|
||||||
<string name="analysis_notification_action_stop">Berhenti</string>
|
<string name="analysis_notification_action_stop">Berhenti</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Mode aman</string>
|
<string name="safe_mode_shortcut_short_label">Mode aman</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Ricerca</string>
|
<string name="search_shortcut_short_label">Ricerca</string>
|
||||||
<string name="videos_shortcut_short_label">Video</string>
|
<string name="videos_shortcut_short_label">Video</string>
|
||||||
<string name="analysis_channel_name">Scansione media</string>
|
<string name="analysis_channel_name">Scansione media</string>
|
||||||
<string name="analysis_service_description">Scansione immagini & videos</string>
|
|
||||||
<string name="analysis_notification_default_title">Scansione in corso</string>
|
<string name="analysis_notification_default_title">Scansione in corso</string>
|
||||||
<string name="analysis_notification_action_stop">Annulla</string>
|
<string name="analysis_notification_action_stop">Annulla</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Modalità provvisoria</string>
|
<string name="safe_mode_shortcut_short_label">Modalità provvisoria</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">חיפוש</string>
|
<string name="search_shortcut_short_label">חיפוש</string>
|
||||||
<string name="videos_shortcut_short_label">סרטים</string>
|
<string name="videos_shortcut_short_label">סרטים</string>
|
||||||
<string name="analysis_channel_name">סריקת מדיה</string>
|
<string name="analysis_channel_name">סריקת מדיה</string>
|
||||||
<string name="analysis_service_description">סרוק תמונות וסרטים</string>
|
|
||||||
<string name="analysis_notification_default_title">סורק מדיה</string>
|
<string name="analysis_notification_default_title">סורק מדיה</string>
|
||||||
<string name="analysis_notification_action_stop">הפסק</string>
|
<string name="analysis_notification_action_stop">הפסק</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">検索</string>
|
<string name="search_shortcut_short_label">検索</string>
|
||||||
<string name="videos_shortcut_short_label">動画</string>
|
<string name="videos_shortcut_short_label">動画</string>
|
||||||
<string name="analysis_channel_name">メディアスキャン</string>
|
<string name="analysis_channel_name">メディアスキャン</string>
|
||||||
<string name="analysis_service_description">画像と動画をスキャン</string>
|
|
||||||
<string name="analysis_notification_default_title">メディアをスキャン中</string>
|
<string name="analysis_notification_default_title">メディアをスキャン中</string>
|
||||||
<string name="analysis_notification_action_stop">停止</string>
|
<string name="analysis_notification_action_stop">停止</string>
|
||||||
<string name="safe_mode_shortcut_short_label">セーフモード</string>
|
<string name="safe_mode_shortcut_short_label">セーフモード</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">검색</string>
|
<string name="search_shortcut_short_label">검색</string>
|
||||||
<string name="videos_shortcut_short_label">동영상</string>
|
<string name="videos_shortcut_short_label">동영상</string>
|
||||||
<string name="analysis_channel_name">미디어 분석</string>
|
<string name="analysis_channel_name">미디어 분석</string>
|
||||||
<string name="analysis_service_description">사진과 동영상 분석</string>
|
|
||||||
<string name="analysis_notification_default_title">미디어 분석</string>
|
<string name="analysis_notification_default_title">미디어 분석</string>
|
||||||
<string name="analysis_notification_action_stop">취소</string>
|
<string name="analysis_notification_action_stop">취소</string>
|
||||||
<string name="safe_mode_shortcut_short_label">안전 모드</string>
|
<string name="safe_mode_shortcut_short_label">안전 모드</string>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="analysis_service_description">Nuskaityti paveikslėlius ir vaizdo įrašus</string>
|
|
||||||
<string name="wallpaper">Ekrano paveikslėlis</string>
|
<string name="wallpaper">Ekrano paveikslėlis</string>
|
||||||
<string name="videos_shortcut_short_label">Vaizdo įrašai</string>
|
<string name="videos_shortcut_short_label">Vaizdo įrašai</string>
|
||||||
<string name="app_name">Aves</string>
|
<string name="app_name">Aves</string>
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<string name="app_name">Aves</string>
|
<string name="app_name">Aves</string>
|
||||||
<string name="videos_shortcut_short_label">Videoer</string>
|
<string name="videos_shortcut_short_label">Videoer</string>
|
||||||
<string name="analysis_channel_name">Mediaskanning</string>
|
<string name="analysis_channel_name">Mediaskanning</string>
|
||||||
<string name="analysis_service_description">Skann bilder og videoer</string>
|
|
||||||
<string name="analysis_notification_default_title">Skanning av media</string>
|
<string name="analysis_notification_default_title">Skanning av media</string>
|
||||||
<string name="app_widget_label">Bilderamme</string>
|
<string name="app_widget_label">Bilderamme</string>
|
||||||
<string name="wallpaper">Bakgrunnsbilde</string>
|
<string name="wallpaper">Bakgrunnsbilde</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Zoeken</string>
|
<string name="search_shortcut_short_label">Zoeken</string>
|
||||||
<string name="videos_shortcut_short_label">Video’s</string>
|
<string name="videos_shortcut_short_label">Video’s</string>
|
||||||
<string name="analysis_channel_name">Media indexeren</string>
|
<string name="analysis_channel_name">Media indexeren</string>
|
||||||
<string name="analysis_service_description">Indexeren van afdbeeldingen & video’s</string>
|
|
||||||
<string name="analysis_notification_default_title">Indexeren van media</string>
|
<string name="analysis_notification_default_title">Indexeren van media</string>
|
||||||
<string name="analysis_notification_action_stop">Stop</string>
|
<string name="analysis_notification_action_stop">Stop</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Søk</string>
|
<string name="search_shortcut_short_label">Søk</string>
|
||||||
<string name="videos_shortcut_short_label">Videoar</string>
|
<string name="videos_shortcut_short_label">Videoar</string>
|
||||||
<string name="analysis_channel_name">Mediasøking</string>
|
<string name="analysis_channel_name">Mediasøking</string>
|
||||||
<string name="analysis_service_description">Søk igjennom bilete og videoar</string>
|
|
||||||
<string name="analysis_notification_default_title">Søkjer igjennom media</string>
|
<string name="analysis_notification_default_title">Søkjer igjennom media</string>
|
||||||
<string name="analysis_notification_action_stop">Stogg</string>
|
<string name="analysis_notification_action_stop">Stogg</string>
|
||||||
</resources>
|
</resources>
|
|
@ -4,7 +4,6 @@
|
||||||
<string name="search_shortcut_short_label">Szukaj</string>
|
<string name="search_shortcut_short_label">Szukaj</string>
|
||||||
<string name="videos_shortcut_short_label">Wideo</string>
|
<string name="videos_shortcut_short_label">Wideo</string>
|
||||||
<string name="analysis_channel_name">Przeskanuj multimedia</string>
|
<string name="analysis_channel_name">Przeskanuj multimedia</string>
|
||||||
<string name="analysis_service_description">Przeskanuj obrazy oraz wideo</string>
|
|
||||||
<string name="analysis_notification_default_title">Skanowanie multimediów</string>
|
<string name="analysis_notification_default_title">Skanowanie multimediów</string>
|
||||||
<string name="analysis_notification_action_stop">Zatrzymaj</string>
|
<string name="analysis_notification_action_stop">Zatrzymaj</string>
|
||||||
<string name="app_name">Aves</string>
|
<string name="app_name">Aves</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Procurar</string>
|
<string name="search_shortcut_short_label">Procurar</string>
|
||||||
<string name="videos_shortcut_short_label">Vídeos</string>
|
<string name="videos_shortcut_short_label">Vídeos</string>
|
||||||
<string name="analysis_channel_name">Digitalização de mídia</string>
|
<string name="analysis_channel_name">Digitalização de mídia</string>
|
||||||
<string name="analysis_service_description">Digitalizar imagens & vídeos</string>
|
|
||||||
<string name="analysis_notification_default_title">Digitalizando mídia</string>
|
<string name="analysis_notification_default_title">Digitalizando mídia</string>
|
||||||
<string name="analysis_notification_action_stop">Pare</string>
|
<string name="analysis_notification_action_stop">Pare</string>
|
||||||
</resources>
|
</resources>
|
|
@ -5,7 +5,6 @@
|
||||||
<string name="wallpaper">Tapet</string>
|
<string name="wallpaper">Tapet</string>
|
||||||
<string name="videos_shortcut_short_label">Videoclipuri</string>
|
<string name="videos_shortcut_short_label">Videoclipuri</string>
|
||||||
<string name="analysis_channel_name">Scanare media</string>
|
<string name="analysis_channel_name">Scanare media</string>
|
||||||
<string name="analysis_service_description">Scanați imagini și videoclipuri</string>
|
|
||||||
<string name="analysis_notification_default_title">Scanarea suporturilor</string>
|
<string name="analysis_notification_default_title">Scanarea suporturilor</string>
|
||||||
<string name="analysis_notification_action_stop">Stop</string>
|
<string name="analysis_notification_action_stop">Stop</string>
|
||||||
<string name="search_shortcut_short_label">Căutare</string>
|
<string name="search_shortcut_short_label">Căutare</string>
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Поиск</string>
|
<string name="search_shortcut_short_label">Поиск</string>
|
||||||
<string name="videos_shortcut_short_label">Видео</string>
|
<string name="videos_shortcut_short_label">Видео</string>
|
||||||
<string name="analysis_channel_name">Сканировать медия</string>
|
<string name="analysis_channel_name">Сканировать медия</string>
|
||||||
<string name="analysis_service_description">Сканировать изображения и видео</string>
|
|
||||||
<string name="analysis_notification_default_title">Сканирование медиа</string>
|
<string name="analysis_notification_default_title">Сканирование медиа</string>
|
||||||
<string name="analysis_notification_action_stop">Стоп</string>
|
<string name="analysis_notification_action_stop">Стоп</string>
|
||||||
<string name="safe_mode_shortcut_short_label">Безопасный режим</string>
|
<string name="safe_mode_shortcut_short_label">Безопасный режим</string>
|
||||||
|
|
|
@ -7,6 +7,5 @@
|
||||||
<string name="videos_shortcut_short_label">Videá</string>
|
<string name="videos_shortcut_short_label">Videá</string>
|
||||||
<string name="analysis_notification_action_stop">Zastaviť</string>
|
<string name="analysis_notification_action_stop">Zastaviť</string>
|
||||||
<string name="analysis_channel_name">Skenovanie médií</string>
|
<string name="analysis_channel_name">Skenovanie médií</string>
|
||||||
<string name="analysis_service_description">Skenovanie obrázkov & videí</string>
|
|
||||||
<string name="analysis_notification_default_title">Skenovanie média</string>
|
<string name="analysis_notification_default_title">Skenovanie média</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">ค้นหา</string>
|
<string name="search_shortcut_short_label">ค้นหา</string>
|
||||||
<string name="videos_shortcut_short_label">วิดีโอ</string>
|
<string name="videos_shortcut_short_label">วิดีโอ</string>
|
||||||
<string name="analysis_channel_name">สแกนสื่อบันเทิง</string>
|
<string name="analysis_channel_name">สแกนสื่อบันเทิง</string>
|
||||||
<string name="analysis_service_description">สแกนรูปภาพและวิดีโอ</string>
|
|
||||||
<string name="analysis_notification_default_title">กำลังสแกนสื่อบันเทิง</string>
|
<string name="analysis_notification_default_title">กำลังสแกนสื่อบันเทิง</string>
|
||||||
<string name="analysis_notification_action_stop">หยุด</string>
|
<string name="analysis_notification_action_stop">หยุด</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">Arama</string>
|
<string name="search_shortcut_short_label">Arama</string>
|
||||||
<string name="videos_shortcut_short_label">Videolar</string>
|
<string name="videos_shortcut_short_label">Videolar</string>
|
||||||
<string name="analysis_channel_name">Medya tarama</string>
|
<string name="analysis_channel_name">Medya tarama</string>
|
||||||
<string name="analysis_service_description">Görüntüleri ve videoları tarayın</string>
|
|
||||||
<string name="analysis_notification_default_title">Medya taranıyor</string>
|
<string name="analysis_notification_default_title">Medya taranıyor</string>
|
||||||
<string name="analysis_notification_action_stop">Durdur</string>
|
<string name="analysis_notification_action_stop">Durdur</string>
|
||||||
</resources>
|
</resources>
|
|
@ -5,7 +5,6 @@
|
||||||
<string name="search_shortcut_short_label">Пошук</string>
|
<string name="search_shortcut_short_label">Пошук</string>
|
||||||
<string name="videos_shortcut_short_label">Відео</string>
|
<string name="videos_shortcut_short_label">Відео</string>
|
||||||
<string name="analysis_channel_name">Сканувати медіа</string>
|
<string name="analysis_channel_name">Сканувати медіа</string>
|
||||||
<string name="analysis_service_description">Сканувати зображення та відео</string>
|
|
||||||
<string name="analysis_notification_action_stop">Стоп</string>
|
<string name="analysis_notification_action_stop">Стоп</string>
|
||||||
<string name="app_widget_label">Фоторамка</string>
|
<string name="app_widget_label">Фоторамка</string>
|
||||||
<string name="analysis_notification_default_title">Сканування медіа</string>
|
<string name="analysis_notification_default_title">Сканування медіа</string>
|
||||||
|
|
|
@ -7,6 +7,5 @@
|
||||||
<string name="app_widget_label">相框</string>
|
<string name="app_widget_label">相框</string>
|
||||||
<string name="search_shortcut_short_label">搜尋</string>
|
<string name="search_shortcut_short_label">搜尋</string>
|
||||||
<string name="analysis_channel_name">媒體掃描</string>
|
<string name="analysis_channel_name">媒體掃描</string>
|
||||||
<string name="analysis_service_description">掃描圖片和影片</string>
|
|
||||||
<string name="analysis_notification_action_stop">停止</string>
|
<string name="analysis_notification_action_stop">停止</string>
|
||||||
</resources>
|
</resources>
|
|
@ -6,7 +6,6 @@
|
||||||
<string name="search_shortcut_short_label">搜索</string>
|
<string name="search_shortcut_short_label">搜索</string>
|
||||||
<string name="videos_shortcut_short_label">视频</string>
|
<string name="videos_shortcut_short_label">视频</string>
|
||||||
<string name="analysis_channel_name">媒体扫描</string>
|
<string name="analysis_channel_name">媒体扫描</string>
|
||||||
<string name="analysis_service_description">扫描图像 & 视频</string>
|
|
||||||
<string name="analysis_notification_default_title">正在扫描媒体库</string>
|
<string name="analysis_notification_default_title">正在扫描媒体库</string>
|
||||||
<string name="analysis_notification_action_stop">停止</string>
|
<string name="analysis_notification_action_stop">停止</string>
|
||||||
</resources>
|
</resources>
|
|
@ -7,7 +7,6 @@
|
||||||
<string name="search_shortcut_short_label">Search</string>
|
<string name="search_shortcut_short_label">Search</string>
|
||||||
<string name="videos_shortcut_short_label">Videos</string>
|
<string name="videos_shortcut_short_label">Videos</string>
|
||||||
<string name="analysis_channel_name">Media scan</string>
|
<string name="analysis_channel_name">Media scan</string>
|
||||||
<string name="analysis_service_description">Scan images & videos</string>
|
|
||||||
<string name="analysis_notification_default_title">Scanning media</string>
|
<string name="analysis_notification_default_title">Scanning media</string>
|
||||||
<string name="analysis_notification_action_stop">Stop</string>
|
<string name="analysis_notification_action_stop">Stop</string>
|
||||||
</resources>
|
</resources>
|
|
@ -1,6 +1,6 @@
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlin_version = '1.8.0'
|
kotlin_version = '1.8.21'
|
||||||
agp_version = '7.4.2'
|
agp_version = '7.4.2'
|
||||||
glide_version = '4.15.1'
|
glide_version = '4.15.1'
|
||||||
huawei_agconnect_version = '1.8.0.300'
|
huawei_agconnect_version = '1.8.0.300'
|
||||||
|
|
|
@ -31,7 +31,7 @@ class AnalysisService {
|
||||||
static Future<void> startService({required bool force, List<int>? entryIds}) async {
|
static Future<void> startService({required bool force, List<int>? entryIds}) async {
|
||||||
await reportService.log('Start analysis service${entryIds != null ? ' for ${entryIds.length} items' : ''}');
|
await reportService.log('Start analysis service${entryIds != null ? ' for ${entryIds.length} items' : ''}');
|
||||||
try {
|
try {
|
||||||
await _platform.invokeMethod('startService', <String, dynamic>{
|
await _platform.invokeMethod('startAnalysis', <String, dynamic>{
|
||||||
'entryIds': entryIds,
|
'entryIds': entryIds,
|
||||||
'force': force,
|
'force': force,
|
||||||
});
|
});
|
||||||
|
@ -155,7 +155,6 @@ class Analyzer {
|
||||||
|
|
||||||
void _onSourceStateChanged() {
|
void _onSourceStateChanged() {
|
||||||
if (_source.isReady) {
|
if (_source.isReady) {
|
||||||
_refreshApp();
|
|
||||||
_serviceStateNotifier.value = AnalyzerState.stopping;
|
_serviceStateNotifier.value = AnalyzerState.stopping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,14 +178,6 @@ class Analyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshApp() async {
|
|
||||||
try {
|
|
||||||
await _channel.invokeMethod('refreshApp');
|
|
||||||
} on PlatformException catch (e, stack) {
|
|
||||||
await reportService.recordError(e, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _stopPlatformService() async {
|
Future<void> _stopPlatformService() async {
|
||||||
try {
|
try {
|
||||||
await _channel.invokeMethod('stop');
|
await _channel.invokeMethod('stop');
|
||||||
|
|
|
@ -2,7 +2,7 @@ group 'deckers.thibault.aves.aves_screen_state'
|
||||||
version '1.0-SNAPSHOT'
|
version '1.0-SNAPSHOT'
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.8.0'
|
ext.kotlin_version = '1.8.21'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
Loading…
Reference in a new issue