mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Modularize more models/utils (#3182)
This commit is contained in:
parent
5bb3f73e0d
commit
4eba3e9daf
80 changed files with 656 additions and 629 deletions
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.util
|
||||
|
||||
import java.text.DateFormat
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
// return time if within 24 hours, otherwise date
|
||||
fun getShortDate(time: Long): String? {
|
||||
val date = if (time != 0L) Date(time) else return null
|
||||
val isWithin24Hours = System.currentTimeMillis() - date.time <= TimeUnit.DAYS.toMillis(1)
|
||||
|
||||
return if (isWithin24Hours) {
|
||||
DateFormat.getTimeInstance(DateFormat.SHORT).format(date)
|
||||
} else {
|
||||
DateFormat.getDateInstance(DateFormat.SHORT).format(date)
|
||||
}
|
||||
}
|
||||
|
||||
// return time if within 24 hours, otherwise date/time
|
||||
fun getShortDateTime(time: Long): String {
|
||||
val date = Date(time)
|
||||
val isWithin24Hours = System.currentTimeMillis() - date.time <= TimeUnit.DAYS.toMillis(1)
|
||||
|
||||
return if (isWithin24Hours) {
|
||||
DateFormat.getTimeInstance(DateFormat.SHORT).format(date)
|
||||
} else {
|
||||
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(date)
|
||||
}
|
||||
}
|
||||
|
||||
fun formatUptime(seconds: Int): String = formatUptime(seconds.toLong())
|
||||
|
||||
private fun formatUptime(seconds: Long): String {
|
||||
val days = TimeUnit.SECONDS.toDays(seconds)
|
||||
val hours = TimeUnit.SECONDS.toHours(seconds) % TimeUnit.DAYS.toHours(1)
|
||||
val minutes = TimeUnit.SECONDS.toMinutes(seconds) % TimeUnit.HOURS.toMinutes(1)
|
||||
val secs = seconds % TimeUnit.MINUTES.toSeconds(1)
|
||||
|
||||
return listOfNotNull(
|
||||
"${days}d".takeIf { days > 0 },
|
||||
"${hours}h".takeIf { hours > 0 },
|
||||
"${minutes}m".takeIf { minutes > 0 },
|
||||
"${secs}s".takeIf { secs > 0 },
|
||||
).joinToString(" ")
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun onlineTimeThreshold() = (System.currentTimeMillis() / 1000 - 2 * 60 * 60).toInt()
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.util
|
||||
|
||||
import android.widget.EditText
|
||||
import com.geeksville.mesh.BuildConfig
|
||||
import com.geeksville.mesh.ConfigProtos
|
||||
import com.geeksville.mesh.MeshProtos
|
||||
|
||||
/**
|
||||
* When printing strings to logs sometimes we want to print useful debugging information about users or positions. But
|
||||
* we don't want to leak things like usernames or locations. So this function if given a string, will return a string
|
||||
* which is a maximum of three characters long, taken from the tail of the string. Which should effectively hide real
|
||||
* usernames and locations, but still let us see if values were zero, empty or different.
|
||||
*/
|
||||
val Any?.anonymize: String
|
||||
get() = this.anonymize()
|
||||
|
||||
/** A version of anonymize that allows passing in a custom minimum length */
|
||||
fun Any?.anonymize(maxLen: Int = 3) = if (this != null) ("..." + this.toString().takeLast(maxLen)) else "null"
|
||||
|
||||
// A toString that makes sure all newlines are removed (for nice logging).
|
||||
fun Any.toOneLineString() = this.toString().replace('\n', ' ')
|
||||
|
||||
fun ConfigProtos.Config.toOneLineString(): String {
|
||||
val redactedFields = """(wifi_psk:|public_key:|private_key:|admin_key:)\s*".*"""
|
||||
return this.toString()
|
||||
.replace(redactedFields.toRegex()) { "${it.groupValues[1]} \"[REDACTED]\"" }
|
||||
.replace('\n', ' ')
|
||||
}
|
||||
|
||||
fun MeshProtos.toOneLineString(): String {
|
||||
val redactedFields = """(public_key:|private_key:|admin_key:)\s*".*""" // Redact keys
|
||||
return this.toString()
|
||||
.replace(redactedFields.toRegex()) { "${it.groupValues[1]} \"[REDACTED]\"" }
|
||||
.replace('\n', ' ')
|
||||
}
|
||||
|
||||
// Return a one line string version of an object (but if a release build, just say 'might be PII)
|
||||
fun Any.toPIIString() = if (!BuildConfig.DEBUG) {
|
||||
"<PII?>"
|
||||
} else {
|
||||
this.toOneLineString()
|
||||
}
|
||||
|
||||
fun ByteArray.toHexString() = joinToString("") { "%02x".format(it) }
|
||||
|
||||
fun formatAgo(lastSeenUnix: Int, currentTimeMillis: Long = System.currentTimeMillis()): String {
|
||||
val currentTime = (currentTimeMillis / 1000).toInt()
|
||||
val diffMin = (currentTime - lastSeenUnix) / 60
|
||||
return when {
|
||||
diffMin < 1 -> "now"
|
||||
diffMin < 60 -> diffMin.toString() + " min"
|
||||
diffMin < 2880 -> (diffMin / 60).toString() + " h"
|
||||
diffMin < 1440000 -> (diffMin / (60 * 24)).toString() + " d"
|
||||
else -> "?"
|
||||
}
|
||||
}
|
||||
|
||||
private const val MPS_TO_KMPH = 3.6f
|
||||
private const val KM_TO_MILES = 0.621371f
|
||||
|
||||
fun Int.mpsToKmph(): Float {
|
||||
// Convert meters per second to kilometers per hour
|
||||
val kmph = this * MPS_TO_KMPH
|
||||
return kmph
|
||||
}
|
||||
|
||||
fun Int.mpsToMph(): Float {
|
||||
// Convert meters per second to miles per hour
|
||||
val mph = this * MPS_TO_KMPH * KM_TO_MILES
|
||||
return mph
|
||||
}
|
||||
|
||||
// Allows usage like email.onEditorAction(EditorInfo.IME_ACTION_NEXT, { confirm() })
|
||||
fun EditText.onEditorAction(actionId: Int, func: () -> Unit) {
|
||||
setOnEditorActionListener { _, receivedActionId, _ ->
|
||||
if (actionId == receivedActionId) {
|
||||
func()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.util
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import com.geeksville.mesh.Position
|
||||
import java.util.Locale
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@SuppressLint("PropertyNaming")
|
||||
object GPSFormat {
|
||||
fun toDec(latitude: Double, longitude: Double): String =
|
||||
String.format(Locale.getDefault(), "%.5f, %.5f", latitude, longitude)
|
||||
}
|
||||
|
||||
private const val EARTH_RADIUS_METERS = 6371e3
|
||||
|
||||
/** @return distance in meters along the surface of the earth (ish) */
|
||||
fun latLongToMeter(latitudeA: Double, longitudeA: Double, latitudeB: Double, longitudeB: Double): Double {
|
||||
val lat1 = Math.toRadians(latitudeA)
|
||||
val lon1 = Math.toRadians(longitudeA)
|
||||
val lat2 = Math.toRadians(latitudeB)
|
||||
val lon2 = Math.toRadians(longitudeB)
|
||||
|
||||
val dLat = lat2 - lat1
|
||||
val dLon = lon2 - lon1
|
||||
|
||||
val a = sin(dLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(dLon / 2).pow(2)
|
||||
val c = 2 * asin(sqrt(a))
|
||||
|
||||
return EARTH_RADIUS_METERS * c
|
||||
}
|
||||
|
||||
// Same as above, but takes Mesh Position proto.
|
||||
fun positionToMeter(a: Position, b: Position): Double =
|
||||
latLongToMeter(a.latitude * 1e-7, a.longitude * 1e-7, b.latitude * 1e-7, b.longitude * 1e-7)
|
||||
|
||||
/**
|
||||
* Computes the bearing in degrees between two points on Earth.
|
||||
*
|
||||
* @param lat1 Latitude of the first point
|
||||
* @param lon1 Longitude of the first point
|
||||
* @param lat2 Latitude of the second point
|
||||
* @param lon2 Longitude of the second point
|
||||
* @return Bearing between the two points in degrees. A value of 0 means due north.
|
||||
*/
|
||||
fun bearing(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
|
||||
val lat1Rad = Math.toRadians(lat1)
|
||||
val lon1Rad = Math.toRadians(lon1)
|
||||
val lat2Rad = Math.toRadians(lat2)
|
||||
val lon2Rad = Math.toRadians(lon2)
|
||||
|
||||
val dLon = lon2Rad - lon1Rad
|
||||
|
||||
val y = sin(dLon) * cos(lat2Rad)
|
||||
val x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dLon)
|
||||
val bearing = Math.toDegrees(atan2(y, x))
|
||||
|
||||
return (bearing + 360) % 360
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025 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.util
|
||||
|
||||
import kotlin.math.ln
|
||||
|
||||
object UnitConversions {
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun celsiusToFahrenheit(celsius: Float): Float {
|
||||
return (celsius * 1.8F) + 32
|
||||
}
|
||||
|
||||
fun Float.toTempString(isFahrenheit: Boolean) = if (isFahrenheit) {
|
||||
val fahrenheit = celsiusToFahrenheit(this)
|
||||
"%.0f°F".format(fahrenheit)
|
||||
} else {
|
||||
"%.0f°C".format(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculated the dew point based on the Magnus-Tetens approximation which is a widely used
|
||||
* formula for calculating dew point temperature.
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
fun calculateDewPoint(tempCelsius: Float, humidity: Float): Float {
|
||||
val (a, b) = 17.27f to 237.7f
|
||||
val alpha = (a * tempCelsius) / (b + tempCelsius) + ln(humidity / 100f)
|
||||
return (b * alpha) / (a - alpha)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue