simplify :app-wear-shared:shared

This commit is contained in:
Milos Kozak 2023-09-18 21:00:54 +02:00
parent b7bc33c1d8
commit 81eb4f3a1c
52 changed files with 1145 additions and 834 deletions

View file

@ -4,10 +4,18 @@ import android.content.Context
import androidx.preference.PreferenceManager
import dagger.Module
import dagger.Provides
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.interfaces.L
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.shared.impl.logging.AAPSLoggerProduction
import info.nightscout.shared.impl.logging.LImpl
import info.nightscout.shared.impl.rx.AapsSchedulersImpl
import info.nightscout.shared.impl.rx.bus.RxBusImpl
import info.nightscout.shared.impl.sharedPreferences.SPImplementation
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.DateUtilImpl
import javax.inject.Singleton
@Module(
@ -23,4 +31,20 @@ open class SharedImplModule {
@Provides
@Singleton
fun provideL(sp: SP): L = LImpl(sp)
@Provides
@Singleton
fun provideDateUtil(context: Context): DateUtil = DateUtilImpl(context)
@Provides
@Singleton
fun provideAAPSLogger(l: L): AAPSLogger = AAPSLoggerProduction(l)
@Provides
@Singleton
fun provideRxBus(aapsSchedulers: AapsSchedulers, aapsLogger: AAPSLogger): RxBus = RxBusImpl(aapsSchedulers, aapsLogger)
@Provides
@Singleton
internal fun provideSchedulers(): AapsSchedulers = AapsSchedulersImpl()
}

View file

@ -1,6 +1,8 @@
package info.nightscout.rx.logging
package info.nightscout.shared.impl.logging
import info.nightscout.rx.interfaces.L
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import org.slf4j.LoggerFactory
/**

View file

@ -0,0 +1,16 @@
package info.nightscout.shared.impl.rx
import info.nightscout.rx.AapsSchedulers
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.schedulers.Schedulers
import javax.inject.Singleton
@Singleton
class AapsSchedulersImpl : AapsSchedulers {
override val main: Scheduler = AndroidSchedulers.mainThread()
override val io: Scheduler = Schedulers.io()
override val cpu: Scheduler = Schedulers.computation()
override val newThread: Scheduler = Schedulers.newThread()
}

View file

@ -0,0 +1,34 @@
package info.nightscout.shared.impl.rx.bus
import info.nightscout.annotations.OpenForTesting
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.Event
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting
@Singleton
class RxBusImpl @Inject constructor(
val aapsSchedulers: AapsSchedulers,
val aapsLogger: AAPSLogger
) : RxBus {
private val publisher = PublishSubject.create<Event>()
override fun send(event: Event) {
aapsLogger.debug(LTag.EVENTS, "Sending $event")
publisher.onNext(event)
}
// Listen should return an Observable and not the publisher
// Using ofType we filter only events that match that class type
override fun <T : Any> toObservable(eventType: Class<T>): Observable<T> =
publisher
.subscribeOn(aapsSchedulers.io)
.ofType(eventType)
}

View file

@ -0,0 +1,470 @@
package info.nightscout.shared.utils
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.collection.LongSparseArray
import info.nightscout.annotations.OpenForTesting
import info.nightscout.shared.R
import info.nightscout.shared.SafeParse
import info.nightscout.shared.interfaces.ResourceHelper
import org.apache.commons.lang3.time.DateUtils.isSameDay
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.ISODateTimeFormat
import java.security.SecureRandom
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.ZoneId
import java.time.ZoneOffset
import java.util.Calendar
import java.util.Date
import java.util.EnumSet
import java.util.GregorianCalendar
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import java.util.stream.Collectors
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.ceil
import kotlin.math.floor
/**
* The Class DateUtil. A simple wrapper around SimpleDateFormat to ease the handling of iso date string &lt;-&gt; date obj
* with TZ
*/
@OpenForTesting
@Singleton
class DateUtilImpl @Inject constructor(private val context: Context) : DateUtil {
/**
* The date format in iso.
*/
@Suppress("PrivatePropertyName", "SpellCheckingInspection")
private val FORMAT_DATE_ISO_OUT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
/**
* Takes in an ISO date string of the following format:
* yyyy-mm-ddThh:mm:ss.ms+HoMo
*
* @param isoDateString the iso date string
* @return the date
*/
override fun fromISODateString(isoDateString: String): Long {
val parser = ISODateTimeFormat.dateTimeParser()
val dateTime = DateTime.parse(isoDateString, parser)
return dateTime.toDate().time
}
/**
* Render date
*
* @param date the date obj
* @return the iso-formatted date string
*/
override fun toISOString(date: Long): String {
val f: DateFormat = SimpleDateFormat(FORMAT_DATE_ISO_OUT, Locale.getDefault())
f.timeZone = TimeZone.getTimeZone("UTC")
return f.format(date)
}
@Suppress("SpellCheckingInspection")
override fun toISOAsUTC(timestamp: Long): String {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'0000Z'", Locale.US)
format.timeZone = TimeZone.getTimeZone("UTC")
return format.format(timestamp)
}
@Suppress("SpellCheckingInspection")
override fun toISONoZone(timestamp: Long): String {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
format.timeZone = TimeZone.getDefault()
return format.format(timestamp)
}
override fun secondsOfTheDayToMilliseconds(seconds: Int): Long {
val calendar: Calendar = GregorianCalendar()
calendar[Calendar.MONTH] = 0 // Set january to be sure we miss DST changing
calendar[Calendar.HOUR_OF_DAY] = seconds / 60 / 60
calendar[Calendar.MINUTE] = seconds / 60 % 60
calendar[Calendar.SECOND] = 0
return calendar.timeInMillis
}
override fun toSeconds(hhColonMm: String): Int {
val p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)")
val m = p.matcher(hhColonMm)
var retVal = 0
if (m.find()) {
retVal = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60
if ((m.group(3) == " a.m." || m.group(3) == " AM" || m.group(3) == "AM") && m.group(1) == "12") retVal -= 12 * 60 * 60
if ((m.group(3) == " p.m." || m.group(3) == " PM" || m.group(3) == "PM") && m.group(1) != "12") retVal += 12 * 60 * 60
}
return retVal
}
override fun dateString(mills: Long): String {
val df = DateFormat.getDateInstance(DateFormat.SHORT)
return df.format(mills)
}
override fun dateStringRelative(mills: Long, rh: ResourceHelper): String {
val df = DateFormat.getDateInstance(DateFormat.SHORT)
val day = df.format(mills)
val beginOfToday = beginOfDay(now())
return if (mills < now()) // Past
when {
mills > beginOfToday -> rh.gs(R.string.today)
mills > beginOfToday - T.days(1).msecs() -> rh.gs(R.string.yesterday)
mills > beginOfToday - T.days(7).msecs() -> dayAgo(mills, rh, true)
else -> day
}
else // Future
when {
mills < beginOfToday + T.days(1).msecs() -> rh.gs(R.string.later_today)
mills < beginOfToday + T.days(2).msecs() -> rh.gs(R.string.tomorrow)
mills < beginOfToday + T.days(7).msecs() -> dayAgo(mills, rh, true)
else -> day
}
}
override fun dateStringShort(mills: Long): String {
var format = "MM/dd"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "dd/MM"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
override fun timeString(): String = timeString(now())
override fun timeString(mills: Long): String {
var format = "hh:mma"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "HH:mm"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
override fun secondString(): String = secondString(now())
override fun secondString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("ss"))
override fun minuteString(): String = minuteString(now())
override fun minuteString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("mm"))
override fun hourString(): String = hourString(now())
override fun hourString(mills: Long): String {
var format = "hh"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "HH"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
override fun amPm(): String = amPm(now())
override fun amPm(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("a"))
override fun dayNameString(format: String): String = dayNameString(now(), format)
override fun dayNameString(mills: Long, format: String): String =
DateTime(mills).toString(DateTimeFormat.forPattern(format))
override fun dayString(): String = dayString(now())
override fun dayString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("dd"))
override fun monthString(format: String): String = monthString(now(), format)
override fun monthString(mills: Long, format: String): String =
DateTime(mills).toString(DateTimeFormat.forPattern(format))
override fun weekString(): String = weekString(now())
override fun weekString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("ww"))
override fun timeStringWithSeconds(mills: Long): String {
var format = "hh:mm:ssa"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "HH:mm:ss"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
override fun dateAndTimeRangeString(start: Long, end: Long): String {
return dateAndTimeString(start) + " - " + timeString(end)
}
override fun timeRangeString(start: Long, end: Long): String {
return timeString(start) + " - " + timeString(end)
}
override fun dateAndTimeString(mills: Long): String {
return if (mills == 0L) "" else dateString(mills) + " " + timeString(mills)
}
override fun dateAndTimeAndSecondsString(mills: Long): String {
return if (mills == 0L) "" else dateString(mills) + " " + timeStringWithSeconds(mills)
}
override fun minAgo(rh: ResourceHelper, time: Long?): String {
if (time == null) return ""
val minutes = ((now() - time) / 1000 / 60).toInt()
return rh.gs(R.string.minago, minutes)
}
override fun minAgoShort(time: Long?): String {
if (time == null) return ""
val minutes = ((time - now()) / 1000 / 60).toInt()
return (if (minutes > 0) "+" else "") + minutes
}
override fun minAgoLong(rh: ResourceHelper, time: Long?): String {
if (time == null) return ""
val minutes = ((now() - time) / 1000 / 60).toInt()
return rh.gs(R.string.minago_long, minutes)
}
override fun hourAgo(time: Long, rh: ResourceHelper): String {
val hours = (now() - time) / 1000.0 / 60 / 60
return rh.gs(R.string.hoursago, hours)
}
override fun dayAgo(time: Long, rh: ResourceHelper, round: Boolean): String {
var days = (now() - time) / 1000.0 / 60 / 60 / 24
if (round) {
return if (now() > time) {
days = ceil(days)
rh.gs(R.string.days_ago_round, days)
} else {
days = floor(days)
rh.gs(R.string.in_days_round, days)
}
}
return if (now() > time)
rh.gs(R.string.days_ago, days)
else
rh.gs(R.string.in_days, days)
}
override fun beginOfDay(mills: Long): Long {
val givenDate = Calendar.getInstance()
givenDate.timeInMillis = mills
givenDate[Calendar.HOUR_OF_DAY] = 0
givenDate[Calendar.MINUTE] = 0
givenDate[Calendar.SECOND] = 0
givenDate[Calendar.MILLISECOND] = 0
return givenDate.timeInMillis
}
override fun timeStringFromSeconds(seconds: Int): String {
val cached = timeStrings[seconds.toLong()]
if (cached != null) return cached
val t = timeString(secondsOfTheDayToMilliseconds(seconds))
timeStrings.put(seconds.toLong(), t)
return t
}
override fun timeFrameString(timeInMillis: Long, rh: ResourceHelper): String {
var remainingTimeMinutes = timeInMillis / (1000 * 60)
val remainingTimeHours = remainingTimeMinutes / 60
remainingTimeMinutes %= 60
return "(" + (if (remainingTimeHours > 0) remainingTimeHours.toString() + rh.gs(R.string.shorthour) + " " else "") + remainingTimeMinutes + "')"
}
override fun sinceString(timestamp: Long, rh: ResourceHelper): String {
return timeFrameString(System.currentTimeMillis() - timestamp, rh)
}
override fun untilString(timestamp: Long, rh: ResourceHelper): String {
return timeFrameString(timestamp - System.currentTimeMillis(), rh)
}
override fun now(): Long {
return System.currentTimeMillis()
}
override fun nowWithoutMilliseconds(): Long {
var n = System.currentTimeMillis()
n -= n % 1000
return n
}
override fun isOlderThan(date: Long, minutes: Long): Boolean {
val diff = now() - date
return diff > T.mins(minutes).msecs()
}
override fun getTimeZoneOffsetMs(): Long {
return GregorianCalendar().timeZone.rawOffset.toLong()
}
override fun getTimeZoneOffsetMinutes(timestamp: Long): Int {
return TimeZone.getDefault().getOffset(timestamp) / 60000
}
override fun isSameDay(timestamp1: Long, timestamp2: Long) = isSameDay(Date(timestamp1), Date(timestamp2))
override fun isSameDayGroup(timestamp1: Long, timestamp2: Long): Boolean {
val now = now()
if (now in (timestamp1 + 1) until timestamp2 || now in (timestamp2 + 1) until timestamp1)
return false
return isSameDay(Date(timestamp1), Date(timestamp2))
}
//Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}
override fun computeDiff(date1: Long, date2: Long): Map<TimeUnit, Long> {
val units: MutableList<TimeUnit> = ArrayList(EnumSet.allOf(TimeUnit::class.java))
units.reverse()
val result: MutableMap<TimeUnit, Long> = LinkedHashMap()
var millisecondsRest = date2 - date1
for (unit in units) {
val diff = unit.convert(millisecondsRest, TimeUnit.MILLISECONDS)
val diffInMillisecondsForUnit = unit.toMillis(diff)
millisecondsRest -= diffInMillisecondsForUnit
result[unit] = diff
}
return result
}
override fun age(milliseconds: Long, useShortText: Boolean, rh: ResourceHelper): String {
val diff = computeDiff(0L, milliseconds)
var days = " " + rh.gs(R.string.days) + " "
var hours = " " + rh.gs(R.string.hours) + " "
var minutes = " " + rh.gs(R.string.unit_minutes) + " "
if (useShortText) {
days = rh.gs(R.string.shortday)
hours = rh.gs(R.string.shorthour)
minutes = rh.gs(R.string.shortminute)
}
var result = ""
if (diff.getOrDefault(TimeUnit.DAYS, -1) > 0) result += diff[TimeUnit.DAYS].toString() + days
if (diff.getOrDefault(TimeUnit.HOURS, -1) > 0) result += diff[TimeUnit.HOURS].toString() + hours
if (diff[TimeUnit.DAYS] == 0L) result += diff[TimeUnit.MINUTES].toString() + minutes
return result
}
override fun niceTimeScalar(time: Long, rh: ResourceHelper): String {
var t = time
var unit = rh.gs(R.string.unit_second)
t /= 1000
if (t != 1L) unit = rh.gs(R.string.unit_seconds)
if (t > 59) {
unit = rh.gs(R.string.unit_minute)
t /= 60
if (t != 1L) unit = rh.gs(R.string.unit_minutes)
if (t > 59) {
unit = rh.gs(R.string.unit_hour)
t /= 60
if (t != 1L) unit = rh.gs(R.string.unit_hours)
if (t > 24) {
unit = rh.gs(R.string.unit_day)
t /= 24
if (t != 1L) unit = rh.gs(R.string.unit_days)
if (t > 28) {
unit = rh.gs(R.string.unit_week)
t /= 7
@Suppress("KotlinConstantConditions")
if (t != 1L) unit = rh.gs(R.string.unit_weeks)
}
}
}
}
//if (t != 1) unit = unit + "s"; //implemented plurality in every step, because in other languages plurality of time is not every time adding the same character
return qs(t.toDouble(), 0) + " " + unit
}
override fun qs(x: Double, numDigits: Int): String {
var digits = numDigits
if (digits == -1) {
digits = 0
if ((x.toInt() % x == 0.0)) {
digits++
if ((x.toInt() * 10 / 10).toDouble() != x) {
digits++
if ((x.toInt() * 100 / 100).toDouble() != x) digits++
}
}
}
if (dfs == null) {
val localDfs = DecimalFormatSymbols()
localDfs.decimalSeparator = '.'
dfs = localDfs // avoid race condition
}
val thisDf: DecimalFormat?
// use singleton if on ui thread otherwise allocate new as DecimalFormat is not thread safe
if (Thread.currentThread().id == 1L) {
if (df == null) {
val localDf = DecimalFormat("#", dfs)
localDf.minimumIntegerDigits = 1
df = localDf // avoid race condition
}
thisDf = df
} else {
thisDf = DecimalFormat("#", dfs)
}
thisDf?.maximumFractionDigits = digits
return thisDf?.format(x) ?: ""
}
override fun formatHHMM(timeAsSeconds: Int): String {
val hour = timeAsSeconds / 60 / 60
val minutes = (timeAsSeconds - hour * 60 * 60) / 60
val df = DecimalFormat("00")
return df.format(hour.toLong()) + ":" + df.format(minutes.toLong())
}
@RequiresApi(Build.VERSION_CODES.O)
override fun timeZoneByOffset(offsetInMilliseconds: Long): TimeZone =
TimeZone.getTimeZone(
if (offsetInMilliseconds == 0L) ZoneId.of("UTC")
else ZoneId.getAvailableZoneIds()
.stream()
.map(ZoneId::of)
.filter { z -> z.rules.getOffset(Instant.now()).totalSeconds == ZoneOffset.ofHours((offsetInMilliseconds / 1000 / 3600).toInt()).totalSeconds }
.collect(Collectors.toList())
.firstOrNull() ?: ZoneId.of("UTC")
)
override fun timeStampToUtcDateMillis(timestamp: Long): Long {
val current = Calendar.getInstance().apply { timeInMillis = timestamp }
return Calendar.getInstance().apply {
set(Calendar.YEAR, current[Calendar.YEAR])
set(Calendar.MONTH, current[Calendar.MONTH])
set(Calendar.DAY_OF_MONTH, current[Calendar.DAY_OF_MONTH])
}.timeInMillis
}
override fun mergeUtcDateToTimestamp(timestamp: Long, dateUtcMillis: Long): Long {
val selected = Calendar.getInstance().apply { timeInMillis = dateUtcMillis }
return Calendar.getInstance().apply {
timeInMillis = timestamp
set(Calendar.YEAR, selected[Calendar.YEAR])
set(Calendar.MONTH, selected[Calendar.MONTH])
set(Calendar.DAY_OF_MONTH, selected[Calendar.DAY_OF_MONTH])
}.timeInMillis
}
override fun mergeHourMinuteToTimestamp(timestamp: Long, hour: Int, minute: Int, randomSecond: Boolean): Long {
return Calendar.getInstance().apply {
timeInMillis = timestamp
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
if (randomSecond) set(Calendar.SECOND, seconds++)
}.timeInMillis
}
companion object {
private val timeStrings = LongSparseArray<String>()
private var seconds: Int = (SecureRandom().nextDouble() * 59.0).toInt()
// singletons to avoid repeated allocation
private var dfs: DecimalFormatSymbols? = null
private var df: DecimalFormat? = null
}
}

