split other platform handlers to separate files

This commit is contained in:
Vadim 2025-05-11 12:48:10 +02:00
parent 56ac6fe558
commit 4c689db8ee
12 changed files with 226 additions and 147 deletions

View file

@ -2,23 +2,17 @@ package com.vodemn.lightmeter
import android.os.Bundle
import android.view.KeyEvent
import android.view.WindowManager
import androidx.core.view.WindowCompat
import com.vodemn.lightmeter.PlatformChannels.CaffeinePlatformChannel
import com.vodemn.lightmeter.PlatformChannels.CameraInfoPlatformChannel
import com.vodemn.lightmeter.PlatformChannels.VolumePlatformChannel
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private lateinit var keepScreenOnChannel: MethodChannel
private lateinit var volumeHandlingChannel: MethodChannel
private lateinit var volumeEventChannel: EventChannel
private var volumeEventsEmitter: EventSink? = null
private var handleVolume = false
private val caffeinePlatformChannel = CaffeinePlatformChannel()
private val cameraInfoPlatformChannel = CameraInfoPlatformChannel()
private val volumePlatformChannel = VolumePlatformChannel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -27,75 +21,24 @@ class MainActivity : FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
cameraInfoPlatformChannel.onAttachedToEngine(flutterEngine.dartExecutor.binaryMessenger, context)
keepScreenOnChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.vodemn.lightmeter/keepScreenOn"
)
keepScreenOnChannel.setMethodCallHandler { call, result ->
when (call.method) {
"isKeepScreenOn" -> result.success((window.attributes.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0)
"setKeepScreenOn" -> {
if (call.arguments !is Boolean) {
result.error("invalid args", "Argument should be of type Bool for 'setKeepScreenOn' call", null)
} else {
if (call.arguments as Boolean) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
result.success(true)
}
}
else -> result.notImplemented()
}
}
volumeHandlingChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.vodemn.lightmeter/volumeHandling"
)
volumeHandlingChannel.setMethodCallHandler { call, result ->
when (call.method) {
"setVolumeHandling" -> {
handleVolume = call.arguments as Boolean
result.success(handleVolume)
}
else -> result.notImplemented()
}
}
volumeEventChannel = EventChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.vodemn.lightmeter/volumeEvents"
)
volumeEventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(listener: Any?, eventSink: EventSink) {
volumeEventsEmitter = eventSink
}
override fun onCancel(listener: Any?) {
volumeEventsEmitter = null
}
})
val binaryMessenger = flutterEngine.dartExecutor.binaryMessenger
caffeinePlatformChannel.onAttachedToEngine(binaryMessenger, window)
cameraInfoPlatformChannel.onAttachedToEngine(binaryMessenger, context)
volumePlatformChannel.onAttachedToEngine(binaryMessenger)
}
override fun onDestroy() {
keepScreenOnChannel.setMethodCallHandler(null)
volumeHandlingChannel.setMethodCallHandler(null)
volumeEventChannel.setStreamHandler(null)
caffeinePlatformChannel.onDestroy()
cameraInfoPlatformChannel.onDestroy()
volumePlatformChannel.onDestroy()
super.onDestroy()
}
override fun onKeyDown(code: Int, event: KeyEvent): Boolean {
return when (val keyCode: Int = event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (handleVolume) {
volumeEventsEmitter?.success(keyCode)
true
} else {
super.onKeyDown(code, event)
}
}
else -> super.onKeyDown(code, event)
return if (volumePlatformChannel.onKeyDown(code, event)) {
true
} else {
super.onKeyDown(code, event)
}
}
}

View file

@ -0,0 +1,42 @@
package com.vodemn.lightmeter.PlatformChannels
import android.view.Window
import android.view.WindowManager
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
/** CaffeinePlatformChannel */
class CaffeinePlatformChannel {
private lateinit var channel: MethodChannel
fun onAttachedToEngine(binaryMessenger: BinaryMessenger, window: Window) {
channel = MethodChannel(
binaryMessenger,
"com.vodemn.lightmeter.CaffeinePlatformChannel.MethodChannel"
)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"isKeepScreenOn" -> result.success((window.attributes.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0)
"setKeepScreenOn" -> {
if (call.arguments !is Boolean) {
result.error(
"invalid args",
"Argument should be of type Bool for 'setKeepScreenOn' call",
null
)
} else {
if (call.arguments as Boolean) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
result.success(true)
}
}
else -> result.notImplemented()
}
}
}
fun onDestroy() {
channel.setMethodCallHandler(null)
}
}

View file

