/* * Copyright (c) Double Symmetry GmbH * Commercial use requires a license. See https://rntp.dev/pricing */ package com.doublesymmetry.trackplayer.extensions import android.app.ActivityManager import android.content.Context import android.content.Intent import android.os.Bundle import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.modules.core.DeviceEventManagerModule import com.doublesymmetry.trackplayer.TrackPlayerTaskService import com.doublesymmetry.trackplayer.models.EmitEvent fun ReactApplicationContext.emitEvent(event: EmitEvent) { if (!isAppOnForeground()) { val service = Intent(applicationContext, TrackPlayerTaskService::class.java) val payload = Bundle().apply { putString("event", event.type.value) putBundle("payload", pairsToBundle(event.pairs())) } service.putExtras(payload) startService(service) } else { getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit(event.type.value, writableMapOf(*event.pairs())) } } /** * Extension function to emit events from a Context (e.g., from a Service). * Use when the call site has a generic Context, not a ReactApplicationContext. */ fun android.content.Context.emitEvent(event: EmitEvent) { val reactContext = (applicationContext as? com.facebook.react.ReactApplication) ?.reactHost ?.currentReactContext as? ReactApplicationContext reactContext?.emitEvent(event) } //region Private Helpers private fun ReactApplicationContext.isAppOnForeground(): Boolean { val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager val appProcesses = activityManager.runningAppProcesses ?: return false for (appProcess in appProcesses) { if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName == packageName ) { return true } } return false } private fun pairsToBundle(pairs: Array>): Bundle { val bundle = Bundle() for ((key, value) in pairs) { when (value) { is Boolean -> bundle.putBoolean(key, value) is Int -> bundle.putInt(key, value) is Long -> bundle.putLong(key, value) is Float -> bundle.putFloat(key, value) is Double -> bundle.putDouble(key, value) is String -> bundle.putString(key, value) is ReadableMap -> bundle.putBundle(key, readableMapToBundle(value)) is ReadableArray -> bundle.putBundle(key, readableArrayToBundle(value)) else -> bundle.putString(key, value.toString()) } } return bundle } private fun readableMapToBundle(map: ReadableMap): Bundle { val bundle = Bundle() val iter = map.keySetIterator() while (iter.hasNextKey()) { val key = iter.nextKey() when (map.getType(key).name) { "Null" -> {} "Boolean" -> bundle.putBoolean(key, map.getBoolean(key)) "Number" -> bundle.putDouble(key, map.getDouble(key)) "String" -> bundle.putString(key, map.getString(key)) "Map" -> map.getMap(key)?.let { bundle.putBundle(key, readableMapToBundle(it)) } "Array" -> map.getArray(key)?.let { bundle.putBundle(key, readableArrayToBundle(it)) } } } return bundle } private fun readableArrayToBundle(array: ReadableArray): Bundle { val bundle = Bundle() bundle.putInt("__rntp_array_length", array.size()) for (i in 0 until array.size()) { val key = i.toString() when (array.getType(i).name) { "Null" -> {} "Boolean" -> bundle.putBoolean(key, array.getBoolean(i)) "Number" -> bundle.putDouble(key, array.getDouble(i)) "String" -> bundle.putString(key, array.getString(i)) "Map" -> array.getMap(i)?.let { bundle.putBundle(key, readableMapToBundle(it)) } "Array" -> array.getArray(i)?.let { bundle.putBundle(key, readableArrayToBundle(it)) } } } return bundle } //endregion