View file

@ -3,7 +3,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M0.103,0h24v24h-24z"/>
<path
android:fillColor="#FF000000"
android:pathData="M0.103,0h24v24h-24z" />
</vector>

View file

@ -0,0 +1,306 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="400dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#00000000"
android:pathData="M10.744,0.074L10.885,1.416"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M13.113,22.599L13.254,23.942"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M9.504,0.27L9.784,1.591"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M14.214,22.425L14.495,23.745"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M8.29,0.595L8.708,1.88"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M15.291,22.136L15.708,23.42"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M7.118,1.046L7.667,2.279"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M16.332,21.737L16.881,22.97"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M4.945,2.3L5.739,3.392"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M18.26,20.623L19.053,21.716"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M3.969,3.091L4.873,4.094"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19.126,19.922L20.029,20.925"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M3.081,3.979L4.084,4.882"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19.914,19.133L20.917,20.037"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M2.291,4.955L3.383,5.749"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M20.616,18.267L21.708,19.061"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M1.036,7.128L2.27,7.677"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M21.729,16.339L22.962,16.888"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0.586,8.3L1.871,8.718"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M22.128,15.298L23.412,15.715"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0.261,9.514L1.582,9.794"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M22.416,14.221L23.737,14.502"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0.065,10.754L1.408,10.895"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M22.591,13.12L23.934,13.261"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0.065,13.263L1.408,13.122"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M22.591,10.894L23.933,10.753"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0.262,14.503L1.582,14.223"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M22.416,9.793L23.737,9.512"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0.587,15.717L1.871,15.299"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M22.128,8.716L23.412,8.299"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M1.037,16.889L2.271,16.34"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M21.728,7.676L22.962,7.126"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M2.291,19.062L3.384,18.268"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M20.615,5.747L21.707,4.954"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M3.082,20.038L4.085,19.134"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19.913,4.881L20.917,3.978"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M3.97,20.926L4.874,19.923"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19.125,4.093L20.028,3.09"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M4.946,21.716L5.74,20.624"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M18.258,3.392L19.052,2.299"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M7.119,22.971L7.668,21.737"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M16.33,2.278L16.879,1.045"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M8.292,23.421L8.709,22.137"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M15.29,1.879L15.707,0.595"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M9.505,23.746L9.786,22.425"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M14.213,1.591L14.494,0.27"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M10.746,23.942L10.887,22.599"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M13.112,1.416L13.253,0.073"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M11.999,0.008L11.999,2.708"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M11.999,21.307L11.999,24.008"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M23.999,12L21.298,12"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M2.699,12L-0.001,12"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M5.999,1.616L7.349,3.954"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M16.648,20.061L17.999,22.4"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M22.387,6.001L20.048,7.351"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M3.941,16.651L1.602,18.001"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M1.606,6.008L3.945,7.358"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M20.052,16.658L22.391,18.008"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M17.992,1.612L16.642,3.95"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M7.342,20.057L5.992,22.396"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="400dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M11.999,4.906c-0.104,0 -0.188,0.084 -0.188,0.188V12c0,0.104 0.084,0.188 0.188,0.188c0.104,0 0.188,-0.084 0.188,-0.188V5.094C12.186,4.99 12.102,4.906 11.999,4.906zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="400dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M11.999,1.406c-0.104,0 -0.188,0.084 -0.188,0.188V12c0,0.104 0.084,0.188 0.188,0.188c0.104,0 0.188,-0.084 0.188,-0.188V1.594C12.186,1.49 12.102,1.406 11.999,1.406zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="400dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF1313"
android:pathData="M12.198,11.462v-0.237c0,-0.077 -0.05,-0.131 -0.114,-0.164V0.508c0,-0.047 -0.038,-0.086 -0.086,-0.086c-0.047,0 -0.086,0.038 -0.086,0.086v10.553c-0.063,0.033 -0.114,0.087 -0.114,0.164v0.238c-0.219,0.082 -0.376,0.29 -0.376,0.537s0.157,0.455 0.376,0.537v0.92c0,0.11 0.089,0.2 0.2,0.2s0.2,-0.089 0.2,-0.2v-0.919c0.221,-0.081 0.381,-0.289 0.381,-0.538S12.419,11.543 12.198,11.462zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z" />
</vector>