@ -5,30 +5,31 @@ import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CameraMetadata.LENS_FACING_BACK
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.math.pow
import kotlin.math.sqrt
/** CameraInfoPlatformChannel */
class CameraInfoPlatformChannel : MethodChannel.MethodCallHandler {
class CameraInfoPlatformChannel {
private lateinit var channel: MethodChannel
private lateinit var cameraManager: CameraManager
private var mainCameraEfl: Double? = null
fun onAttachedToEngine(binaryMessenger: BinaryMessenger, context: Context) {
channel = MethodChannel(binaryMessenger, "com.vodemn.lightmeter.CameraInfoPlatformChannel")
channel = MethodChannel(
binaryMessenger,
"com.vodemn.lightmeter.CameraInfoPlatformChannel.MethodChannel"
)
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
channel.setMethodCallHandler(this)
}
channel.setMethodCallHandler { call, result ->
when (call.method) {
"mainCameraEfl" -> {
mainCameraEfl = mainCameraEfl ?: getMainCameraFocalLength35mm()
result.success(mainCameraEfl)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"mainCameraEfl" -> {
mainCameraEfl = mainCameraEfl ?: getMainCameraFocalLength35mm()
result.success(mainCameraEfl)
else -> result.notImplemented()
}
else -> result.notImplemented()
}
}
@ -45,7 +46,8 @@ class CameraInfoPlatformChannel : MethodChannel.MethodCallHandler {
}
private fun CameraCharacteristics.focalLength35mm(): Double? {
val defaultFocalLength = get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)?.first()
val defaultFocalLength =
get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS)?.first()
val sensorSize = get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)
return if (defaultFocalLength != null && sensorSize != null) {
// https://en.wikipedia.org/wiki/35_mm_equivalent_focal_length#Conversions

View file

@ -0,0 +1,66 @@
package com.vodemn.lightmeter.PlatformChannels
import android.view.KeyEvent
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.EventChannel.EventSink
import io.flutter.plugin.common.MethodChannel
/** VolumePlatformChannel */
class VolumePlatformChannel {
private lateinit var volumeMethodChannel: MethodChannel
private lateinit var volumeEventChannel: EventChannel
private var volumeEventsEmitter: EventSink? = null
private var handleVolume = false
fun onAttachedToEngine(binaryMessenger: BinaryMessenger) {
volumeMethodChannel = MethodChannel(
binaryMessenger,
"com.vodemn.lightmeter.VolumePlatformChannel.MethodChannel"
)
volumeMethodChannel.setMethodCallHandler { call, result ->
when (call.method) {
"setVolumeHandling" -> {
handleVolume = call.arguments as Boolean
result.success(handleVolume)
}
else -> result.notImplemented()
}
}
volumeEventChannel = EventChannel(
binaryMessenger,
"com.vodemn.lightmeter.VolumePlatformChannel.EventChannel"
)
volumeEventChannel.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(listener: Any?, eventSink: EventSink) {
volumeEventsEmitter = eventSink
}
override fun onCancel(listener: Any?) {
volumeEventsEmitter = null
}
})
}
fun onDestroy() {
volumeMethodChannel.setMethodCallHandler(null)
volumeEventChannel.setStreamHandler(null)
}
fun onKeyDown(code: Int, event: KeyEvent): Boolean {
return when (val keyCode: Int = event.keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (handleVolume) {
volumeEventsEmitter?.success(keyCode)
true
} else {
false
}
}
else -> false
}
}
}

View file

@ -8,25 +8,7 @@ import Flutter
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let keepScreenOnChannel = FlutterMethodChannel(name: "com.vodemn.lightmeter/keepScreenOn",
binaryMessenger: controller.binaryMessenger)
keepScreenOnChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
switch call.method {
case "isKeepScreenOn":
result(UIApplication.shared.isIdleTimerDisabled)
case "setKeepScreenOn":
guard let keepOn = call.arguments as? Bool else {
result(FlutterError(code: "invalid arguments", message: "Argument should be of type Bool for 'setKeepScreenOn' call", details: nil))
return
}
UIApplication.shared.isIdleTimerDisabled = keepOn
result(true)
default:
result(FlutterMethodNotImplemented)
}
})
let caffeinePlatformChannel = CaffeinePlatformChannel(binaryMessenger: controller.binaryMessenger)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}

View file

@ -0,0 +1,36 @@
//
// CaffeinePlatformChannel.swift
// Runner
//
// Created by Vadim Turko on 2025-05-11.
//
import Flutter
public class CaffeinePlatformChannel: NSObject {
let methodChannel: FlutterMethodChannel
init(binaryMessenger: FlutterBinaryMessenger) {
self.methodChannel = FlutterMethodChannel(
name: "com.vodemn.lightmeter.CaffeinePlatformChannel.MethodChannel",
binaryMessenger: binaryMessenger
)
super.init()
methodChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
switch call.method {
case "isKeepScreenOn":
result(UIApplication.shared.isIdleTimerDisabled)
case "setKeepScreenOn":
guard let keepOn = call.arguments as? Bool else {
result(FlutterError(code: "invalid arguments", message: "Argument should be of type Bool for 'setKeepScreenOn' call", details: nil))
return
}
UIApplication.shared.isIdleTimerDisabled = keepOn
result(true)
default:
result(FlutterMethodNotImplemented)
}
})
}
}

View file

@ -100,13 +100,11 @@ class _ApplicationWrapperState extends State<ApplicationWrapper> {
await Future.wait([
SharedPreferences.getInstance(),
const LightSensorService(LocalPlatform()).hasSensor(),
const CameraInfoService().mainCameraEfl(),
remoteConfigService.activeAndFetchFeatures(),
equipmentProfilesStorageService.init(),
filmsStorageService.init(),
]).then((value) {
userPreferencesService = UserPreferencesService((value[0] as SharedPreferences?)!)
..cameraFocalLength = value[2] as int?;
userPreferencesService = UserPreferencesService((value[0] as SharedPreferences?)!);
hasLightSensor = value[1] as bool? ?? false;
});
}

