mirror of
https://github.com/immich-app/immich.git
synced 2026-03-22 11:19:37 +03:00
feat: show notification and battery optimization warning
This commit is contained in:
@@ -689,6 +689,7 @@
|
|||||||
"backup_settings_subtitle": "Manage upload settings",
|
"backup_settings_subtitle": "Manage upload settings",
|
||||||
"backup_upload_details_page_more_details": "Tap for more details",
|
"backup_upload_details_page_more_details": "Tap for more details",
|
||||||
"backward": "Backward",
|
"backward": "Backward",
|
||||||
|
"battery_optimization_backup_reliability": "Disabling battery optimizations can improve the reliability of background backup",
|
||||||
"biometric_auth_enabled": "Biometric authentication enabled",
|
"biometric_auth_enabled": "Biometric authentication enabled",
|
||||||
"biometric_locked_out": "You are locked out of biometric authentication",
|
"biometric_locked_out": "You are locked out of biometric authentication",
|
||||||
"biometric_no_options": "No biometric options available",
|
"biometric_no_options": "No biometric options available",
|
||||||
@@ -1623,6 +1624,7 @@
|
|||||||
"not_selected": "Not selected",
|
"not_selected": "Not selected",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"nothing_here_yet": "Nothing here yet",
|
"nothing_here_yet": "Nothing here yet",
|
||||||
|
"notification_backup_reliability": "Enable notifications to improve background backup reliability",
|
||||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import app.alextran.immich.images.LocalImageApi
|
|||||||
import app.alextran.immich.images.LocalImagesImpl
|
import app.alextran.immich.images.LocalImagesImpl
|
||||||
import app.alextran.immich.images.RemoteImageApi
|
import app.alextran.immich.images.RemoteImageApi
|
||||||
import app.alextran.immich.images.RemoteImagesImpl
|
import app.alextran.immich.images.RemoteImagesImpl
|
||||||
|
import app.alextran.immich.permission.PermissionApi
|
||||||
|
import app.alextran.immich.permission.PermissionApiImpl
|
||||||
import app.alextran.immich.sync.NativeSyncApi
|
import app.alextran.immich.sync.NativeSyncApi
|
||||||
import app.alextran.immich.sync.NativeSyncApiImpl26
|
import app.alextran.immich.sync.NativeSyncApiImpl26
|
||||||
import app.alextran.immich.sync.NativeSyncApiImpl30
|
import app.alextran.immich.sync.NativeSyncApiImpl30
|
||||||
@@ -48,6 +50,7 @@ class MainActivity : FlutterFragmentActivity() {
|
|||||||
|
|
||||||
BackgroundWorkerFgHostApi.setUp(messenger, BackgroundWorkerApiImpl(ctx))
|
BackgroundWorkerFgHostApi.setUp(messenger, BackgroundWorkerApiImpl(ctx))
|
||||||
ConnectivityApi.setUp(messenger, ConnectivityApiImpl(ctx))
|
ConnectivityApi.setUp(messenger, ConnectivityApiImpl(ctx))
|
||||||
|
PermissionApi.setUp(messenger, PermissionApiImpl(ctx))
|
||||||
|
|
||||||
flutterEngine.plugins.add(BackgroundServicePlugin())
|
flutterEngine.plugins.add(BackgroundServicePlugin())
|
||||||
flutterEngine.plugins.add(backgroundEngineLockImpl)
|
flutterEngine.plugins.add(backgroundEngineLockImpl)
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
// Autogenerated from Pigeon (v26.0.2), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
|
||||||
|
|
||||||
|
package app.alextran.immich.permission
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.flutter.plugin.common.BasicMessageChannel
|
||||||
|
import io.flutter.plugin.common.BinaryMessenger
|
||||||
|
import io.flutter.plugin.common.EventChannel
|
||||||
|
import io.flutter.plugin.common.MessageCodec
|
||||||
|
import io.flutter.plugin.common.StandardMethodCodec
|
||||||
|
import io.flutter.plugin.common.StandardMessageCodec
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
private object PermissionApiPigeonUtils {
|
||||||
|
|
||||||
|
fun wrapResult(result: Any?): List<Any?> {
|
||||||
|
return listOf(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun wrapError(exception: Throwable): List<Any?> {
|
||||||
|
return if (exception is FlutterError) {
|
||||||
|
listOf(
|
||||||
|
exception.code,
|
||||||
|
exception.message,
|
||||||
|
exception.details
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
listOf(
|
||||||
|
exception.javaClass.simpleName,
|
||||||
|
exception.toString(),
|
||||||
|
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error class for passing custom error details to Flutter via a thrown PlatformException.
|
||||||
|
* @property code The error code.
|
||||||
|
* @property message The error message.
|
||||||
|
* @property details The error details. Must be a datatype supported by the api codec.
|
||||||
|
*/
|
||||||
|
class FlutterError (
|
||||||
|
val code: String,
|
||||||
|
override val message: String? = null,
|
||||||
|
val details: Any? = null
|
||||||
|
) : Throwable()
|
||||||
|
|
||||||
|
enum class PermissionStatus(val raw: Int) {
|
||||||
|
GRANTED(0),
|
||||||
|
DENIED(1),
|
||||||
|
PERMANENTLY_DENIED(2);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun ofRaw(raw: Int): PermissionStatus? {
|
||||||
|
return values().firstOrNull { it.raw == raw }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private open class PermissionApiPigeonCodec : StandardMessageCodec() {
|
||||||
|
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||||
|
return when (type) {
|
||||||
|
129.toByte() -> {
|
||||||
|
return (readValue(buffer) as Long?)?.let {
|
||||||
|
PermissionStatus.ofRaw(it.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> super.readValueOfType(type, buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
|
||||||
|
when (value) {
|
||||||
|
is PermissionStatus -> {
|
||||||
|
stream.write(129)
|
||||||
|
writeValue(stream, value.raw)
|
||||||
|
}
|
||||||
|
else -> super.writeValue(stream, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
|
interface PermissionApi {
|
||||||
|
fun isIgnoringBatteryOptimizations(): PermissionStatus
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** The codec used by PermissionApi. */
|
||||||
|
val codec: MessageCodec<Any?> by lazy {
|
||||||
|
PermissionApiPigeonCodec()
|
||||||
|
}
|
||||||
|
/** Sets up an instance of `PermissionApi` to handle messages through the `binaryMessenger`. */
|
||||||
|
@JvmOverloads
|
||||||
|
fun setUp(binaryMessenger: BinaryMessenger, api: PermissionApi?, messageChannelSuffix: String = "") {
|
||||||
|
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.immich_mobile.PermissionApi.isIgnoringBatteryOptimizations$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
listOf(api.isIgnoringBatteryOptimizations())
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
PermissionApiPigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package app.alextran.immich.permission
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.PowerManager
|
||||||
|
|
||||||
|
class PermissionApiImpl(context: Context) : PermissionApi {
|
||||||
|
private val ctx: Context = context.applicationContext
|
||||||
|
|
||||||
|
private val powerManager =
|
||||||
|
ctx.getSystemService(Context.POWER_SERVICE) as PowerManager
|
||||||
|
|
||||||
|
|
||||||
|
override fun isIgnoringBatteryOptimizations(): PermissionStatus {
|
||||||
|
if (powerManager.isIgnoringBatteryOptimizations(ctx.packageName)) {
|
||||||
|
return PermissionStatus.GRANTED
|
||||||
|
}
|
||||||
|
return PermissionStatus.DENIED
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,18 +8,25 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
|||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/generated/translations.g.dart';
|
import 'package:immich_mobile/generated/translations.g.dart';
|
||||||
|
import 'package:immich_mobile/platform/permission_api.g.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/backup/backup_toggle_button.widget.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup_album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/store.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/notification_permission.provider.dart';
|
||||||
import 'package:immich_mobile/providers/sync_status.provider.dart';
|
import 'package:immich_mobile/providers/sync_status.provider.dart';
|
||||||
import 'package:immich_mobile/providers/user.provider.dart';
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/backup/backup_info_card.dart';
|
import 'package:immich_mobile/widgets/backup/backup_info_card.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart' as pm;
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
@@ -161,11 +168,7 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
TextButton.icon(
|
const _BackupFooter(),
|
||||||
icon: const Icon(Icons.info_outline_rounded),
|
|
||||||
onPressed: () => context.pushRoute(const DriftUploadDetailRoute()),
|
|
||||||
label: Text("view_details".t(context: context)),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -176,6 +179,130 @@ class _DriftBackupPageState extends ConsumerState<DriftBackupPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _BackupFooter extends ConsumerStatefulWidget {
|
||||||
|
const _BackupFooter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<_BackupFooter> createState() => _BackupFooterState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BackupFooterState extends ConsumerState<_BackupFooter> with WidgetsBindingObserver {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
if (CurrentPlatform.isAndroid && state == AppLifecycleState.resumed && mounted) {
|
||||||
|
unawaited(ref.read(notificationPermissionProvider.notifier).getNotificationPermission());
|
||||||
|
unawaited(ref.read(_batteryOptimizationProvider.notifier).getBatteryOptimizationPermission());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showPermissionsDialog() {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
content: Text(context.t.notification_permission_dialog_content),
|
||||||
|
actions: [
|
||||||
|
TextButton(child: Text(context.t.cancel), onPressed: () => ctx.pop()),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
context.pop();
|
||||||
|
pm.openAppSettings();
|
||||||
|
},
|
||||||
|
child: Text(context.t.settings),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showBatteryOptimizationInfo() {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext ctx) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(context.t.backup_controller_page_background_battery_info_title),
|
||||||
|
content: SingleChildScrollView(child: Text(context.t.backup_controller_page_background_battery_info_message)),
|
||||||
|
actions: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => launchUrl(Uri.parse('https://dontkillmyapp.com'), mode: LaunchMode.externalApplication),
|
||||||
|
child: Text(
|
||||||
|
context.t.backup_controller_page_background_battery_info_link,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
child: Text(
|
||||||
|
context.t.backup_controller_page_background_battery_info_ok,
|
||||||
|
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
|
||||||
|
),
|
||||||
|
onPressed: () => ctx.pop(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isBackupEnabled = ref.watch(_backupStatusProvider).valueOrNull ?? false;
|
||||||
|
final notificationStatus = ref.watch(notificationPermissionProvider);
|
||||||
|
final batteryOptimizationStatus = ref.watch(_batteryOptimizationProvider).valueOrNull;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (CurrentPlatform.isAndroid && isBackupEnabled) ...[
|
||||||
|
if (notificationStatus != pm.PermissionStatus.granted)
|
||||||
|
TextButton.icon(
|
||||||
|
icon: Icon(Icons.open_in_new_outlined, color: context.colorScheme.onSurfaceSecondary),
|
||||||
|
label: Text(
|
||||||
|
context.t.notification_backup_reliability,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(notificationPermissionProvider.notifier).requestNotificationPermission().then((p) {
|
||||||
|
if (p == pm.PermissionStatus.permanentlyDenied) {
|
||||||
|
showPermissionsDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// Show only after notification permission is granted
|
||||||
|
if (notificationStatus == pm.PermissionStatus.granted &&
|
||||||
|
batteryOptimizationStatus != pm.PermissionStatus.granted)
|
||||||
|
TextButton.icon(
|
||||||
|
icon: Icon(Icons.open_in_new_outlined, color: context.colorScheme.onSurfaceSecondary),
|
||||||
|
label: Text(
|
||||||
|
context.t.battery_optimization_backup_reliability,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary),
|
||||||
|
),
|
||||||
|
onPressed: showBatteryOptimizationInfo,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
TextButton.icon(
|
||||||
|
icon: const Icon(Icons.info_outline_rounded),
|
||||||
|
onPressed: () => context.pushRoute(const DriftUploadDetailRoute()),
|
||||||
|
label: Text(context.t.view_details),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _BackupAlbumSelectionCard extends ConsumerWidget {
|
class _BackupAlbumSelectionCard extends ConsumerWidget {
|
||||||
const _BackupAlbumSelectionCard();
|
const _BackupAlbumSelectionCard();
|
||||||
|
|
||||||
@@ -521,3 +648,28 @@ class _PreparingStatusState extends ConsumerState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final _backupStatusProvider = StreamProvider.autoDispose<bool?>((ref) async* {
|
||||||
|
yield* ref.watch(storeServiceProvider).watch(StoreKey.enableBackup);
|
||||||
|
});
|
||||||
|
|
||||||
|
final _batteryOptimizationProvider = AsyncNotifierProvider<_BatteryOptimizationNotifier, pm.PermissionStatus>(
|
||||||
|
_BatteryOptimizationNotifier.new,
|
||||||
|
);
|
||||||
|
|
||||||
|
class _BatteryOptimizationNotifier extends AsyncNotifier<pm.PermissionStatus> {
|
||||||
|
Future<pm.PermissionStatus> getBatteryOptimizationPermission() async {
|
||||||
|
final pm.PermissionStatus status;
|
||||||
|
final isIgnoring = await ref.read(permissionApiProvider).isIgnoringBatteryOptimizations();
|
||||||
|
if (isIgnoring == PermissionStatus.granted) {
|
||||||
|
status = pm.PermissionStatus.granted;
|
||||||
|
} else {
|
||||||
|
status = pm.PermissionStatus.denied;
|
||||||
|
}
|
||||||
|
state = AsyncValue.data(status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<pm.PermissionStatus> build() => getBatteryOptimizationPermission();
|
||||||
|
}
|
||||||
|
|||||||
87
mobile/lib/platform/permission_api.g.dart
generated
Normal file
87
mobile/lib/platform/permission_api.g.dart
generated
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// Autogenerated from Pigeon (v26.0.2), do not edit directly.
|
||||||
|
// See also: https://pub.dev/packages/pigeon
|
||||||
|
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
PlatformException _createConnectionError(String channelName) {
|
||||||
|
return PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel: "$channelName".',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PermissionStatus { granted, denied, permanentlyDenied }
|
||||||
|
|
||||||
|
class _PigeonCodec extends StandardMessageCodec {
|
||||||
|
const _PigeonCodec();
|
||||||
|
@override
|
||||||
|
void writeValue(WriteBuffer buffer, Object? value) {
|
||||||
|
if (value is int) {
|
||||||
|
buffer.putUint8(4);
|
||||||
|
buffer.putInt64(value);
|
||||||
|
} else if (value is PermissionStatus) {
|
||||||
|
buffer.putUint8(129);
|
||||||
|
writeValue(buffer, value.index);
|
||||||
|
} else {
|
||||||
|
super.writeValue(buffer, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||||
|
switch (type) {
|
||||||
|
case 129:
|
||||||
|
final int? value = readValue(buffer) as int?;
|
||||||
|
return value == null ? null : PermissionStatus.values[value];
|
||||||
|
default:
|
||||||
|
return super.readValueOfType(type, buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermissionApi {
|
||||||
|
/// Constructor for [PermissionApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
PermissionApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||||
|
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||||
|
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||||
|
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||||
|
|
||||||
|
final String pigeonVar_messageChannelSuffix;
|
||||||
|
|
||||||
|
Future<PermissionStatus> isIgnoringBatteryOptimizations() async {
|
||||||
|
final String pigeonVar_channelName =
|
||||||
|
'dev.flutter.pigeon.immich_mobile.PermissionApi.isIgnoringBatteryOptimizations$pigeonVar_messageChannelSuffix';
|
||||||
|
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||||
|
final List<Object?>? pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else if (pigeonVar_replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (pigeonVar_replyList[0] as PermissionStatus?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,10 @@ import 'package:immich_mobile/domain/services/background_worker.service.dart';
|
|||||||
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
import 'package:immich_mobile/platform/background_worker_api.g.dart';
|
||||||
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
import 'package:immich_mobile/platform/background_worker_lock_api.g.dart';
|
||||||
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
import 'package:immich_mobile/platform/connectivity_api.g.dart';
|
||||||
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
|
||||||
import 'package:immich_mobile/platform/local_image_api.g.dart';
|
import 'package:immich_mobile/platform/local_image_api.g.dart';
|
||||||
|
import 'package:immich_mobile/platform/native_sync_api.g.dart';
|
||||||
import 'package:immich_mobile/platform/network_api.g.dart';
|
import 'package:immich_mobile/platform/network_api.g.dart';
|
||||||
|
import 'package:immich_mobile/platform/permission_api.g.dart';
|
||||||
import 'package:immich_mobile/platform/remote_image_api.g.dart';
|
import 'package:immich_mobile/platform/remote_image_api.g.dart';
|
||||||
|
|
||||||
final backgroundWorkerFgServiceProvider = Provider((_) => BackgroundWorkerFgService(BackgroundWorkerFgHostApi()));
|
final backgroundWorkerFgServiceProvider = Provider((_) => BackgroundWorkerFgService(BackgroundWorkerFgHostApi()));
|
||||||
@@ -18,6 +19,8 @@ final nativeSyncApiProvider = Provider<NativeSyncApi>((_) => NativeSyncApi());
|
|||||||
|
|
||||||
final connectivityApiProvider = Provider<ConnectivityApi>((_) => ConnectivityApi());
|
final connectivityApiProvider = Provider<ConnectivityApi>((_) => ConnectivityApi());
|
||||||
|
|
||||||
|
final permissionApiProvider = Provider<PermissionApi>((_) => PermissionApi());
|
||||||
|
|
||||||
final localImageApi = LocalImageApi();
|
final localImageApi = LocalImageApi();
|
||||||
|
|
||||||
final remoteImageApi = RemoteImageApi();
|
final remoteImageApi = RemoteImageApi();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ pigeon:
|
|||||||
dart run pigeon --input pigeon/background_worker_lock_api.dart
|
dart run pigeon --input pigeon/background_worker_lock_api.dart
|
||||||
dart run pigeon --input pigeon/connectivity_api.dart
|
dart run pigeon --input pigeon/connectivity_api.dart
|
||||||
dart run pigeon --input pigeon/network_api.dart
|
dart run pigeon --input pigeon/network_api.dart
|
||||||
|
dart run pigeon --input pigeon/permission_api.dart
|
||||||
dart format lib/platform/native_sync_api.g.dart
|
dart format lib/platform/native_sync_api.g.dart
|
||||||
dart format lib/platform/local_image_api.g.dart
|
dart format lib/platform/local_image_api.g.dart
|
||||||
dart format lib/platform/remote_image_api.g.dart
|
dart format lib/platform/remote_image_api.g.dart
|
||||||
@@ -20,6 +21,7 @@ pigeon:
|
|||||||
dart format lib/platform/background_worker_lock_api.g.dart
|
dart format lib/platform/background_worker_lock_api.g.dart
|
||||||
dart format lib/platform/connectivity_api.g.dart
|
dart format lib/platform/connectivity_api.g.dart
|
||||||
dart format lib/platform/network_api.g.dart
|
dart format lib/platform/network_api.g.dart
|
||||||
|
dart format lib/platform/permission_api.g.dart
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
dart run build_runner watch --delete-conflicting-outputs
|
dart run build_runner watch --delete-conflicting-outputs
|
||||||
|
|||||||
@@ -40,7 +40,13 @@ depends = [
|
|||||||
[tasks."codegen:translation"]
|
[tasks."codegen:translation"]
|
||||||
alias = "translation"
|
alias = "translation"
|
||||||
description = "Generate translations from i18n JSONs"
|
description = "Generate translations from i18n JSONs"
|
||||||
run = [{ task = "//:i18n:format-fix" }, { tasks = ["i18n:loader", "i18n:keys"] }]
|
run = [
|
||||||
|
{ task = "//:i18n:format-fix" },
|
||||||
|
{ tasks = [
|
||||||
|
"i18n:loader",
|
||||||
|
"i18n:keys",
|
||||||
|
] },
|
||||||
|
]
|
||||||
|
|
||||||
[tasks."codegen:app-icon"]
|
[tasks."codegen:app-icon"]
|
||||||
description = "Generate app icons"
|
description = "Generate app icons"
|
||||||
@@ -146,6 +152,19 @@ run = [
|
|||||||
"dart format lib/platform/connectivity_api.g.dart",
|
"dart format lib/platform/connectivity_api.g.dart",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tasks."pigeon:permission"]
|
||||||
|
description = "Generate permission API pigeon code"
|
||||||
|
hide = true
|
||||||
|
sources = ["pigeon/permission_api.dart"]
|
||||||
|
outputs = [
|
||||||
|
"lib/platform/permission_api.g.dart",
|
||||||
|
"android/app/src/main/kotlin/app/alextran/immich/permission/PermissionApi.g.kt",
|
||||||
|
]
|
||||||
|
run = [
|
||||||
|
"dart run pigeon --input pigeon/permission_api.dart",
|
||||||
|
"dart format lib/platform/permission_api.g.dart",
|
||||||
|
]
|
||||||
|
|
||||||
[tasks."i18n:loader"]
|
[tasks."i18n:loader"]
|
||||||
description = "Generate i18n loader"
|
description = "Generate i18n loader"
|
||||||
hide = true
|
hide = true
|
||||||
|
|||||||
17
mobile/pigeon/permission_api.dart
Normal file
17
mobile/pigeon/permission_api.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:pigeon/pigeon.dart';
|
||||||
|
|
||||||
|
enum PermissionStatus { granted, denied, permanentlyDenied }
|
||||||
|
|
||||||
|
@ConfigurePigeon(
|
||||||
|
PigeonOptions(
|
||||||
|
dartOut: 'lib/platform/permission_api.g.dart',
|
||||||
|
kotlinOut: 'android/app/src/main/kotlin/app/alextran/immich/permission/PermissionApi.g.kt',
|
||||||
|
kotlinOptions: KotlinOptions(package: 'app.alextran.immich.permission'),
|
||||||
|
dartOptions: DartOptions(),
|
||||||
|
dartPackageName: 'immich_mobile',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
@HostApi()
|
||||||
|
abstract class PermissionApi {
|
||||||
|
PermissionStatus isIgnoringBatteryOptimizations();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user