View file

@ -0,0 +1,66 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="400dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M11.999,0.008L11.999,2.708"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M11.999,21.307L11.999,24.008"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M23.999,12L21.298,12"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M2.699,12L-0.001,12"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M5.999,1.616L7.349,3.954"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M16.648,20.061L17.999,22.4"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M22.387,6.001L20.048,7.351"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M3.941,16.651L1.602,18.001"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M1.606,6.008L3.945,7.358"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M20.052,16.658L22.391,18.008"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M17.992,1.612L16.642,3.95"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:pathData="M7.342,20.057L5.992,22.396"
android:strokeWidth="0.1417"
android:strokeColor="#FFFFFF" />
</vector>

View file

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View file

@ -14,16 +14,18 @@ apply from: "${project.rootDir}/core/main/jacoco_global.gradle"
android {
namespace 'info.nightscout.sharedtests'
}
}
dependencies {
implementation project(':database:entities')
implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':core:interfaces')
implementation project(':core:main')
implementation project(':core:utils')
implementation project(':implementation')
api "org.mockito:mockito-junit-jupiter:$mockito_version"
api "org.mockito.kotlin:mockito-kotlin:5.1.0"
api "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version"

View file

@ -1,4 +1,7 @@
package info.nightscout.rx.logging
package info.nightscout.sharedtests
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
/**
* Created by adrian on 2019-12-27.

View file

@ -2,8 +2,7 @@ package info.nightscout.sharedtests
import android.annotation.SuppressLint
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.TestAapsSchedulers
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.sharedtests.rx.TestAapsSchedulers
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.ArgumentMatcher

View file

@ -19,11 +19,11 @@ import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.interfaces.profile.ProfileStore
import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.interfaces.utils.HardLimits
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.impl.rx.bus.RxBusImpl
import info.nightscout.shared.interfaces.ProfileUtil
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.DateUtilImpl
import org.json.JSONObject
import org.junit.jupiter.api.BeforeEach
import org.mockito.ArgumentMatchers.anyDouble
@ -45,11 +45,11 @@ open class TestBaseWithProfile : TestBase() {
@Mock lateinit var context: Context
@Mock lateinit var sp: SP
lateinit var dateUtil: DateUtil
lateinit var dateUtil: DateUtilImpl
lateinit var profileUtil: ProfileUtil
lateinit var decimalFormatter: DecimalFormatter
lateinit var hardLimits: HardLimits
val rxBus = RxBus(aapsSchedulers, aapsLogger)
val rxBus = RxBusImpl(aapsSchedulers, aapsLogger)
val profileInjector = HasAndroidInjector {
AndroidInjector {
@ -82,7 +82,7 @@ open class TestBaseWithProfile : TestBase() {
invalidProfileJSON = "{\"dia\":\"1\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," +
"{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," +
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
dateUtil = Mockito.spy(DateUtil(context))
dateUtil = Mockito.spy(DateUtilImpl(context))
decimalFormatter = DecimalFormatterImpl(rh)
profileUtil = ProfileUtilImpl(sp, decimalFormatter)
testPumpPlugin = TestPumpPlugin(profileInjector)

View file

@ -0,0 +1,17 @@
package info.nightscout.sharedtests.rx
import info.nightscout.rx.AapsSchedulers
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.schedulers.Schedulers
/**
* Created by adrian on 12.04.20.
*/
class TestAapsSchedulers : AapsSchedulers {
override val main: Scheduler = Schedulers.trampoline()
override val io: Scheduler = Schedulers.trampoline()
override val cpu: Scheduler = Schedulers.trampoline()
override val newThread: Scheduler = Schedulers.trampoline()
}

View file

@ -1,30 +1,15 @@
package info.nightscout.rx
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.schedulers.Schedulers
/**
* Created by adrian on 12.04.20.
*/
interface AapsSchedulers {
val main: Scheduler
val io: Scheduler
val cpu: Scheduler
val newThread: Scheduler
}
class DefaultAapsSchedulers : AapsSchedulers {
override val main: Scheduler = AndroidSchedulers.mainThread()
override val io: Scheduler = Schedulers.io()
override val cpu: Scheduler = Schedulers.computation()
override val newThread: Scheduler = Schedulers.newThread()
}
class TestAapsSchedulers : AapsSchedulers {
override val main: Scheduler = Schedulers.trampoline()
override val io: Scheduler = Schedulers.trampoline()
override val cpu: Scheduler = Schedulers.trampoline()
override val newThread: Scheduler = Schedulers.trampoline()
}

View file

