mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
This commit centralizes all Android Intent constants into a new `MeshtasticIntent` object within the `core/api` module. This refactoring makes the constants accessible to external applications and removes the duplicated definitions from the main application. The app and the service example have been updated to use these new centralized constants. Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
127 lines
5.1 KiB
Kotlin
127 lines
5.1 KiB
Kotlin
/*
|
|
* Copyright (c) 2025-2026 Meshtastic LLC
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.geeksville.mesh.service
|
|
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.os.Parcelable
|
|
import co.touchlab.kermit.Logger
|
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
import org.meshtastic.core.model.DataPacket
|
|
import org.meshtastic.core.model.MessageStatus
|
|
import org.meshtastic.core.model.NodeInfo
|
|
import org.meshtastic.core.model.util.toPIIString
|
|
import org.meshtastic.core.service.ConnectionState
|
|
import org.meshtastic.core.service.ServiceRepository
|
|
import java.util.Locale
|
|
import javax.inject.Inject
|
|
import javax.inject.Singleton
|
|
|
|
@Singleton
|
|
class MeshServiceBroadcasts
|
|
@Inject
|
|
constructor(
|
|
@ApplicationContext private val context: Context,
|
|
private val connectionStateHolder: ConnectionStateHandler,
|
|
private val serviceRepository: ServiceRepository,
|
|
) {
|
|
// A mapping of receiver class name to package name - used for explicit broadcasts
|
|
private val clientPackages = mutableMapOf<String, String>()
|
|
|
|
fun subscribeReceiver(receiverName: String, packageName: String) {
|
|
clientPackages[receiverName] = packageName
|
|
}
|
|
|
|
/** Broadcast some received data Payload will be a DataPacket */
|
|
fun broadcastReceivedData(payload: DataPacket) {
|
|
val action = MeshService.actionReceived(payload.dataType)
|
|
explicitBroadcast(Intent(action).putExtra(EXTRA_PAYLOAD, payload))
|
|
|
|
// Also broadcast with the numeric port number for backwards compatibility with some apps
|
|
val numericAction = actionReceived(payload.dataType.toString())
|
|
if (numericAction != action) {
|
|
explicitBroadcast(Intent(numericAction).putExtra(EXTRA_PAYLOAD, payload))
|
|
}
|
|
}
|
|
|
|
fun broadcastNodeChange(info: NodeInfo) {
|
|
Logger.d { "Broadcasting node change ${info.user?.toPIIString()}" }
|
|
val intent = Intent(ACTION_NODE_CHANGE).putExtra(EXTRA_NODEINFO, info)
|
|
explicitBroadcast(intent)
|
|
}
|
|
|
|
fun broadcastMessageStatus(p: DataPacket) = broadcastMessageStatus(p.id, p.status)
|
|
|
|
fun broadcastMessageStatus(id: Int, status: MessageStatus?) {
|
|
if (id == 0) {
|
|
Logger.d { "Ignoring anonymous packet status" }
|
|
} else {
|
|
// Do not log, contains PII possibly
|
|
// MeshService.Logger.d { "Broadcasting message status $p" }
|
|
val intent =
|
|
Intent(ACTION_MESSAGE_STATUS).apply {
|
|
putExtra(EXTRA_PACKET_ID, id)
|
|
putExtra(EXTRA_STATUS, status as Parcelable)
|
|
}
|
|
explicitBroadcast(intent)
|
|
}
|
|
}
|
|
|
|
/** Broadcast our current connection status */
|
|
fun broadcastConnection() {
|
|
val connectionState = connectionStateHolder.connectionState.value
|
|
// ATAK expects a String: "CONNECTED" or "DISCONNECTED"
|
|
// It uses equalsIgnoreCase, but we'll use uppercase to be specific.
|
|
val stateStr = connectionState.toString().uppercase(Locale.ROOT)
|
|
|
|
val intent = Intent(ACTION_MESH_CONNECTED).apply { putExtra(EXTRA_CONNECTED, stateStr) }
|
|
serviceRepository.setConnectionState(connectionState)
|
|
explicitBroadcast(intent)
|
|
|
|
if (connectionState == ConnectionState.Disconnected) {
|
|
explicitBroadcast(Intent(ACTION_MESH_DISCONNECTED))
|
|
}
|
|
|
|
// Restore legacy action for other consumers (e.g. mesh_service_example)
|
|
val legacyIntent =
|
|
Intent(ACTION_CONNECTION_CHANGED).apply {
|
|
putExtra(EXTRA_CONNECTED, stateStr)
|
|
// Legacy boolean extra often expected by older implementations
|
|
putExtra("connected", connectionState == ConnectionState.Connected)
|
|
}
|
|
explicitBroadcast(legacyIntent)
|
|
}
|
|
|
|
/**
|
|
* See com.geeksville.mesh broadcast intents.
|
|
*
|
|
* RECEIVED_OPAQUE for data received from other nodes
|
|
* NODE_CHANGE for new IDs appearing or disappearing
|
|
* ACTION_MESH_CONNECTED for losing/gaining connection to the packet radio
|
|
* Note: this is not the same as RadioInterfaceService.RADIO_CONNECTED_ACTION,
|
|
* because it implies we have assembled a valid node db.
|
|
*/
|
|
private fun explicitBroadcast(intent: Intent) {
|
|
context.sendBroadcast(
|
|
intent,
|
|
) // We also do a regular (not explicit broadcast) so any context-registered rceivers will work
|
|
clientPackages.forEach {
|
|
intent.setClassName(it.value, it.key)
|
|
context.sendBroadcast(intent)
|
|
}
|
|
}
|
|
}
|