View file

@ -1,15 +1,17 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class CaffeineService {
static const _methodChannel = MethodChannel("com.vodemn.lightmeter/keepScreenOn");
@visibleForTesting
static const caffeineMethodChannel = MethodChannel("com.vodemn.lightmeter.CaffeinePlatformChannel.MethodChannel");
const CaffeineService();
Future<bool> isKeepScreenOn() async {
return _methodChannel.invokeMethod<bool>("isKeepScreenOn").then((value) => value!);
return caffeineMethodChannel.invokeMethod<bool>("isKeepScreenOn").then((value) => value!);
}
Future<bool> keepScreenOn(bool keep) async {
return _methodChannel.invokeMethod<bool>("setKeepScreenOn", keep).then((value) => value!);
return caffeineMethodChannel.invokeMethod<bool>("setKeepScreenOn", keep).then((value) => value!);
}
}

View file

@ -5,7 +5,9 @@ import 'package:flutter/services.dart';
class CameraInfoService {
@visibleForTesting
static const cameraInfoPlatformChannel = MethodChannel("com.vodemn.lightmeter.CameraInfoPlatformChannel");
static const cameraInfoPlatformChannel = MethodChannel(
"com.vodemn.lightmeter.CameraInfoPlatformChannel.MethodChannel",
);
const CameraInfoService();

View file

@ -6,10 +6,10 @@ class VolumeEventsService {
final LocalPlatform _localPlatform;
@visibleForTesting
static const volumeHandlingChannel = MethodChannel("com.vodemn.lightmeter/volumeHandling");
static const volumeMethodChannel = MethodChannel("com.vodemn.lightmeter.VolumePlatformChannel.MethodChannel");
@visibleForTesting
static const volumeEventsChannel = EventChannel("com.vodemn.lightmeter/volumeEvents");
static const volumeEventsChannel = EventChannel("com.vodemn.lightmeter.VolumePlatformChannel.EventChannel");
const VolumeEventsService(this._localPlatform);
@ -19,9 +19,7 @@ class VolumeEventsService {
if (!_localPlatform.isAndroid) {
return false;
}
return volumeHandlingChannel
.invokeMethod<bool>("setVolumeHandling", enableHandling)
.then((value) => value!);
return volumeMethodChannel.invokeMethod<bool>("setVolumeHandling", enableHandling).then((value) => value!);
}
/// Emits new events on
@ -32,9 +30,6 @@ class VolumeEventsService {
if (!_localPlatform.isAndroid) {
return const Stream.empty();
}
return volumeEventsChannel
.receiveBroadcastStream()
.cast<int>()
.where((event) => event == 24 || event == 25);
return volumeEventsChannel.receiveBroadcastStream().cast<int>().where((event) => event == 24 || event == 25);
}
}

View file

@ -7,7 +7,6 @@ void main() {
late CaffeineService service;
const methodChannel = MethodChannel('com.vodemn.lightmeter/keepScreenOn');
Future<Object?>? methodCallSuccessHandler(MethodCall methodCall) async {
switch (methodCall.method) {
case "isKeepScreenOn":
@ -21,45 +20,57 @@ void main() {
setUp(() {
service = const CaffeineService();
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(methodChannel, methodCallSuccessHandler);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CaffeineService.caffeineMethodChannel,
methodCallSuccessHandler,
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(methodChannel, null);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CaffeineService.caffeineMethodChannel,
null,
);
});
group(
'isKeepScreenOn()',
() {
test('true', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(methodChannel, null);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(methodChannel, (methodCall) async {
switch (methodCall.method) {
case "isKeepScreenOn":
return true;
default:
return null;
}
});
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CaffeineService.caffeineMethodChannel,
null,
);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CaffeineService.caffeineMethodChannel,
(methodCall) async {
switch (methodCall.method) {
case "isKeepScreenOn":
return true;
default:
return null;
}
},
);
expectLater(service.isKeepScreenOn(), completion(true));
});
test('false', () async {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(methodChannel, null);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(methodChannel, (methodCall) async {
switch (methodCall.method) {
case "isKeepScreenOn":
return false;
default:
return null;
}
});
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CaffeineService.caffeineMethodChannel,
null,
);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CaffeineService.caffeineMethodChannel,
(methodCall) async {
switch (methodCall.method) {
case "isKeepScreenOn":
return false;
default:
return null;
}
},
);
expectLater(service.isKeepScreenOn(), completion(false));
});
},

View file

@ -27,14 +27,14 @@ void main() {
localPlatform = _MockLocalPlatform();
service = VolumeEventsService(localPlatform);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
VolumeEventsService.volumeHandlingChannel,
VolumeEventsService.volumeMethodChannel,
methodCallSuccessHandler,
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
VolumeEventsService.volumeHandlingChannel,
VolumeEventsService.volumeMethodChannel,
null,
);
});