@ -1,33 +1,13 @@
package info.nightscout.rx.bus
import info.nightscout.annotations.OpenForTesting
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.events.Event
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting
@Singleton
class RxBus @Inject constructor(
val aapsSchedulers: AapsSchedulers,
val aapsLogger: AAPSLogger
) {
interface RxBus {
private val publisher = PublishSubject.create<Event>()
fun send(event: Event) {
aapsLogger.debug(LTag.EVENTS, "Sending $event")
publisher.onNext(event)
}
fun send(event: Event)
// Listen should return an Observable and not the publisher
// Using ofType we filter only events that match that class type
fun <T : Any> toObservable(eventType: Class<T>): Observable<T> =
publisher
.subscribeOn(aapsSchedulers.io)
.ofType(eventType)
fun <T : Any> toObservable(eventType: Class<T>): Observable<T>
}

View file

@ -1,25 +0,0 @@
package info.nightscout.rx.di
import dagger.Module
import dagger.Provides
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.DefaultAapsSchedulers
import info.nightscout.rx.interfaces.L
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.AAPSLoggerProduction
import javax.inject.Singleton
@Module(
includes = [
]
)
open class RxModule {
@Provides
@Singleton
internal fun provideSchedulers(): AapsSchedulers = DefaultAapsSchedulers()
@Provides
@Singleton
fun provideAAPSLogger(l: L): AAPSLogger = AAPSLoggerProduction(l)
}

View file

@ -1,9 +0,0 @@
package info.nightscout.shared.di
import dagger.Module
@Module(
includes = [
]
)
open class SharedModule

View file

@ -1,52 +1,16 @@
package info.nightscout.shared.utils
import android.content.Context
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.collection.LongSparseArray
import info.nightscout.annotations.OpenForTesting
import info.nightscout.shared.R
import info.nightscout.shared.SafeParse
import info.nightscout.shared.interfaces.ResourceHelper
import org.apache.commons.lang3.time.DateUtils.isSameDay
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.ISODateTimeFormat
import java.security.SecureRandom
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.text.SimpleDateFormat
import java.time.Instant
import java.time.ZoneId
import java.time.ZoneOffset
import java.util.Calendar
import java.util.Date
import java.util.EnumSet
import java.util.GregorianCalendar
import java.util.Locale
import java.util.TimeZone
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import java.util.stream.Collectors
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.ceil
import kotlin.math.floor
/**
* The Class DateUtil. A simple wrapper around SimpleDateFormat to ease the handling of iso date string &lt;-&gt; date obj
* with TZ
*/
@OpenForTesting
@Singleton
class DateUtil @Inject constructor(private val context: Context) {
/**
* The date format in iso.
*/
@Suppress("PrivatePropertyName", "SpellCheckingInspection")
private val FORMAT_DATE_ISO_OUT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
interface DateUtil {
/**
* Takes in an ISO date string of the following format:
@ -55,11 +19,7 @@ class DateUtil @Inject constructor(private val context: Context) {
* @param isoDateString the iso date string
* @return the date
*/
fun fromISODateString(isoDateString: String): Long {
val parser = ISODateTimeFormat.dateTimeParser()
val dateTime = DateTime.parse(isoDateString, parser)
return dateTime.toDate().time
}
fun fromISODateString(isoDateString: String): Long
/**
* Render date
@ -67,404 +27,66 @@ class DateUtil @Inject constructor(private val context: Context) {
* @param date the date obj
* @return the iso-formatted date string
*/
fun toISOString(date: Long): String {
val f: DateFormat = SimpleDateFormat(FORMAT_DATE_ISO_OUT, Locale.getDefault())
f.timeZone = TimeZone.getTimeZone("UTC")
return f.format(date)
}
@Suppress("SpellCheckingInspection")
fun toISOAsUTC(timestamp: Long): String {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'0000Z'", Locale.US)
format.timeZone = TimeZone.getTimeZone("UTC")
return format.format(timestamp)
}
@Suppress("SpellCheckingInspection")
fun toISONoZone(timestamp: Long): String {
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
format.timeZone = TimeZone.getDefault()
return format.format(timestamp)
}
fun secondsOfTheDayToMilliseconds(seconds: Int): Long {
val calendar: Calendar = GregorianCalendar()
calendar[Calendar.MONTH] = 0 // Set january to be sure we miss DST changing
calendar[Calendar.HOUR_OF_DAY] = seconds / 60 / 60
calendar[Calendar.MINUTE] = seconds / 60 % 60
calendar[Calendar.SECOND] = 0
return calendar.timeInMillis
}
fun toSeconds(hhColonMm: String): Int {
val p = Pattern.compile("(\\d+):(\\d+)( a.m.| p.m.| AM| PM|AM|PM|)")
val m = p.matcher(hhColonMm)
var retVal = 0
if (m.find()) {
retVal = SafeParse.stringToInt(m.group(1)) * 60 * 60 + SafeParse.stringToInt(m.group(2)) * 60
if ((m.group(3) == " a.m." || m.group(3) == " AM" || m.group(3) == "AM") && m.group(1) == "12") retVal -= 12 * 60 * 60
if ((m.group(3) == " p.m." || m.group(3) == " PM" || m.group(3) == "PM") && m.group(1) != "12") retVal += 12 * 60 * 60
}
return retVal
}
fun dateString(mills: Long): String {
val df = DateFormat.getDateInstance(DateFormat.SHORT)
return df.format(mills)
}
fun dateStringRelative(mills: Long, rh: ResourceHelper): String {
val df = DateFormat.getDateInstance(DateFormat.SHORT)
val day = df.format(mills)
val beginOfToday = beginOfDay(now())
return if (mills < now()) // Past
when {
mills > beginOfToday -> rh.gs(R.string.today)
mills > beginOfToday - T.days(1).msecs() -> rh.gs(R.string.yesterday)
mills > beginOfToday - T.days(7).msecs() -> dayAgo(mills, rh, true)
else -> day
}
else // Future
when {
mills < beginOfToday + T.days(1).msecs() -> rh.gs(R.string.later_today)
mills < beginOfToday + T.days(2).msecs() -> rh.gs(R.string.tomorrow)
mills < beginOfToday + T.days(7).msecs() -> dayAgo(mills, rh, true)
else -> day
}
}
fun dateStringShort(mills: Long): String {
var format = "MM/dd"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "dd/MM"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
fun timeString(): String = timeString(now())
fun timeString(mills: Long): String {
var format = "hh:mma"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "HH:mm"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
fun secondString(): String = secondString(now())
fun secondString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("ss"))
fun minuteString(): String = minuteString(now())
fun minuteString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("mm"))
fun hourString(): String = hourString(now())
fun hourString(mills: Long): String {
var format = "hh"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "HH"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
fun amPm(): String = amPm(now())
fun amPm(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("a"))
fun dayNameString(format: String = "E"): String = dayNameString(now(), format)
fun dayNameString(mills: Long, format: String = "E"): String =
DateTime(mills).toString(DateTimeFormat.forPattern(format))
fun toISOString(date: Long): String
fun toISOAsUTC(timestamp: Long): String
fun toISONoZone(timestamp: Long): String
fun secondsOfTheDayToMilliseconds(seconds: Int): Long
fun toSeconds(hhColonMm: String): Int
fun dateString(mills: Long): String
fun dateStringRelative(mills: Long, rh: ResourceHelper): String
fun dateStringShort(mills: Long): String
fun timeString(): String
fun timeString(mills: Long): String
fun secondString(): String
fun secondString(mills: Long): String
fun minuteString(): String
fun minuteString(mills: Long): String
fun hourString(): String
fun hourString(mills: Long): String
fun amPm(): String
fun amPm(mills: Long): String
fun dayNameString(format: String = "E"): String
fun dayNameString(mills: Long, format: String = "E"): String
fun dayString(): String = dayString(now())
fun dayString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("dd"))
fun dayString(mills: Long): String
fun monthString(format: String = "MMM"): String
fun monthString(mills: Long, format: String = "MMM"): String
fun weekString(): String
fun weekString(mills: Long): String
fun timeStringWithSeconds(mills: Long): String
fun dateAndTimeRangeString(start: Long, end: Long): String
fun timeRangeString(start: Long, end: Long): String
fun dateAndTimeString(mills: Long): String
fun dateAndTimeAndSecondsString(mills: Long): String
fun minAgo(rh: ResourceHelper, time: Long?): String
fun minAgoShort(time: Long?): String
fun minAgoLong(rh: ResourceHelper, time: Long?): String
fun hourAgo(time: Long, rh: ResourceHelper): String
fun dayAgo(time: Long, rh: ResourceHelper, round: Boolean = false): String
fun beginOfDay(mills: Long): Long
fun timeStringFromSeconds(seconds: Int): String
fun timeFrameString(timeInMillis: Long, rh: ResourceHelper): String
fun sinceString(timestamp: Long, rh: ResourceHelper): String
fun untilString(timestamp: Long, rh: ResourceHelper): String
fun now(): Long
fun nowWithoutMilliseconds(): Long
fun isOlderThan(date: Long, minutes: Long): Boolean
fun getTimeZoneOffsetMs(): Long
fun getTimeZoneOffsetMinutes(timestamp: Long): Int
fun isSameDay(timestamp1: Long, timestamp2: Long): Boolean
fun monthString(format: String = "MMM"): String = monthString(now(), format)
fun monthString(mills: Long, format: String = "MMM"): String =
DateTime(mills).toString(DateTimeFormat.forPattern(format))
fun weekString(): String = weekString(now())
fun weekString(mills: Long): String =
DateTime(mills).toString(DateTimeFormat.forPattern("ww"))
fun timeStringWithSeconds(mills: Long): String {
var format = "hh:mm:ssa"
if (android.text.format.DateFormat.is24HourFormat(context)) {
format = "HH:mm:ss"
}
return DateTime(mills).toString(DateTimeFormat.forPattern(format))
}
fun dateAndTimeRangeString(start: Long, end: Long): String {
return dateAndTimeString(start) + " - " + timeString(end)
}
fun timeRangeString(start: Long, end: Long): String {
return timeString(start) + " - " + timeString(end)
}
fun dateAndTimeString(mills: Long): String {
return if (mills == 0L) "" else dateString(mills) + " " + timeString(mills)
}
fun dateAndTimeAndSecondsString(mills: Long): String {
return if (mills == 0L) "" else dateString(mills) + " " + timeStringWithSeconds(mills)
}
fun minAgo(rh: ResourceHelper, time: Long?): String {
if (time == null) return ""
val minutes = ((now() - time) / 1000 / 60).toInt()
return rh.gs(R.string.minago, minutes)
}
fun minAgoShort(time: Long?): String {
if (time == null) return ""
val minutes = ((time - now()) / 1000 / 60).toInt()
return (if (minutes > 0) "+" else "") + minutes
}
fun minAgoLong(rh: ResourceHelper, time: Long?): String {
if (time == null) return ""
val minutes = ((now() - time) / 1000 / 60).toInt()
return rh.gs(R.string.minago_long, minutes)
}
fun hourAgo(time: Long, rh: ResourceHelper): String {
val hours = (now() - time) / 1000.0 / 60 / 60
return rh.gs(R.string.hoursago, hours)
}
fun dayAgo(time: Long, rh: ResourceHelper, round: Boolean = false): String {
var days = (now() - time) / 1000.0 / 60 / 60 / 24
if (round) {
return if (now() > time) {
days = ceil(days)
rh.gs(R.string.days_ago_round, days)
} else {
days = floor(days)
rh.gs(R.string.in_days_round, days)
}
}
return if (now() > time)
rh.gs(R.string.days_ago, days)
else
rh.gs(R.string.in_days, days)
}
fun beginOfDay(mills: Long): Long {
val givenDate = Calendar.getInstance()
givenDate.timeInMillis = mills
givenDate[Calendar.HOUR_OF_DAY] = 0
givenDate[Calendar.MINUTE] = 0
givenDate[Calendar.SECOND] = 0
givenDate[Calendar.MILLISECOND] = 0
return givenDate.timeInMillis
}
fun timeStringFromSeconds(seconds: Int): String {
val cached = timeStrings[seconds.toLong()]
if (cached != null) return cached
val t = timeString(secondsOfTheDayToMilliseconds(seconds))
timeStrings.put(seconds.toLong(), t)
return t
}
fun timeFrameString(timeInMillis: Long, rh: ResourceHelper): String {
var remainingTimeMinutes = timeInMillis / (1000 * 60)
val remainingTimeHours = remainingTimeMinutes / 60
remainingTimeMinutes %= 60
return "(" + (if (remainingTimeHours > 0) remainingTimeHours.toString() + rh.gs(R.string.shorthour) + " " else "") + remainingTimeMinutes + "')"
}
fun sinceString(timestamp: Long, rh: ResourceHelper): String {
return timeFrameString(System.currentTimeMillis() - timestamp, rh)
}
fun untilString(timestamp: Long, rh: ResourceHelper): String {
return timeFrameString(timestamp - System.currentTimeMillis(), rh)
}
fun now(): Long {
return System.currentTimeMillis()
}
fun nowWithoutMilliseconds(): Long {
var n = System.currentTimeMillis()
n -= n % 1000
return n
}
fun isOlderThan(date: Long, minutes: Long): Boolean {
val diff = now() - date
return diff > T.mins(minutes).msecs()
}
fun getTimeZoneOffsetMs(): Long {
return GregorianCalendar().timeZone.rawOffset.toLong()
}
fun getTimeZoneOffsetMinutes(timestamp: Long): Int {
return TimeZone.getDefault().getOffset(timestamp) / 60000
}
fun isSameDay(timestamp1: Long, timestamp2: Long) = isSameDay(Date(timestamp1), Date(timestamp2))
fun isSameDayGroup(timestamp1: Long, timestamp2: Long): Boolean {
val now = now()
if (now in (timestamp1 + 1) until timestamp2 || now in (timestamp2 + 1) until timestamp1)
return false
return isSameDay(Date(timestamp1), Date(timestamp2))
}
fun isSameDayGroup(timestamp1: Long, timestamp2: Long): Boolean
//Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}
fun computeDiff(date1: Long, date2: Long): Map<TimeUnit, Long> {
val units: MutableList<TimeUnit> = ArrayList(EnumSet.allOf(TimeUnit::class.java))
units.reverse()
val result: MutableMap<TimeUnit, Long> = LinkedHashMap()
var millisecondsRest = date2 - date1
for (unit in units) {
val diff = unit.convert(millisecondsRest, TimeUnit.MILLISECONDS)
val diffInMillisecondsForUnit = unit.toMillis(diff)
millisecondsRest -= diffInMillisecondsForUnit
result[unit] = diff
}
return result
}
fun age(milliseconds: Long, useShortText: Boolean, rh: ResourceHelper): String {
val diff = computeDiff(0L, milliseconds)
var days = " " + rh.gs(R.string.days) + " "
var hours = " " + rh.gs(R.string.hours) + " "
var minutes = " " + rh.gs(R.string.unit_minutes) + " "
if (useShortText) {
days = rh.gs(R.string.shortday)
hours = rh.gs(R.string.shorthour)
minutes = rh.gs(R.string.shortminute)
}
var result = ""
if (diff.getOrDefault(TimeUnit.DAYS, -1) > 0) result += diff[TimeUnit.DAYS].toString() + days
if (diff.getOrDefault(TimeUnit.HOURS, -1) > 0) result += diff[TimeUnit.HOURS].toString() + hours
if (diff[TimeUnit.DAYS] == 0L) result += diff[TimeUnit.MINUTES].toString() + minutes
return result
}
fun niceTimeScalar(time: Long, rh: ResourceHelper): String {
var t = time
var unit = rh.gs(R.string.unit_second)
t /= 1000
if (t != 1L) unit = rh.gs(R.string.unit_seconds)
if (t > 59) {
unit = rh.gs(R.string.unit_minute)
t /= 60
if (t != 1L) unit = rh.gs(R.string.unit_minutes)
if (t > 59) {
unit = rh.gs(R.string.unit_hour)
t /= 60
if (t != 1L) unit = rh.gs(R.string.unit_hours)
if (t > 24) {
unit = rh.gs(R.string.unit_day)
t /= 24
if (t != 1L) unit = rh.gs(R.string.unit_days)
if (t > 28) {
unit = rh.gs(R.string.unit_week)
t /= 7
@Suppress("KotlinConstantConditions")
if (t != 1L) unit = rh.gs(R.string.unit_weeks)
}
}
}
}
//if (t != 1) unit = unit + "s"; //implemented plurality in every step, because in other languages plurality of time is not every time adding the same character
return qs(t.toDouble(), 0) + " " + unit
}
fun qs(x: Double, numDigits: Int): String {
var digits = numDigits
if (digits == -1) {
digits = 0
if ((x.toInt() % x == 0.0)) {
digits++
if ((x.toInt() * 10 / 10).toDouble() != x) {
digits++
if ((x.toInt() * 100 / 100).toDouble() != x) digits++
}
}
}
if (dfs == null) {
val localDfs = DecimalFormatSymbols()
localDfs.decimalSeparator = '.'
dfs = localDfs // avoid race condition
}
val thisDf: DecimalFormat?
// use singleton if on ui thread otherwise allocate new as DecimalFormat is not thread safe
if (Thread.currentThread().id == 1L) {
if (df == null) {
val localDf = DecimalFormat("#", dfs)
localDf.minimumIntegerDigits = 1
df = localDf // avoid race condition
}
thisDf = df
} else {
thisDf = DecimalFormat("#", dfs)
}
thisDf?.maximumFractionDigits = digits
return thisDf?.format(x) ?: ""
}
fun formatHHMM(timeAsSeconds: Int): String {
val hour = timeAsSeconds / 60 / 60
val minutes = (timeAsSeconds - hour * 60 * 60) / 60
val df = DecimalFormat("00")
return df.format(hour.toLong()) + ":" + df.format(minutes.toLong())
}
fun computeDiff(date1: Long, date2: Long): Map<TimeUnit, Long>
fun age(milliseconds: Long, useShortText: Boolean, rh: ResourceHelper): String
fun niceTimeScalar(time: Long, rh: ResourceHelper): String
fun qs(x: Double, numDigits: Int): String
fun formatHHMM(timeAsSeconds: Int): String
@RequiresApi(Build.VERSION_CODES.O)
fun timeZoneByOffset(offsetInMilliseconds: Long): TimeZone =
TimeZone.getTimeZone(
if (offsetInMilliseconds == 0L) ZoneId.of("UTC")
else ZoneId.getAvailableZoneIds()
.stream()
.map(ZoneId::of)
.filter { z -> z.rules.getOffset(Instant.now()).totalSeconds == ZoneOffset.ofHours((offsetInMilliseconds / 1000 / 3600).toInt()).totalSeconds }
.collect(Collectors.toList())
.firstOrNull() ?: ZoneId.of("UTC")
)
fun timeStampToUtcDateMillis(timestamp: Long): Long {
val current = Calendar.getInstance().apply { timeInMillis = timestamp }
return Calendar.getInstance().apply {
set(Calendar.YEAR, current[Calendar.YEAR])
set(Calendar.MONTH, current[Calendar.MONTH])
set(Calendar.DAY_OF_MONTH, current[Calendar.DAY_OF_MONTH])
}.timeInMillis
}
fun mergeUtcDateToTimestamp(timestamp: Long, dateUtcMillis: Long): Long {
val selected = Calendar.getInstance().apply { timeInMillis = dateUtcMillis }
return Calendar.getInstance().apply {
timeInMillis = timestamp
set(Calendar.YEAR, selected[Calendar.YEAR])
set(Calendar.MONTH, selected[Calendar.MONTH])
set(Calendar.DAY_OF_MONTH, selected[Calendar.DAY_OF_MONTH])
}.timeInMillis
}
fun mergeHourMinuteToTimestamp(timestamp: Long, hour: Int, minute: Int, randomSecond: Boolean = false): Long {
return Calendar.getInstance().apply {
timeInMillis = timestamp
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
if (randomSecond) set(Calendar.SECOND, seconds++)
}.timeInMillis
}
companion object {
private val timeStrings = LongSparseArray<String>()
private var seconds: Int = (SecureRandom().nextDouble() * 59.0).toInt()
// singletons to avoid repeated allocation
private var dfs: DecimalFormatSymbols? = null
private var df: DecimalFormat? = null
}
fun timeZoneByOffset(offsetInMilliseconds: Long): TimeZone
fun timeStampToUtcDateMillis(timestamp: Long): Long
fun mergeUtcDateToTimestamp(timestamp: Long, dateUtcMillis: Long): Long
fun mergeHourMinuteToTimestamp(timestamp: Long, hour: Int, minute: Int, randomSecond: Boolean = false): Long
}

View file

@ -1,183 +0,0 @@
<vector android:height="400dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#00000000"
android:pathData="M10.744,0.074L10.885,1.416"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M13.113,22.599L13.254,23.942"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M9.504,0.27L9.784,1.591"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M14.214,22.425L14.495,23.745"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M8.29,0.595L8.708,1.88"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M15.291,22.136L15.708,23.42"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M7.118,1.046L7.667,2.279"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M16.332,21.737L16.881,22.97"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M4.945,2.3L5.739,3.392"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M18.26,20.623L19.053,21.716"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M3.969,3.091L4.873,4.094"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M19.126,19.922L20.029,20.925"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M3.081,3.979L4.084,4.882"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M19.914,19.133L20.917,20.037"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M2.291,4.955L3.383,5.749"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M20.616,18.267L21.708,19.061"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M1.036,7.128L2.27,7.677"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M21.729,16.339L22.962,16.888"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M0.586,8.3L1.871,8.718"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M22.128,15.298L23.412,15.715"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M0.261,9.514L1.582,9.794"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M22.416,14.221L23.737,14.502"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M0.065,10.754L1.408,10.895"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M22.591,13.12L23.934,13.261"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M0.065,13.263L1.408,13.122"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M22.591,10.894L23.933,10.753"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M0.262,14.503L1.582,14.223"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M22.416,9.793L23.737,9.512"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M0.587,15.717L1.871,15.299"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M22.128,8.716L23.412,8.299"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M1.037,16.889L2.271,16.34"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M21.728,7.676L22.962,7.126"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M2.291,19.062L3.384,18.268"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M20.615,5.747L21.707,4.954"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M3.082,20.038L4.085,19.134"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M19.913,4.881L20.917,3.978"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M3.97,20.926L4.874,19.923"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M19.125,4.093L20.028,3.09"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M4.946,21.716L5.74,20.624"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M18.258,3.392L19.052,2.299"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M7.119,22.971L7.668,21.737"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M16.33,2.278L16.879,1.045"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M8.292,23.421L8.709,22.137"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M15.29,1.879L15.707,0.595"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M9.505,23.746L9.786,22.425"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M14.213,1.591L14.494,0.27"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M10.746,23.942L10.887,22.599"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#00000000"
android:pathData="M13.112,1.416L13.253,0.073"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M11.999,0.008L11.999,2.708"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M11.999,21.307L11.999,24.008"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M23.999,12L21.298,12"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M2.699,12L-0.001,12"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M5.999,1.616L7.349,3.954"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M16.648,20.061L17.999,22.4"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M22.387,6.001L20.048,7.351"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M3.941,16.651L1.602,18.001"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M1.606,6.008L3.945,7.358"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M20.052,16.658L22.391,18.008"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M17.992,1.612L16.642,3.95"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M7.342,20.057L5.992,22.396"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
</vector>

View file

@ -1,4 +0,0 @@
<vector android:height="400dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M11.999,4.906c-0.104,0 -0.188,0.084 -0.188,0.188V12c0,0.104 0.084,0.188 0.188,0.188c0.104,0 0.188,-0.084 0.188,-0.188V5.094C12.186,4.99 12.102,4.906 11.999,4.906zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z"/>
</vector>

View file

@ -1,4 +0,0 @@
<vector android:height="400dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF" android:pathData="M11.999,1.406c-0.104,0 -0.188,0.084 -0.188,0.188V12c0,0.104 0.084,0.188 0.188,0.188c0.104,0 0.188,-0.084 0.188,-0.188V1.594C12.186,1.49 12.102,1.406 11.999,1.406zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z"/>
</vector>

View file

@ -1,4 +0,0 @@
<vector android:height="400dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF1313" android:pathData="M12.198,11.462v-0.237c0,-0.077 -0.05,-0.131 -0.114,-0.164V0.508c0,-0.047 -0.038,-0.086 -0.086,-0.086c-0.047,0 -0.086,0.038 -0.086,0.086v10.553c-0.063,0.033 -0.114,0.087 -0.114,0.164v0.238c-0.219,0.082 -0.376,0.29 -0.376,0.537s0.157,0.455 0.376,0.537v0.92c0,0.11 0.089,0.2 0.2,0.2s0.2,-0.089 0.2,-0.2v-0.919c0.221,-0.081 0.381,-0.289 0.381,-0.538S12.419,11.543 12.198,11.462zM11.999,12.094c-0.047,0 -0.086,-0.038 -0.086,-0.086s0.039,-0.086 0.086,-0.086c0.047,0 0.086,0.038 0.086,0.086S12.046,12.094 11.999,12.094z"/>
</vector>

View file

@ -1,39 +0,0 @@
<vector android:height="400dp" android:viewportHeight="24"
android:viewportWidth="24" android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFF"
android:pathData="M11.999,0.008L11.999,2.708"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M11.999,21.307L11.999,24.008"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M23.999,12L21.298,12"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M2.699,12L-0.001,12"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M5.999,1.616L7.349,3.954"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M16.648,20.061L17.999,22.4"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M22.387,6.001L20.048,7.351"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M3.941,16.651L1.602,18.001"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M1.606,6.008L3.945,7.358"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M20.052,16.658L22.391,18.008"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M17.992,1.612L16.642,3.95"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
<path android:fillColor="#FFFFFF"
android:pathData="M7.342,20.057L5.992,22.396"
android:strokeColor="#FFFFFF" android:strokeWidth="0.1417"/>
</vector>

View file

@ -8,7 +8,6 @@ import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.insight.di.InsightDatabaseModule
import info.nightscout.androidaps.insight.di.InsightModule
import info.nightscout.plugins.sync.di.OpenHumansModule
import info.nightscout.androidaps.plugins.pump.common.di.RileyLinkModule
import info.nightscout.androidaps.plugins.pump.eopatch.dagger.EopatchModule
import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule
@ -24,6 +23,7 @@ import info.nightscout.insulin.di.InsulinModule
import info.nightscout.plugins.aps.di.ApsModule
import info.nightscout.plugins.constraints.di.PluginsConstraintsModule
import info.nightscout.plugins.di.PluginsModule
import info.nightscout.plugins.sync.di.OpenHumansModule
import info.nightscout.plugins.sync.di.SyncModule
import info.nightscout.pump.combo.di.ComboModule
import info.nightscout.pump.combov2.di.ComboV2Module
@ -31,11 +31,9 @@ import info.nightscout.pump.common.di.PumpCommonModule
import info.nightscout.pump.dana.di.DanaHistoryModule
import info.nightscout.pump.dana.di.DanaModule
import info.nightscout.pump.danars.di.DanaRSModule
import info.nightscout.pump.medtrum.di.MedtrumModule
import info.nightscout.pump.diaconn.di.DiaconnG8Module
import info.nightscout.pump.medtrum.di.MedtrumModule
import info.nightscout.pump.virtual.di.VirtualPumpModule
import info.nightscout.rx.di.RxModule
import info.nightscout.shared.di.SharedModule
import info.nightscout.shared.impl.di.SharedImplModule
import info.nightscout.source.di.SourceModule
import info.nightscout.ui.di.UiModule
@ -62,8 +60,6 @@ import javax.inject.Singleton
InsulinModule::class,
OpenHumansModule::class,
PluginsModule::class,
RxModule::class,
SharedModule::class,
SharedImplModule::class,
UiModule::class,
ValidatorsModule::class,

View file

@ -0,0 +1,3 @@
<resources>
<bool name="isTablet">true</bool>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<bool name="isTablet">false</bool>
</resources>

View file

@ -17,5 +17,6 @@ android {
dependencies {
implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
api "com.google.android.material:material:$material_version"
}

View file

@ -32,7 +32,6 @@ import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventPreferenceChange
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import info.nightscout.shared.extensions.runOnUiThread
import info.nightscout.shared.extensions.toVisibility
import info.nightscout.shared.interfaces.ResourceHelper
import io.reactivex.rxjava3.core.Completable
@ -106,7 +105,7 @@ class MaintenanceFragment : DaggerFragment() {
onError = { aapsLogger.error("Error clearing databases", it) },
onComplete = {
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.utils.R.string.key_units)))
runOnUiThread { activity.recreate() }
info.nightscout.shared.extensions.runOnUiThread { activity.recreate() }
}
)
uel.log(Action.RESET_DATABASES, Sources.Maintenance)

