/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package com.facebook.react.modules.deviceinfo import com.facebook.fbreact.specs.NativeDeviceInfoSpec import com.facebook.react.bridge.LifecycleEventListener import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactNoCrashSoftException import com.facebook.react.bridge.ReactSoftExceptionLogger import com.facebook.react.bridge.ReadableMap import com.facebook.react.module.annotations.ReactModule import com.facebook.react.uimanager.DisplayMetricsHolder.getDisplayMetricsWritableMap import com.facebook.react.uimanager.DisplayMetricsHolder.initDisplayMetricsIfNotInitialized import com.facebook.react.views.view.isEdgeToEdgeFeatureFlagOn /** Module that exposes Android Constants to JS. */ @ReactModule(name = NativeDeviceInfoSpec.NAME) internal class DeviceInfoModule(reactContext: ReactApplicationContext) : NativeDeviceInfoSpec(reactContext), LifecycleEventListener { private var fontScale: Float = reactContext.resources.configuration.fontScale private var previousDisplayMetrics: ReadableMap? = null init { initDisplayMetricsIfNotInitialized(reactContext) reactContext.addLifecycleEventListener(this) } public override fun getTypedExportedConstants(): Map { val displayMetrics = getDisplayMetricsWritableMap(fontScale.toDouble()) // Cache the initial dimensions for later comparison in emitUpdateDimensionsEvent previousDisplayMetrics = displayMetrics.copy() return mapOf( "Dimensions" to displayMetrics.toHashMap(), "isEdgeToEdge" to isEdgeToEdgeFeatureFlagOn, ) } override fun onHostResume() { val newFontScale = reactApplicationContext.resources?.configuration?.fontScale if (newFontScale != null && newFontScale != fontScale) { fontScale = newFontScale emitUpdateDimensionsEvent() } } override fun onHostPause(): Unit = Unit override fun onHostDestroy(): Unit = Unit fun emitUpdateDimensionsEvent() { reactApplicationContext.let { context -> if (context.hasActiveReactInstance()) { // Don't emit an event to JS if the dimensions haven't changed val displayMetrics = getDisplayMetricsWritableMap(fontScale.toDouble()) if (previousDisplayMetrics == null) { previousDisplayMetrics = displayMetrics.copy() } else if (displayMetrics != previousDisplayMetrics) { previousDisplayMetrics = displayMetrics.copy() context.emitDeviceEvent("didUpdateDimensions", displayMetrics) } } else { ReactSoftExceptionLogger.logSoftException( NativeDeviceInfoSpec.NAME, ReactNoCrashSoftException( "No active CatalystInstance, cannot emitUpdateDimensionsEvent" ), ) } } } override fun invalidate() { super.invalidate() reactApplicationContext.removeLifecycleEventListener(this) } companion object { const val NAME: String = NativeDeviceInfoSpec.NAME } }