Add android battery optimizations settings
This commit is contained in:
@@ -40,6 +40,7 @@ class MainActivity : FlutterFragmentActivity(), ServiceConnection.Callback {
|
|||||||
instance = this
|
instance = this
|
||||||
reconnect()
|
reconnect()
|
||||||
flutterEngine.plugins.add(MethodHandler())
|
flutterEngine.plugins.add(MethodHandler())
|
||||||
|
flutterEngine.plugins.add(PlatformSettingsHandler())
|
||||||
flutterEngine.plugins.add(EventHandler())
|
flutterEngine.plugins.add(EventHandler())
|
||||||
flutterEngine.plugins.add(LogHandler())
|
flutterEngine.plugins.add(LogHandler())
|
||||||
flutterEngine.plugins.add(GroupsChannel(lifecycleScope))
|
flutterEngine.plugins.add(GroupsChannel(lifecycleScope))
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.hiddify.hiddify
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||||
|
import io.flutter.plugin.common.MethodCall
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.PluginRegistry
|
||||||
|
import io.flutter.plugin.common.StandardMethodCodec
|
||||||
|
|
||||||
|
class PlatformSettingsHandler : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware,
|
||||||
|
PluginRegistry.ActivityResultListener {
|
||||||
|
private lateinit var channel: MethodChannel
|
||||||
|
private var activity: Activity? = null
|
||||||
|
private lateinit var ignoreRequestResult: MethodChannel.Result
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val channelName = "com.hiddify.app/platform.settings"
|
||||||
|
|
||||||
|
const val REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = 44
|
||||||
|
|
||||||
|
enum class Trigger(val method: String) {
|
||||||
|
IsIgnoringBatteryOptimizations("is_ignoring_battery_optimizations"),
|
||||||
|
RequestIgnoreBatteryOptimizations("request_ignore_battery_optimizations"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
val taskQueue = flutterPluginBinding.binaryMessenger.makeBackgroundTaskQueue()
|
||||||
|
channel = MethodChannel(
|
||||||
|
flutterPluginBinding.binaryMessenger,
|
||||||
|
channelName,
|
||||||
|
StandardMethodCodec.INSTANCE,
|
||||||
|
taskQueue
|
||||||
|
)
|
||||||
|
channel.setMethodCallHandler(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
|
channel.setMethodCallHandler(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||||
|
activity = binding.activity
|
||||||
|
binding.addActivityResultListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromActivityForConfigChanges() {
|
||||||
|
activity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||||
|
activity = binding.activity
|
||||||
|
binding.addActivityResultListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromActivity() {
|
||||||
|
activity = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
|
||||||
|
if (requestCode == REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) {
|
||||||
|
ignoreRequestResult.success(resultCode == Activity.RESULT_OK)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
when (call.method) {
|
||||||
|
Trigger.IsIgnoringBatteryOptimizations.method -> {
|
||||||
|
result.runCatching {
|
||||||
|
success(
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
Application.powerManager.isIgnoringBatteryOptimizations(Application.application.packageName)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Trigger.RequestIgnoreBatteryOptimizations.method -> {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return result.success(true)
|
||||||
|
}
|
||||||
|
val intent = Intent(
|
||||||
|
android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||||
|
Uri.parse("package:${Application.application.packageName}")
|
||||||
|
)
|
||||||
|
ignoreRequestResult = result
|
||||||
|
activity?.startActivityForResult(intent, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -106,7 +106,9 @@
|
|||||||
},
|
},
|
||||||
"trueBlack": "True Black",
|
"trueBlack": "True Black",
|
||||||
"silentStart": "Silent Start",
|
"silentStart": "Silent Start",
|
||||||
"openWorkingDir": "Open Working Directory"
|
"openWorkingDir": "Open Working Directory",
|
||||||
|
"ignoreBatteryOptimizations": "Ignore Battery Optimization",
|
||||||
|
"ignoreBatteryOptimizationsMsg": "Remove restrictions for VPN to work properly"
|
||||||
},
|
},
|
||||||
"advanced": {
|
"advanced": {
|
||||||
"sectionTitle": "Advanced",
|
"sectionTitle": "Advanced",
|
||||||
|
|||||||
@@ -106,7 +106,9 @@
|
|||||||
},
|
},
|
||||||
"trueBlack": "کاملا سیاه",
|
"trueBlack": "کاملا سیاه",
|
||||||
"silentStart": "اجرای ساکت",
|
"silentStart": "اجرای ساکت",
|
||||||
"openWorkingDir": "باز کردن دایرکتوری کاری"
|
"openWorkingDir": "باز کردن دایرکتوری کاری",
|
||||||
|
"ignoreBatteryOptimizations": "نادیدهگرفتن بهینهسازی باتری",
|
||||||
|
"ignoreBatteryOptimizationsMsg": "حذف محدودیتها برای عملکرد بهتر VPN"
|
||||||
},
|
},
|
||||||
"advanced": {
|
"advanced": {
|
||||||
"sectionTitle": "پیشرفته",
|
"sectionTitle": "پیشرفته",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class SettingsPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
SettingsSection(t.settings.general.sectionTitle),
|
SettingsSection(t.settings.general.sectionTitle),
|
||||||
const GeneralSettingTiles(),
|
const GeneralSettingTiles(),
|
||||||
|
const PlatformSettingsTiles(),
|
||||||
const SettingsDivider(),
|
const SettingsDivider(),
|
||||||
SettingsSection(t.settings.advanced.sectionTitle),
|
SettingsSection(t.settings.advanced.sectionTitle),
|
||||||
const AdvancedSettingTiles(),
|
const AdvancedSettingTiles(),
|
||||||
|
|||||||
58
lib/features/settings/widgets/platform_settings_tiles.dart
Normal file
58
lib/features/settings/widgets/platform_settings_tiles.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hiddify/core/core_providers.dart';
|
||||||
|
import 'package:hiddify/services/service_providers.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'platform_settings_tiles.g.dart';
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<bool> isIgnoringBatteryOptimizations(
|
||||||
|
IsIgnoringBatteryOptimizationsRef ref,
|
||||||
|
) async =>
|
||||||
|
ref
|
||||||
|
.watch(platformSettingsProvider)
|
||||||
|
.isIgnoringBatteryOptimizations()
|
||||||
|
.getOrElse((l) => false)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
class PlatformSettingsTiles extends HookConsumerWidget {
|
||||||
|
const PlatformSettingsTiles({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final t = ref.watch(translationsProvider);
|
||||||
|
|
||||||
|
final isIgnoringBatteryOptimizations =
|
||||||
|
ref.watch(isIgnoringBatteryOptimizationsProvider);
|
||||||
|
|
||||||
|
ListTile buildIgnoreTile(bool enabled) => ListTile(
|
||||||
|
title: Text(t.settings.general.ignoreBatteryOptimizations),
|
||||||
|
subtitle: Text(t.settings.general.ignoreBatteryOptimizationsMsg),
|
||||||
|
leading: const Icon(Icons.running_with_errors),
|
||||||
|
enabled: enabled,
|
||||||
|
onTap: () async {
|
||||||
|
await ref
|
||||||
|
.read(platformSettingsProvider)
|
||||||
|
.requestIgnoreBatteryOptimizations()
|
||||||
|
.run();
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
ref.invalidate(isIgnoringBatteryOptimizationsProvider);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
if (Platform.isAndroid)
|
||||||
|
switch (isIgnoringBatteryOptimizations) {
|
||||||
|
AsyncData(:final value) when value == false =>
|
||||||
|
buildIgnoreTile(true),
|
||||||
|
AsyncData(:final value) when value == true => const SizedBox(),
|
||||||
|
_ => buildIgnoreTile(false),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export 'advanced_setting_tiles.dart';
|
export 'advanced_setting_tiles.dart';
|
||||||
export 'general_setting_tiles.dart';
|
export 'general_setting_tiles.dart';
|
||||||
|
export 'platform_settings_tiles.dart';
|
||||||
export 'sections_widgets.dart';
|
export 'sections_widgets.dart';
|
||||||
export 'settings_input_dialog.dart';
|
export 'settings_input_dialog.dart';
|
||||||
|
|||||||
32
lib/services/platform_settings.dart
Normal file
32
lib/services/platform_settings.dart
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:hiddify/utils/utils.dart';
|
||||||
|
|
||||||
|
class PlatformSettings with InfraLogger {
|
||||||
|
late final MethodChannel _methodChannel =
|
||||||
|
const MethodChannel("com.hiddify.app/platform.settings");
|
||||||
|
|
||||||
|
TaskEither<String, bool> isIgnoringBatteryOptimizations() {
|
||||||
|
return TaskEither(
|
||||||
|
() async {
|
||||||
|
loggy.debug("checking battery optimization status");
|
||||||
|
final result = await _methodChannel
|
||||||
|
.invokeMethod<bool>("is_ignoring_battery_optimizations");
|
||||||
|
loggy.debug("is ignoring battery optimizations? [$result]");
|
||||||
|
return right(result!);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskEither<String, bool> requestIgnoreBatteryOptimizations() {
|
||||||
|
return TaskEither(
|
||||||
|
() async {
|
||||||
|
loggy.debug("requesting ignore battery optimization");
|
||||||
|
final result = await _methodChannel
|
||||||
|
.invokeMethod<bool>("request_ignore_battery_optimizations");
|
||||||
|
loggy.debug("ignore battery optimization result: [$result]");
|
||||||
|
return right(result!);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:hiddify/services/connectivity/connectivity.dart';
|
import 'package:hiddify/services/connectivity/connectivity.dart';
|
||||||
import 'package:hiddify/services/files_editor_service.dart';
|
import 'package:hiddify/services/files_editor_service.dart';
|
||||||
import 'package:hiddify/services/notification/notification.dart';
|
import 'package:hiddify/services/notification/notification.dart';
|
||||||
|
import 'package:hiddify/services/platform_settings.dart';
|
||||||
import 'package:hiddify/services/runtime_details_service.dart';
|
import 'package:hiddify/services/runtime_details_service.dart';
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
import 'package:hiddify/services/singbox/singbox_service.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -28,3 +29,7 @@ ConnectivityService connectivityService(ConnectivityServiceRef ref) =>
|
|||||||
ref.watch(singboxServiceProvider),
|
ref.watch(singboxServiceProvider),
|
||||||
ref.watch(notificationServiceProvider),
|
ref.watch(notificationServiceProvider),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
PlatformSettings platformSettings(PlatformSettingsRef ref) =>
|
||||||
|
PlatformSettings();
|
||||||
|
|||||||
Reference in New Issue
Block a user