View file

@ -16,6 +16,7 @@ android {
dependencies {
implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':database:entities')
implementation project(':database:impl')
implementation project(':core:graphview')

View file

@ -34,6 +34,7 @@ import info.nightscout.rx.events.EventNewHistoryData
import info.nightscout.rx.events.EventPumpStatusChanged
import info.nightscout.rx.events.EventUpdateOverviewCalcProgress
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.shared.impl.rx.bus.RxBusImpl
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -72,7 +73,7 @@ class OverviewPlugin @Inject constructor(
private var disposable: CompositeDisposable = CompositeDisposable()
override val overviewBus = RxBus(aapsSchedulers, aapsLogger)
override val overviewBus = RxBusImpl(aapsSchedulers, aapsLogger)
override fun addNotificationWithDialogResponse(id: Int, text: String, level: Int, @StringRes actionButtonId: Int, title: String, message: String) {
rxBus.send(

View file

@ -17,6 +17,7 @@ android {
dependencies {
implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':database:entities')
implementation project(':database:impl')
implementation project(':core:interfaces')

View file

@ -16,6 +16,7 @@ android {
dependencies {
implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':database:entities')
implementation project(':database:impl')
implementation project(':core:main')

View file

@ -19,7 +19,6 @@ import info.nightscout.pump.combo.ruffyscripter.PumpState
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventQueueChanged
import info.nightscout.shared.extensions.runOnUiThread
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -62,7 +61,7 @@ class ComboFragment : DaggerFragment() {
binding.comboRefreshButton.isEnabled = false
commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.user_request), object : Callback() {
override fun run() {
runOnUiThread { binding.comboRefreshButton.isEnabled = true }
activity?.runOnUiThread { binding.comboRefreshButton.isEnabled = true }
}
})
}
@ -153,14 +152,17 @@ class ComboFragment : DaggerFragment() {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
binding.comboInsulinstate.setTypeface(null, Typeface.NORMAL)
}
PumpState.LOW -> {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.omniYellowColor))
binding.comboInsulinstate.setTypeface(null, Typeface.BOLD)
}
PumpState.EMPTY -> {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.warningColor))
binding.comboInsulinstate.setTypeface(null, Typeface.BOLD)
}
else -> {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
binding.comboInsulinstate.setTypeface(null, Typeface.NORMAL)
@ -175,10 +177,12 @@ class ComboFragment : DaggerFragment() {
binding.comboLastconnection.setText(R.string.combo_pump_connected_now)
binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
}
comboPlugin.pump.lastSuccessfulCmdTime + 30 * 60 * 1000 < System.currentTimeMillis() -> {
binding.comboLastconnection.text = rh.gs(R.string.combo_no_pump_connection, min)
binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.warningColor))
}
else -> {
binding.comboLastconnection.text = minAgo
binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))

