From 56ac6fe55885ec5a5a43c53fc8efac2811943217 Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sun, 11 May 2025 11:50:01 +0200 Subject: [PATCH] [android] calculate EFL --- .../com/vodemn/lightmeter/MainActivity.kt | 6 ++ .../CameraInfoPlatformChannel.kt | 57 +++++++++++++++++++ lib/application_wrapper.dart | 5 +- lib/data/camera_info_service.dart | 18 ++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 android/app/src/main/kotlin/com/vodemn/lightmeter/PlatformChannels/CameraInfoPlatformChannel.kt create mode 100644 lib/data/camera_info_service.dart diff --git a/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt b/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt index af458c4..194ec17 100644 --- a/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt +++ b/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.KeyEvent import android.view.WindowManager import androidx.core.view.WindowCompat +import com.vodemn.lightmeter.PlatformChannels.CameraInfoPlatformChannel import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.EventChannel @@ -17,6 +18,8 @@ class MainActivity : FlutterActivity() { private var volumeEventsEmitter: EventSink? = null private var handleVolume = false + private val cameraInfoPlatformChannel = CameraInfoPlatformChannel() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) @@ -24,6 +27,8 @@ 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" @@ -76,6 +81,7 @@ class MainActivity : FlutterActivity() { keepScreenOnChannel.setMethodCallHandler(null) volumeHandlingChannel.setMethodCallHandler(null) volumeEventChannel.setStreamHandler(null) + cameraInfoPlatformChannel.onDestroy() super.onDestroy() } diff --git a/android/app/src/main/kotlin/com/vodemn/lightmeter/PlatformChannels/CameraInfoPlatformChannel.kt b/android/app/src/main/kotlin/com/vodemn/lightmeter/PlatformChannels/CameraInfoPlatformChannel.kt new file mode 100644 index 0000000..53d9f07 --- /dev/null +++ b/android/app/src/main/kotlin/com/vodemn/lightmeter/PlatformChannels/CameraInfoPlatformChannel.kt @@ -0,0 +1,57 @@ +package com.vodemn.lightmeter.PlatformChannels + +import android.content.Context +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 { + 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") + cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager + channel.setMethodCallHandler(this) + } + + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + when (call.method) { + "mainCameraEfl" -> { + mainCameraEfl = mainCameraEfl ?: getMainCameraFocalLength35mm() + result.success(mainCameraEfl) + } + else -> result.notImplemented() + } + } + + fun onDestroy() { + channel.setMethodCallHandler(null) + } + + private fun getMainCameraFocalLength35mm(): Double? { + return cameraManager.cameraIdList.map { + cameraManager.getCameraCharacteristics(it) + }.first { + it.get(CameraCharacteristics.LENS_FACING) == LENS_FACING_BACK + }.focalLength35mm() + } + + private fun CameraCharacteristics.focalLength35mm(): Double? { + 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 + 43.27 * defaultFocalLength / sqrt(sensorSize.height.pow(2) + sensorSize.width.pow(2)) + } else { + null + } + } +} diff --git a/lib/application_wrapper.dart b/lib/application_wrapper.dart index 611e1f7..e2c6136 100644 --- a/lib/application_wrapper.dart +++ b/lib/application_wrapper.dart @@ -5,6 +5,7 @@ import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:lightmeter/data/analytics/analytics.dart'; import 'package:lightmeter/data/analytics/api/analytics_firebase.dart'; import 'package:lightmeter/data/caffeine_service.dart'; +import 'package:lightmeter/data/camera_info_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart'; import 'package:lightmeter/data/permissions_service.dart'; @@ -99,11 +100,13 @@ class _ApplicationWrapperState extends State { 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?)!); + userPreferencesService = UserPreferencesService((value[0] as SharedPreferences?)!) + ..cameraFocalLength = value[2] as int?; hasLightSensor = value[1] as bool? ?? false; }); } diff --git a/lib/data/camera_info_service.dart b/lib/data/camera_info_service.dart new file mode 100644 index 0000000..351a975 --- /dev/null +++ b/lib/data/camera_info_service.dart @@ -0,0 +1,18 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +class CameraInfoService { + @visibleForTesting + static const cameraInfoPlatformChannel = MethodChannel("com.vodemn.lightmeter.CameraInfoPlatformChannel"); + + const CameraInfoService(); + + Future mainCameraEfl() async { + if (Platform.isIOS) { + return null; + } + return (await cameraInfoPlatformChannel.invokeMethod('mainCameraEfl'))?.round(); + } +}