View file

@ -26,6 +26,7 @@ android {
dependencies {
implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':core:libraries')
implementation project(':core:interfaces')
implementation project(':core:utils')

View file

@ -27,6 +27,7 @@ import javax.inject.Inject
import javax.inject.Singleton
interface IAlarmRegistry {
fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean = false): Maybe<AlarmCode>
fun add(patchAeCodes: Set<PatchAeCode>)
fun remove(alarmCode: AlarmCode): Maybe<AlarmCode>
@ -34,11 +35,13 @@ interface IAlarmRegistry {
@Singleton
class AlarmRegistry @Inject constructor() : IAlarmRegistry {
@Inject lateinit var mContext: Context
@Inject lateinit var pm: IPreferenceManager
@Inject lateinit var rxBus: RxBus
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dateUtil: DateUtil
private lateinit var mOsAlarmManager: AlarmManager
private var mDisposable: Disposable? = null
@ -49,19 +52,21 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
mDisposable = pm.observePatchLifeCycle()
.observeOn(aapsSchedulers.main)
.subscribe {
when(it){
when (it) {
PatchLifecycle.REMOVE_NEEDLE_CAP -> {
val triggerAfter = pm.getPatchConfig().patchWakeupTimestamp + TimeUnit.HOURS.toMillis(1) - System.currentTimeMillis()
compositeDisposable.add(add(AlarmCode.A020, triggerAfter).subscribe())
}
PatchLifecycle.ACTIVATED -> {
PatchLifecycle.ACTIVATED -> {
}
PatchLifecycle.SHUTDOWN -> {
PatchLifecycle.SHUTDOWN -> {
val sources = ArrayList<Maybe<*>>()
sources.add(Maybe.just(true))
pm.getAlarms().occurred.let{ occurredAlarms ->
if(occurredAlarms.isNotEmpty()){
pm.getAlarms().occurred.let { occurredAlarms ->
if (occurredAlarms.isNotEmpty()) {
occurredAlarms.keys.forEach { alarmCode ->
sources.add(
Maybe.just(alarmCode)
@ -71,30 +76,30 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
}
}
}
pm.getAlarms().registered.let{ registeredAlarms ->
if(registeredAlarms.isNotEmpty()){
pm.getAlarms().registered.let { registeredAlarms ->
if (registeredAlarms.isNotEmpty()) {
registeredAlarms.keys.forEach { alarmCode ->
sources.add(remove(alarmCode))
}
}
}
compositeDisposable.add(Maybe.concat(sources)
.subscribe {
pm.getAlarms().clear()
pm.flushAlarms()
}
.subscribe {
pm.getAlarms().clear()
pm.flushAlarms()
}
)
}
else -> Unit
else -> Unit
}
}
}
override fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean): Maybe<AlarmCode> {
if(pm.getAlarms().occurred.containsKey(alarmCode)){
if (pm.getAlarms().occurred.containsKey(alarmCode)) {
return Maybe.just(alarmCode)
}else {
} else {
val triggerTimeMilli = System.currentTimeMillis() + triggerAfter
pm.getAlarms().register(alarmCode, triggerAfter)
pm.flushAlarms()
@ -109,11 +114,11 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
override fun add(patchAeCodes: Set<PatchAeCode>) {
compositeDisposable.add(
Observable.fromIterable(patchAeCodes)
.filter{patchAeCodeItem -> AlarmCode.findByPatchAeCode(patchAeCodeItem.aeValue) != null}
.observeOn(aapsSchedulers.main)
.filter { aeCodes -> AlarmCode.findByPatchAeCode(aeCodes.aeValue) != null }
.flatMapMaybe{aeCodeResponse -> add(AlarmCode.findByPatchAeCode(aeCodeResponse.aeValue)!!, 0L, true)}
.subscribe()
.filter { patchAeCodeItem -> AlarmCode.findByPatchAeCode(patchAeCodeItem.aeValue) != null }
.observeOn(aapsSchedulers.main)
.filter { aeCodes -> AlarmCode.findByPatchAeCode(aeCodes.aeValue) != null }
.flatMapMaybe { aeCodeResponse -> add(AlarmCode.findByPatchAeCode(aeCodeResponse.aeValue)!!, 0L, true) }
.subscribe()
)
}
@ -121,21 +126,21 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
return Maybe.fromCallable {
cancelOsAlarmInternal(alarmCode)
val pendingIntent = createPendingIntent(alarmCode, 0)
aapsLogger.debug("[${alarmCode}] OS Alarm added. ${DateUtil(mContext).toISOString(triggerTime)}")
aapsLogger.debug("[${alarmCode}] OS Alarm added. ${dateUtil.toISOString(triggerTime)}")
mOsAlarmManager.setAlarmClock(AlarmClockInfo(triggerTime, pendingIntent), pendingIntent)
alarmCode
}
}
override fun remove(alarmCode: AlarmCode): Maybe<AlarmCode> {
return if(pm.getAlarms().registered.containsKey(alarmCode)) {
return if (pm.getAlarms().registered.containsKey(alarmCode)) {
cancelOsAlarms(alarmCode)
.doOnSuccess {
pm.getAlarms().unregister(alarmCode)
pm.flushAlarms()
}
.map { alarmCode }
}else{
} else {
Maybe.just(alarmCode)
}
}

View file

@ -8,7 +8,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.Omnipod
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.database.DashHistoryDatabase
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.database.HistoryRecordDao
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.mapper.HistoryMapper
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.sharedtests.AAPSLoggerTest
import org.junit.Before
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test

View file

@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.endecry
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
import info.nightscout.core.utils.toHex
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.sharedtests.AAPSLoggerTest
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.spongycastle.util.encoders.Hex

View file

@ -4,7 +4,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.Rand
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.util.X25519KeyGenerator
import info.nightscout.core.utils.toHex
import info.nightscout.interfaces.Config
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.sharedtests.AAPSLoggerTest
import info.nightscout.sharedtests.TestBase
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
import info.nightscout.core.utils.toHex
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.sharedtests.AAPSLoggerTest
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.spongycastle.util.encoders.Hex

View file

@ -2,7 +2,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.session
import info.nightscout.core.utils.toHex
import info.nightscout.interfaces.Config
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.sharedtests.AAPSLoggerTest
import info.nightscout.sharedtests.TestBase
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test

View file

@ -37,6 +37,8 @@ dependencies {
implementation project(':pump:rileylink')
implementation project(':pump:omnipod-common')
testImplementation project(':app-wear-shared:shared-impl')
api "androidx.room:room-ktx:$room_version"
api "androidx.room:room-runtime:$room_version"
api "androidx.room:room-rxjava3:$room_version"

View file

@ -2,10 +2,10 @@ package info.nightscout.androidaps.plugins.pump.omnipod.eros.manager
import info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.FirmwareVersion
import info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.PodProgressStatus
import info.nightscout.rx.TestAapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.impl.rx.bus.RxBusImpl
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.sharedtests.TestBase
import info.nightscout.sharedtests.rx.TestAapsSchedulers
import org.joda.time.DateTime
import org.joda.time.DateTimeUtils
import org.joda.time.DateTimeZone
@ -19,7 +19,7 @@ class AapsErosPodStateManagerTest : TestBase() {
@Mock lateinit var sp: SP
private val rxBus = RxBus(TestAapsSchedulers(), aapsLogger)
private val rxBus = RxBusImpl(TestAapsSchedulers(), aapsLogger)
@Test fun times() {
val timeZone = DateTimeZone.UTC

View file

@ -6,8 +6,6 @@ import dagger.Module
import dagger.Provides
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.WearApp
import info.nightscout.rx.di.RxModule
import info.nightscout.shared.di.SharedModule
import info.nightscout.shared.impl.di.SharedImplModule
import kotlinx.datetime.Clock
@ -16,8 +14,6 @@ import kotlinx.datetime.Clock
includes = [
WearModule.AppBindings::class,
WearActivitiesModule::class,
RxModule::class,
SharedModule::class,
SharedImplModule::class
]
)

View file

@ -35,15 +35,15 @@ import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace
import info.nightscout.rx.logging.LTag
import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfData
import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.CwfResDataMap
import info.nightscout.rx.weardata.CwfMetadataKey
import info.nightscout.rx.weardata.CwfMetadataMap
import info.nightscout.rx.weardata.ResData
import info.nightscout.rx.weardata.ResFormat
import info.nightscout.rx.weardata.CwfResDataMap
import info.nightscout.rx.weardata.EventData
import info.nightscout.rx.weardata.JsonKeyValues
import info.nightscout.rx.weardata.JsonKeys.*
import info.nightscout.rx.weardata.ResData
import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.ResFormat
import info.nightscout.rx.weardata.ViewKeys
import info.nightscout.rx.weardata.ZipWatchfaceFormat
import info.nightscout.shared.extensions.toVisibility
@ -208,7 +208,7 @@ class CustomWatchface : BaseWatchFace() {
it.clearColorFilter()
view.setImageDrawable(it)
} ?: apply {
view.setImageDrawable(id.defaultDrawable?.let {resources.getDrawable(it)})
view.setImageDrawable(id.defaultDrawable?.let { resources.getDrawable(it) })
if (viewJson.has(COLOR.key))
view.setColorFilter(getColor(viewJson.optString(COLOR.key)))
else
@ -217,7 +217,7 @@ class CustomWatchface : BaseWatchFace() {
}
}
} ?:apply {
} ?: apply {
view.visibility = View.GONE
if (view is TextView) {
view.text = ""
@ -301,7 +301,7 @@ class CustomWatchface : BaseWatchFace() {
}
val metadataMap = ZipWatchfaceFormat.loadMetadata(json)
val drawableDataMap: CwfResDataMap = mutableMapOf()
getResourceByteArray(info.nightscout.shared.R.drawable.watchface_custom)?.let {
getResourceByteArray(info.nightscout.shared.impl.R.drawable.watchface_custom)?.let {
drawableDataMap[ResFileMap.CUSTOM_WATCHFACE.fileName] = ResData(it, ResFormat.PNG)
}
return EventData.ActionSetCustomWatchface(CwfData(json.toString(4), metadataMap, drawableDataMap))
@ -394,7 +394,7 @@ class CustomWatchface : BaseWatchFace() {
@StringRes val pref: Int?,
@IdRes val defaultDrawable: Int?,
val customDrawable: ResFileMap?,
val customHigh:ResFileMap?,
val customHigh: ResFileMap?,
val customLow: ResFileMap?
) {
@ -402,7 +402,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.BACKGROUND.key,
R.id.background,
null,
info.nightscout.shared.R.drawable.background,
info.nightscout.shared.impl.R.drawable.background,
ResFileMap.BACKGROUND,
ResFileMap.BACKGROUND_HIGH,
ResFileMap.BACKGROUND_LOW
@ -448,7 +448,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.COVER_PLATE.key,
R.id.cover_plate,
null,
info.nightscout.shared.R.drawable.simplified_dial,
info.nightscout.shared.impl.R.drawable.simplified_dial,
ResFileMap.COVER_PLATE,
ResFileMap.COVER_PLATE_HIGH,
ResFileMap.COVER_PLATE_LOW
@ -457,7 +457,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.HOUR_HAND.key,
R.id.hour_hand,
null,
info.nightscout.shared.R.drawable.hour_hand,
info.nightscout.shared.impl.R.drawable.hour_hand,
ResFileMap.HOUR_HAND,
ResFileMap.HOUR_HAND_HIGH,
ResFileMap.HOUR_HAND_LOW
@ -466,7 +466,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.MINUTE_HAND.key,
R.id.minute_hand,
null,
info.nightscout.shared.R.drawable.minute_hand,
info.nightscout.shared.impl.R.drawable.minute_hand,
ResFileMap.MINUTE_HAND,
ResFileMap.MINUTE_HAND_HIGH,
ResFileMap.MINUTE_HAND_LOW
@ -475,7 +475,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.SECOND_HAND.key,
R.id.second_hand,
R.string.key_show_seconds,
info.nightscout.shared.R.drawable.second_hand,
info.nightscout.shared.impl.R.drawable.second_hand,
ResFileMap.SECOND_HAND,
ResFileMap.SECOND_HAND_HIGH,
ResFileMap.SECOND_HAND_LOW
@ -491,16 +491,25 @@ class CustomWatchface : BaseWatchFace() {
fun drawable(resources: Resources, drawableDataMap: CwfResDataMap, sgvLevel: Long): Drawable? = customDrawable?.let { cd ->
when (sgvLevel) {
1L -> { customHigh?.let {drawableDataMap[customHigh.fileName]}?.toDrawable(resources) ?: drawableDataMap[cd.fileName]?.toDrawable(resources) }
0L -> { drawableDataMap[cd.fileName]?.toDrawable(resources) }
-1L -> { customLow?.let {drawableDataMap[customLow.fileName]}?.toDrawable(resources) ?: drawableDataMap[cd.fileName]?.toDrawable(resources) }
1L -> {
customHigh?.let { drawableDataMap[customHigh.fileName] }?.toDrawable(resources) ?: drawableDataMap[cd.fileName]?.toDrawable(resources)
}
0L -> {
drawableDataMap[cd.fileName]?.toDrawable(resources)
}
-1L -> {
customLow?.let { drawableDataMap[customLow.fileName] }?.toDrawable(resources) ?: drawableDataMap[cd.fileName]?.toDrawable(resources)
}
else -> drawableDataMap[cd.fileName]?.toDrawable(resources)
}
}
}
}
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int,val customDrawable: ResFileMap?) {
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int, val customDrawable: ResFileMap?) {
NONE("??", R.drawable.ic_invalid, ResFileMap.ARROW_NONE),
TRIPLE_UP("X", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
DOUBLE_UP("\u21c8", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
@ -515,8 +524,8 @@ private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int,
companion object {
fun drawable(direction: String?, resources: Resources, drawableDataMap: CwfResDataMap): Drawable {
val arrow = values().firstOrNull { it.symbol == direction } ?:NONE
return arrow.customDrawable?. let {drawableDataMap[arrow.customDrawable.fileName]}?.toDrawable(resources) ?:resources.getDrawable(arrow.icon)
val arrow = values().firstOrNull { it.symbol == direction } ?: NONE
return arrow.customDrawable?.let { drawableDataMap[arrow.customDrawable.fileName] }?.toDrawable(resources) ?: resources.getDrawable(arrow.icon)
}
}
@ -558,7 +567,7 @@ private enum class FontMap(val key: String, var font: Typeface, @FontRes val fon
resDataMap.filter { (_, resData) ->
resData.format == ResFormat.TTF || resData.format == ResFormat.OTF
}.forEach { (key, resData) ->
customFonts[key.lowercase()] = resData.toTypeface() ?:Typeface.DEFAULT
customFonts[key.lowercase()] = resData.toTypeface() ?: Typeface.DEFAULT
}
}

View file

@ -7,16 +7,13 @@ import android.support.wearable.watchface.WatchFaceStyle
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.viewbinding.ViewBinding
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ActivityDigitalstyleBinding
import info.nightscout.shared.extensions.toVisibility
import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace
import info.nightscout.rx.logging.LTag
class DigitalStyleWatchface : BaseWatchFace() {
private lateinit var binding: ActivityDigitalstyleBinding

View file

@ -4,8 +4,8 @@ import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorManager
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.logging.AAPSLoggerTest
import info.nightscout.rx.weardata.EventData.ActionHeartRate
import info.nightscout.sharedtests.AAPSLoggerTest
import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.disposables.Disposable
import org.junit.jupiter.api.AfterEach
@ -22,8 +22,9 @@ import org.mockito.Mockito.`when`
import java.util.concurrent.TimeUnit
internal class HeartRateListenerTest {
private val aapsLogger = AAPSLoggerTest()
private val aapsSchedulers = object: AapsSchedulers {
private val aapsSchedulers = object : AapsSchedulers {
override val main: Scheduler = mock(Scheduler::class.java)
override val io: Scheduler = mock(Scheduler::class.java)
override val cpu: Scheduler = mock(Scheduler::class.java)
@ -35,11 +36,15 @@ internal class HeartRateListenerTest {
private fun create(timestampMillis: Long): HeartRateListener {
val ctx = mock(Context::class.java)
`when`(aapsSchedulers.io.schedulePeriodicallyDirect(
any(), eq(60_000L), eq(60_000L), eq(TimeUnit.MILLISECONDS))).thenReturn(schedule)
`when`(
aapsSchedulers.io.schedulePeriodicallyDirect(
any(), eq(60_000L), eq(60_000L), eq(TimeUnit.MILLISECONDS)
)
).thenReturn(schedule)
val listener = HeartRateListener(ctx, aapsLogger, aapsSchedulers, timestampMillis)
verify(aapsSchedulers.io).schedulePeriodicallyDirect(
any(), eq(60_000L), eq(60_000L), eq(TimeUnit.MILLISECONDS))
any(), eq(60_000L), eq(60_000L), eq(TimeUnit.MILLISECONDS)
)
listener.sendHeartRate = { hr -> heartRates.add(hr) }
return listener
}
@ -49,10 +54,11 @@ internal class HeartRateListenerTest {
timestamp: Long,
heartRate: Int,
sensorType: Int? = Sensor.TYPE_HEART_RATE,
accuracy: Int = SensorManager.SENSOR_STATUS_ACCURACY_HIGH) {
accuracy: Int = SensorManager.SENSOR_STATUS_ACCURACY_HIGH
) {
listener.onSensorChanged(sensorType, accuracy, timestamp, floatArrayOf(heartRate.toFloat()))
}
@BeforeEach
fun before() {
heartRates.clear()
@ -66,7 +72,7 @@ internal class HeartRateListenerTest {
Mockito.verifyNoInteractions(aapsSchedulers.newThread)
verify(schedule).dispose()
}
@Test
fun onSensorChanged() {
val start = System.currentTimeMillis()
@ -95,7 +101,7 @@ internal class HeartRateListenerTest {
sendSensorEvent(listener, start, 80)
assertEquals(0, heartRates.size)
assertEquals(80, listener.currentHeartRateBpm)
sendSensorEvent(listener, start + d1,100)
sendSensorEvent(listener, start + d1, 100)
assertEquals(0, heartRates.size)
assertEquals(100, listener.currentHeartRateBpm)
@ -117,7 +123,7 @@ internal class HeartRateListenerTest {
listener.send(start + d1)
assertEquals(1, heartRates.size)
sendSensorEvent(listener, start + d1,100)
sendSensorEvent(listener, start + d1, 100)
assertEquals(1, heartRates.size)
listener.send(start + d2)
assertEquals(2, heartRates.size)