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 androidx.preference.PreferenceManager
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.interfaces.L 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.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.impl.sharedPreferences.SPImplementation
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil
import info.nightscout.shared.utils.DateUtilImpl
import javax.inject.Singleton import javax.inject.Singleton
@Module( @Module(
@ -23,4 +31,20 @@ open class SharedImplModule {
@Provides @Provides
@Singleton @Singleton
fun provideL(sp: SP): L = LImpl(sp) 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.interfaces.L
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import org.slf4j.LoggerFactory 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:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M0.103,0h24v24h-24z"/> android:pathData="M0.103,0h24v24h-24z" />
</vector> </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 { android {
namespace 'info.nightscout.sharedtests' namespace 'info.nightscout.sharedtests'
} }
dependencies { dependencies {
implementation project(':database:entities') implementation project(':database:entities')
implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':core:interfaces') implementation project(':core:interfaces')
implementation project(':core:main') implementation project(':core:main')
implementation project(':core:utils') implementation project(':core:utils')
implementation project(':implementation') implementation project(':implementation')
api "org.mockito:mockito-junit-jupiter:$mockito_version" api "org.mockito:mockito-junit-jupiter:$mockito_version"
api "org.mockito.kotlin:mockito-kotlin:5.1.0" api "org.mockito.kotlin:mockito-kotlin:5.1.0"
api "org.junit.jupiter:junit-jupiter-api:$junit_jupiter_version" 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. * Created by adrian on 2019-12-27.

View file

@ -2,8 +2,7 @@ package info.nightscout.sharedtests
import android.annotation.SuppressLint import android.annotation.SuppressLint
import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.TestAapsSchedulers import info.nightscout.sharedtests.rx.TestAapsSchedulers
import info.nightscout.rx.logging.AAPSLoggerTest
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.ArgumentMatcher 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.profile.ProfileStore
import info.nightscout.interfaces.utils.DecimalFormatter import info.nightscout.interfaces.utils.DecimalFormatter
import info.nightscout.interfaces.utils.HardLimits 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.ProfileUtil
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtilImpl
import org.json.JSONObject import org.json.JSONObject
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.mockito.ArgumentMatchers.anyDouble import org.mockito.ArgumentMatchers.anyDouble
@ -45,11 +45,11 @@ open class TestBaseWithProfile : TestBase() {
@Mock lateinit var context: Context @Mock lateinit var context: Context
@Mock lateinit var sp: SP @Mock lateinit var sp: SP
lateinit var dateUtil: DateUtil lateinit var dateUtil: DateUtilImpl
lateinit var profileUtil: ProfileUtil lateinit var profileUtil: ProfileUtil
lateinit var decimalFormatter: DecimalFormatter lateinit var decimalFormatter: DecimalFormatter
lateinit var hardLimits: HardLimits lateinit var hardLimits: HardLimits
val rxBus = RxBus(aapsSchedulers, aapsLogger) val rxBus = RxBusImpl(aapsSchedulers, aapsLogger)
val profileInjector = HasAndroidInjector { val profileInjector = HasAndroidInjector {
AndroidInjector { 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\"}," + 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\"}]," + "{\"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\"}" "\"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) decimalFormatter = DecimalFormatterImpl(rh)
profileUtil = ProfileUtilImpl(sp, decimalFormatter) profileUtil = ProfileUtilImpl(sp, decimalFormatter)
testPumpPlugin = TestPumpPlugin(profileInjector) 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 package info.nightscout.rx
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Scheduler import io.reactivex.rxjava3.core.Scheduler
import io.reactivex.rxjava3.schedulers.Schedulers
/** /**
* Created by adrian on 12.04.20. * Created by adrian on 12.04.20.
*/ */
interface AapsSchedulers { interface AapsSchedulers {
val main: Scheduler val main: Scheduler
val io: Scheduler val io: Scheduler
val cpu: Scheduler val cpu: Scheduler
val newThread: 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 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.events.Event
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag
import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.subjects.PublishSubject
import javax.inject.Inject
import javax.inject.Singleton
@OpenForTesting interface RxBus {
@Singleton
class RxBus @Inject constructor(
val aapsSchedulers: AapsSchedulers,
val aapsLogger: AAPSLogger
) {
private val publisher = PublishSubject.create<Event>() fun send(event: Event)
fun send(event: Event) {
aapsLogger.debug(LTag.EVENTS, "Sending $event")
publisher.onNext(event)
}
// Listen should return an Observable and not the publisher // Listen should return an Observable and not the publisher
// Using ofType we filter only events that match that class type // Using ofType we filter only events that match that class type
fun <T : Any> toObservable(eventType: Class<T>): Observable<T> = fun <T : Any> toObservable(eventType: Class<T>): Observable<T>
publisher
.subscribeOn(aapsSchedulers.io)
.ofType(eventType)
} }

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 package info.nightscout.shared.utils
import android.content.Context
import android.os.Build import android.os.Build
import androidx.annotation.RequiresApi 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 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.TimeZone
import java.util.concurrent.TimeUnit 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 * The Class DateUtil. A simple wrapper around SimpleDateFormat to ease the handling of iso date string &lt;-&gt; date obj
* with TZ * with TZ
*/ */
@OpenForTesting interface DateUtil {
@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'"
/** /**
* Takes in an ISO date string of the following format: * 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 * @param isoDateString the iso date string
* @return the date * @return the date
*/ */
fun fromISODateString(isoDateString: String): Long { fun fromISODateString(isoDateString: String): Long
val parser = ISODateTimeFormat.dateTimeParser()
val dateTime = DateTime.parse(isoDateString, parser)
return dateTime.toDate().time
}
/** /**
* Render date * Render date
@ -67,404 +27,66 @@ class DateUtil @Inject constructor(private val context: Context) {
* @param date the date obj * @param date the date obj
* @return the iso-formatted date string * @return the iso-formatted date string
*/ */
fun toISOString(date: Long): String { fun toISOString(date: Long): String
val f: DateFormat = SimpleDateFormat(FORMAT_DATE_ISO_OUT, Locale.getDefault()) fun toISOAsUTC(timestamp: Long): String
f.timeZone = TimeZone.getTimeZone("UTC") fun toISONoZone(timestamp: Long): String
return f.format(date) fun secondsOfTheDayToMilliseconds(seconds: Int): Long
} fun toSeconds(hhColonMm: String): Int
fun dateString(mills: Long): String
@Suppress("SpellCheckingInspection") fun dateStringRelative(mills: Long, rh: ResourceHelper): String
fun toISOAsUTC(timestamp: Long): String { fun dateStringShort(mills: Long): String
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'0000Z'", Locale.US) fun timeString(): String
format.timeZone = TimeZone.getTimeZone("UTC") fun timeString(mills: Long): String
return format.format(timestamp) fun secondString(): String
} fun secondString(mills: Long): String
fun minuteString(): String
@Suppress("SpellCheckingInspection") fun minuteString(mills: Long): String
fun toISONoZone(timestamp: Long): String { fun hourString(): String
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) fun hourString(mills: Long): String
format.timeZone = TimeZone.getDefault() fun amPm(): String
return format.format(timestamp) fun amPm(mills: Long): String
} fun dayNameString(format: String = "E"): String
fun dayNameString(mills: Long, format: String = "E"): String
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 dayString(): String = dayString(now()) fun dayString(): String = dayString(now())
fun dayString(mills: Long): String = fun dayString(mills: Long): String
DateTime(mills).toString(DateTimeFormat.forPattern("dd")) 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 isSameDayGroup(timestamp1: Long, timestamp2: Long): Boolean
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))
}
//Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0} //Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}
fun computeDiff(date1: Long, date2: Long): Map<TimeUnit, Long> { fun computeDiff(date1: Long, date2: Long): Map<TimeUnit, Long>
val units: MutableList<TimeUnit> = ArrayList(EnumSet.allOf(TimeUnit::class.java)) fun age(milliseconds: Long, useShortText: Boolean, rh: ResourceHelper): String
units.reverse() fun niceTimeScalar(time: Long, rh: ResourceHelper): String
val result: MutableMap<TimeUnit, Long> = LinkedHashMap() fun qs(x: Double, numDigits: Int): String
var millisecondsRest = date2 - date1 fun formatHHMM(timeAsSeconds: Int): String
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())
}
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
fun timeZoneByOffset(offsetInMilliseconds: Long): TimeZone = fun timeZoneByOffset(offsetInMilliseconds: Long): TimeZone
TimeZone.getTimeZone( fun timeStampToUtcDateMillis(timestamp: Long): Long
if (offsetInMilliseconds == 0L) ZoneId.of("UTC") fun mergeUtcDateToTimestamp(timestamp: Long, dateUtcMillis: Long): Long
else ZoneId.getAvailableZoneIds() fun mergeHourMinuteToTimestamp(timestamp: Long, hour: Int, minute: Int, randomSecond: Boolean = false): Long
.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
}
} }

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.danar.di.DanaRModule
import info.nightscout.androidaps.insight.di.InsightDatabaseModule import info.nightscout.androidaps.insight.di.InsightDatabaseModule
import info.nightscout.androidaps.insight.di.InsightModule 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.common.di.RileyLinkModule
import info.nightscout.androidaps.plugins.pump.eopatch.dagger.EopatchModule import info.nightscout.androidaps.plugins.pump.eopatch.dagger.EopatchModule
import info.nightscout.androidaps.plugins.pump.medtronic.di.MedtronicModule 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.aps.di.ApsModule
import info.nightscout.plugins.constraints.di.PluginsConstraintsModule import info.nightscout.plugins.constraints.di.PluginsConstraintsModule
import info.nightscout.plugins.di.PluginsModule import info.nightscout.plugins.di.PluginsModule
import info.nightscout.plugins.sync.di.OpenHumansModule
import info.nightscout.plugins.sync.di.SyncModule import info.nightscout.plugins.sync.di.SyncModule
import info.nightscout.pump.combo.di.ComboModule import info.nightscout.pump.combo.di.ComboModule
import info.nightscout.pump.combov2.di.ComboV2Module 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.DanaHistoryModule
import info.nightscout.pump.dana.di.DanaModule import info.nightscout.pump.dana.di.DanaModule
import info.nightscout.pump.danars.di.DanaRSModule 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.diaconn.di.DiaconnG8Module
import info.nightscout.pump.medtrum.di.MedtrumModule
import info.nightscout.pump.virtual.di.VirtualPumpModule 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.shared.impl.di.SharedImplModule
import info.nightscout.source.di.SourceModule import info.nightscout.source.di.SourceModule
import info.nightscout.ui.di.UiModule import info.nightscout.ui.di.UiModule
@ -62,8 +60,6 @@ import javax.inject.Singleton
InsulinModule::class, InsulinModule::class,
OpenHumansModule::class, OpenHumansModule::class,
PluginsModule::class, PluginsModule::class,
RxModule::class,
SharedModule::class,
SharedImplModule::class, SharedImplModule::class,
UiModule::class, UiModule::class,
ValidatorsModule::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 { dependencies {
implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
api "com.google.android.material:material:$material_version" 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.events.EventPreferenceChange
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.shared.extensions.runOnUiThread
import info.nightscout.shared.extensions.toVisibility import info.nightscout.shared.extensions.toVisibility
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Completable
@ -106,7 +105,7 @@ class MaintenanceFragment : DaggerFragment() {
onError = { aapsLogger.error("Error clearing databases", it) }, onError = { aapsLogger.error("Error clearing databases", it) },
onComplete = { onComplete = {
rxBus.send(EventPreferenceChange(rh.gs(info.nightscout.core.utils.R.string.key_units))) 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) uel.log(Action.RESET_DATABASES, Sources.Maintenance)

View file

@ -16,6 +16,7 @@ android {
dependencies { dependencies {
implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':database:entities') implementation project(':database:entities')
implementation project(':database:impl') implementation project(':database:impl')
implementation project(':core:graphview') 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.EventPumpStatusChanged
import info.nightscout.rx.events.EventUpdateOverviewCalcProgress import info.nightscout.rx.events.EventUpdateOverviewCalcProgress
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.shared.impl.rx.bus.RxBusImpl
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -72,7 +73,7 @@ class OverviewPlugin @Inject constructor(
private var disposable: CompositeDisposable = CompositeDisposable() 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) { override fun addNotificationWithDialogResponse(id: Int, text: String, level: Int, @StringRes actionButtonId: Int, title: String, message: String) {
rxBus.send( rxBus.send(

View file

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

View file

@ -16,6 +16,7 @@ android {
dependencies { dependencies {
implementation project(':app-wear-shared:shared') implementation project(':app-wear-shared:shared')
implementation project(':app-wear-shared:shared-impl')
implementation project(':database:entities') implementation project(':database:entities')
implementation project(':database:impl') implementation project(':database:impl')
implementation project(':core:main') 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.AapsSchedulers
import info.nightscout.rx.bus.RxBus import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventQueueChanged import info.nightscout.rx.events.EventQueueChanged
import info.nightscout.shared.extensions.runOnUiThread
import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.DateUtil
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -62,7 +61,7 @@ class ComboFragment : DaggerFragment() {
binding.comboRefreshButton.isEnabled = false binding.comboRefreshButton.isEnabled = false
commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.user_request), object : Callback() { commandQueue.readStatus(rh.gs(info.nightscout.core.ui.R.string.user_request), object : Callback() {
override fun run() { 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.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
binding.comboInsulinstate.setTypeface(null, Typeface.NORMAL) binding.comboInsulinstate.setTypeface(null, Typeface.NORMAL)
} }
PumpState.LOW -> { PumpState.LOW -> {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.omniYellowColor)) binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.omniYellowColor))
binding.comboInsulinstate.setTypeface(null, Typeface.BOLD) binding.comboInsulinstate.setTypeface(null, Typeface.BOLD)
} }
PumpState.EMPTY -> { PumpState.EMPTY -> {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.warningColor)) binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.warningColor))
binding.comboInsulinstate.setTypeface(null, Typeface.BOLD) binding.comboInsulinstate.setTypeface(null, Typeface.BOLD)
} }
else -> { else -> {
binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor)) binding.comboInsulinstate.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
binding.comboInsulinstate.setTypeface(null, Typeface.NORMAL) binding.comboInsulinstate.setTypeface(null, Typeface.NORMAL)
@ -175,10 +177,12 @@ class ComboFragment : DaggerFragment() {
binding.comboLastconnection.setText(R.string.combo_pump_connected_now) binding.comboLastconnection.setText(R.string.combo_pump_connected_now)
binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor)) binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))
} }
comboPlugin.pump.lastSuccessfulCmdTime + 30 * 60 * 1000 < System.currentTimeMillis() -> { comboPlugin.pump.lastSuccessfulCmdTime + 30 * 60 * 1000 < System.currentTimeMillis() -> {
binding.comboLastconnection.text = rh.gs(R.string.combo_no_pump_connection, min) 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)) binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.warningColor))
} }
else -> { else -> {
binding.comboLastconnection.text = minAgo binding.comboLastconnection.text = minAgo
binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor)) binding.comboLastconnection.setTextColor(rh.gac(context, info.nightscout.core.ui.R.attr.defaultTextColor))

View file

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

View file

@ -27,6 +27,7 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
interface IAlarmRegistry { interface IAlarmRegistry {
fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean = false): Maybe<AlarmCode> fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean = false): Maybe<AlarmCode>
fun add(patchAeCodes: Set<PatchAeCode>) fun add(patchAeCodes: Set<PatchAeCode>)
fun remove(alarmCode: AlarmCode): Maybe<AlarmCode> fun remove(alarmCode: AlarmCode): Maybe<AlarmCode>
@ -34,11 +35,13 @@ interface IAlarmRegistry {
@Singleton @Singleton
class AlarmRegistry @Inject constructor() : IAlarmRegistry { class AlarmRegistry @Inject constructor() : IAlarmRegistry {
@Inject lateinit var mContext: Context @Inject lateinit var mContext: Context
@Inject lateinit var pm: IPreferenceManager @Inject lateinit var pm: IPreferenceManager
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var dateUtil: DateUtil
private lateinit var mOsAlarmManager: AlarmManager private lateinit var mOsAlarmManager: AlarmManager
private var mDisposable: Disposable? = null private var mDisposable: Disposable? = null
@ -49,19 +52,21 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
mDisposable = pm.observePatchLifeCycle() mDisposable = pm.observePatchLifeCycle()
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe { .subscribe {
when(it){ when (it) {
PatchLifecycle.REMOVE_NEEDLE_CAP -> { PatchLifecycle.REMOVE_NEEDLE_CAP -> {
val triggerAfter = pm.getPatchConfig().patchWakeupTimestamp + TimeUnit.HOURS.toMillis(1) - System.currentTimeMillis() val triggerAfter = pm.getPatchConfig().patchWakeupTimestamp + TimeUnit.HOURS.toMillis(1) - System.currentTimeMillis()
compositeDisposable.add(add(AlarmCode.A020, triggerAfter).subscribe()) compositeDisposable.add(add(AlarmCode.A020, triggerAfter).subscribe())
} }
PatchLifecycle.ACTIVATED -> {
PatchLifecycle.ACTIVATED -> {
} }
PatchLifecycle.SHUTDOWN -> {
PatchLifecycle.SHUTDOWN -> {
val sources = ArrayList<Maybe<*>>() val sources = ArrayList<Maybe<*>>()
sources.add(Maybe.just(true)) sources.add(Maybe.just(true))
pm.getAlarms().occurred.let{ occurredAlarms -> pm.getAlarms().occurred.let { occurredAlarms ->
if(occurredAlarms.isNotEmpty()){ if (occurredAlarms.isNotEmpty()) {
occurredAlarms.keys.forEach { alarmCode -> occurredAlarms.keys.forEach { alarmCode ->
sources.add( sources.add(
Maybe.just(alarmCode) Maybe.just(alarmCode)
@ -71,30 +76,30 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
} }
} }
} }
pm.getAlarms().registered.let{ registeredAlarms -> pm.getAlarms().registered.let { registeredAlarms ->
if(registeredAlarms.isNotEmpty()){ if (registeredAlarms.isNotEmpty()) {
registeredAlarms.keys.forEach { alarmCode -> registeredAlarms.keys.forEach { alarmCode ->
sources.add(remove(alarmCode)) sources.add(remove(alarmCode))
} }
} }
} }
compositeDisposable.add(Maybe.concat(sources) compositeDisposable.add(Maybe.concat(sources)
.subscribe { .subscribe {
pm.getAlarms().clear() pm.getAlarms().clear()
pm.flushAlarms() pm.flushAlarms()
} }
) )
} }
else -> Unit else -> Unit
} }
} }
} }
override fun add(alarmCode: AlarmCode, triggerAfter: Long, isFirst: Boolean): Maybe<AlarmCode> { 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) return Maybe.just(alarmCode)
}else { } else {
val triggerTimeMilli = System.currentTimeMillis() + triggerAfter val triggerTimeMilli = System.currentTimeMillis() + triggerAfter
pm.getAlarms().register(alarmCode, triggerAfter) pm.getAlarms().register(alarmCode, triggerAfter)
pm.flushAlarms() pm.flushAlarms()
@ -109,11 +114,11 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
override fun add(patchAeCodes: Set<PatchAeCode>) { override fun add(patchAeCodes: Set<PatchAeCode>) {
compositeDisposable.add( compositeDisposable.add(
Observable.fromIterable(patchAeCodes) Observable.fromIterable(patchAeCodes)
.filter{patchAeCodeItem -> AlarmCode.findByPatchAeCode(patchAeCodeItem.aeValue) != null} .filter { patchAeCodeItem -> AlarmCode.findByPatchAeCode(patchAeCodeItem.aeValue) != null }
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.filter { aeCodes -> AlarmCode.findByPatchAeCode(aeCodes.aeValue) != null } .filter { aeCodes -> AlarmCode.findByPatchAeCode(aeCodes.aeValue) != null }
.flatMapMaybe{aeCodeResponse -> add(AlarmCode.findByPatchAeCode(aeCodeResponse.aeValue)!!, 0L, true)} .flatMapMaybe { aeCodeResponse -> add(AlarmCode.findByPatchAeCode(aeCodeResponse.aeValue)!!, 0L, true) }
.subscribe() .subscribe()
) )
} }
@ -121,21 +126,21 @@ class AlarmRegistry @Inject constructor() : IAlarmRegistry {
return Maybe.fromCallable { return Maybe.fromCallable {
cancelOsAlarmInternal(alarmCode) cancelOsAlarmInternal(alarmCode)
val pendingIntent = createPendingIntent(alarmCode, 0) 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) mOsAlarmManager.setAlarmClock(AlarmClockInfo(triggerTime, pendingIntent), pendingIntent)
alarmCode alarmCode
} }
} }
override fun remove(alarmCode: AlarmCode): Maybe<AlarmCode> { override fun remove(alarmCode: AlarmCode): Maybe<AlarmCode> {
return if(pm.getAlarms().registered.containsKey(alarmCode)) { return if (pm.getAlarms().registered.containsKey(alarmCode)) {
cancelOsAlarms(alarmCode) cancelOsAlarms(alarmCode)
.doOnSuccess { .doOnSuccess {
pm.getAlarms().unregister(alarmCode) pm.getAlarms().unregister(alarmCode)
pm.flushAlarms() pm.flushAlarms()
} }
.map { alarmCode } .map { alarmCode }
}else{ } else {
Maybe.just(alarmCode) 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.DashHistoryDatabase
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.database.HistoryRecordDao 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.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.Before
import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test 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.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessagePacket
import info.nightscout.core.utils.toHex 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.Assertions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.spongycastle.util.encoders.Hex 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.androidaps.plugins.pump.omnipod.dash.driver.pod.util.X25519KeyGenerator
import info.nightscout.core.utils.toHex import info.nightscout.core.utils.toHex
import info.nightscout.interfaces.Config import info.nightscout.interfaces.Config
import info.nightscout.rx.logging.AAPSLoggerTest import info.nightscout.sharedtests.AAPSLoggerTest
import info.nightscout.sharedtests.TestBase import info.nightscout.sharedtests.TestBase
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test

View file

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

View file

@ -37,6 +37,8 @@ dependencies {
implementation project(':pump:rileylink') implementation project(':pump:rileylink')
implementation project(':pump:omnipod-common') implementation project(':pump:omnipod-common')
testImplementation project(':app-wear-shared:shared-impl')
api "androidx.room:room-ktx:$room_version" api "androidx.room:room-ktx:$room_version"
api "androidx.room:room-runtime:$room_version" api "androidx.room:room-runtime:$room_version"
api "androidx.room:room-rxjava3:$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.FirmwareVersion
import info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.PodProgressStatus import info.nightscout.androidaps.plugins.pump.omnipod.eros.driver.definition.PodProgressStatus
import info.nightscout.rx.TestAapsSchedulers import info.nightscout.shared.impl.rx.bus.RxBusImpl
import info.nightscout.rx.bus.RxBus
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.sharedtests.TestBase import info.nightscout.sharedtests.TestBase
import info.nightscout.sharedtests.rx.TestAapsSchedulers
import org.joda.time.DateTime import org.joda.time.DateTime
import org.joda.time.DateTimeUtils import org.joda.time.DateTimeUtils
import org.joda.time.DateTimeZone import org.joda.time.DateTimeZone
@ -19,7 +19,7 @@ class AapsErosPodStateManagerTest : TestBase() {
@Mock lateinit var sp: SP @Mock lateinit var sp: SP
private val rxBus = RxBus(TestAapsSchedulers(), aapsLogger) private val rxBus = RxBusImpl(TestAapsSchedulers(), aapsLogger)
@Test fun times() { @Test fun times() {
val timeZone = DateTimeZone.UTC val timeZone = DateTimeZone.UTC

View file

@ -6,8 +6,6 @@ import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.WearApp 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 info.nightscout.shared.impl.di.SharedImplModule
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
@ -16,8 +14,6 @@ import kotlinx.datetime.Clock
includes = [ includes = [
WearModule.AppBindings::class, WearModule.AppBindings::class,
WearActivitiesModule::class, WearActivitiesModule::class,
RxModule::class,
SharedModule::class,
SharedImplModule::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.logging.LTag
import info.nightscout.rx.weardata.CUSTOM_VERSION import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfData 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.CwfMetadataKey
import info.nightscout.rx.weardata.CwfMetadataMap import info.nightscout.rx.weardata.CwfMetadataMap
import info.nightscout.rx.weardata.ResData import info.nightscout.rx.weardata.CwfResDataMap
import info.nightscout.rx.weardata.ResFormat
import info.nightscout.rx.weardata.EventData import info.nightscout.rx.weardata.EventData
import info.nightscout.rx.weardata.JsonKeyValues import info.nightscout.rx.weardata.JsonKeyValues
import info.nightscout.rx.weardata.JsonKeys.* 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.ViewKeys
import info.nightscout.rx.weardata.ZipWatchfaceFormat import info.nightscout.rx.weardata.ZipWatchfaceFormat
import info.nightscout.shared.extensions.toVisibility import info.nightscout.shared.extensions.toVisibility
@ -208,7 +208,7 @@ class CustomWatchface : BaseWatchFace() {
it.clearColorFilter() it.clearColorFilter()
view.setImageDrawable(it) view.setImageDrawable(it)
} ?: apply { } ?: apply {
view.setImageDrawable(id.defaultDrawable?.let {resources.getDrawable(it)}) view.setImageDrawable(id.defaultDrawable?.let { resources.getDrawable(it) })
if (viewJson.has(COLOR.key)) if (viewJson.has(COLOR.key))
view.setColorFilter(getColor(viewJson.optString(COLOR.key))) view.setColorFilter(getColor(viewJson.optString(COLOR.key)))
else else
@ -217,7 +217,7 @@ class CustomWatchface : BaseWatchFace() {
} }
} }
} ?:apply { } ?: apply {
view.visibility = View.GONE view.visibility = View.GONE
if (view is TextView) { if (view is TextView) {
view.text = "" view.text = ""
@ -301,7 +301,7 @@ class CustomWatchface : BaseWatchFace() {
} }
val metadataMap = ZipWatchfaceFormat.loadMetadata(json) val metadataMap = ZipWatchfaceFormat.loadMetadata(json)
val drawableDataMap: CwfResDataMap = mutableMapOf() 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) drawableDataMap[ResFileMap.CUSTOM_WATCHFACE.fileName] = ResData(it, ResFormat.PNG)
} }
return EventData.ActionSetCustomWatchface(CwfData(json.toString(4), metadataMap, drawableDataMap)) return EventData.ActionSetCustomWatchface(CwfData(json.toString(4), metadataMap, drawableDataMap))
@ -394,7 +394,7 @@ class CustomWatchface : BaseWatchFace() {
@StringRes val pref: Int?, @StringRes val pref: Int?,
@IdRes val defaultDrawable: Int?, @IdRes val defaultDrawable: Int?,
val customDrawable: ResFileMap?, val customDrawable: ResFileMap?,
val customHigh:ResFileMap?, val customHigh: ResFileMap?,
val customLow: ResFileMap? val customLow: ResFileMap?
) { ) {
@ -402,7 +402,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.BACKGROUND.key, ViewKeys.BACKGROUND.key,
R.id.background, R.id.background,
null, null,
info.nightscout.shared.R.drawable.background, info.nightscout.shared.impl.R.drawable.background,
ResFileMap.BACKGROUND, ResFileMap.BACKGROUND,
ResFileMap.BACKGROUND_HIGH, ResFileMap.BACKGROUND_HIGH,
ResFileMap.BACKGROUND_LOW ResFileMap.BACKGROUND_LOW
@ -448,7 +448,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.COVER_PLATE.key, ViewKeys.COVER_PLATE.key,
R.id.cover_plate, R.id.cover_plate,
null, null,
info.nightscout.shared.R.drawable.simplified_dial, info.nightscout.shared.impl.R.drawable.simplified_dial,
ResFileMap.COVER_PLATE, ResFileMap.COVER_PLATE,
ResFileMap.COVER_PLATE_HIGH, ResFileMap.COVER_PLATE_HIGH,
ResFileMap.COVER_PLATE_LOW ResFileMap.COVER_PLATE_LOW
@ -457,7 +457,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.HOUR_HAND.key, ViewKeys.HOUR_HAND.key,
R.id.hour_hand, R.id.hour_hand,
null, null,
info.nightscout.shared.R.drawable.hour_hand, info.nightscout.shared.impl.R.drawable.hour_hand,
ResFileMap.HOUR_HAND, ResFileMap.HOUR_HAND,
ResFileMap.HOUR_HAND_HIGH, ResFileMap.HOUR_HAND_HIGH,
ResFileMap.HOUR_HAND_LOW ResFileMap.HOUR_HAND_LOW
@ -466,7 +466,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.MINUTE_HAND.key, ViewKeys.MINUTE_HAND.key,
R.id.minute_hand, R.id.minute_hand,
null, null,
info.nightscout.shared.R.drawable.minute_hand, info.nightscout.shared.impl.R.drawable.minute_hand,
ResFileMap.MINUTE_HAND, ResFileMap.MINUTE_HAND,
ResFileMap.MINUTE_HAND_HIGH, ResFileMap.MINUTE_HAND_HIGH,
ResFileMap.MINUTE_HAND_LOW ResFileMap.MINUTE_HAND_LOW
@ -475,7 +475,7 @@ class CustomWatchface : BaseWatchFace() {
ViewKeys.SECOND_HAND.key, ViewKeys.SECOND_HAND.key,
R.id.second_hand, R.id.second_hand,
R.string.key_show_seconds, 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,
ResFileMap.SECOND_HAND_HIGH, ResFileMap.SECOND_HAND_HIGH,
ResFileMap.SECOND_HAND_LOW ResFileMap.SECOND_HAND_LOW
@ -491,16 +491,25 @@ class CustomWatchface : BaseWatchFace() {
fun drawable(resources: Resources, drawableDataMap: CwfResDataMap, sgvLevel: Long): Drawable? = customDrawable?.let { cd -> fun drawable(resources: Resources, drawableDataMap: CwfResDataMap, sgvLevel: Long): Drawable? = customDrawable?.let { cd ->
when (sgvLevel) { when (sgvLevel) {
1L -> { customHigh?.let {drawableDataMap[customHigh.fileName]}?.toDrawable(resources) ?: drawableDataMap[cd.fileName]?.toDrawable(resources) } 1L -> {
0L -> { drawableDataMap[cd.fileName]?.toDrawable(resources) } customHigh?.let { drawableDataMap[customHigh.fileName] }?.toDrawable(resources) ?: drawableDataMap[cd.fileName]?.toDrawable(resources)
-1L -> { customLow?.let {drawableDataMap[customLow.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) 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), NONE("??", R.drawable.ic_invalid, ResFileMap.ARROW_NONE),
TRIPLE_UP("X", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP), TRIPLE_UP("X", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
DOUBLE_UP("\u21c8", 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 { companion object {
fun drawable(direction: String?, resources: Resources, drawableDataMap: CwfResDataMap): Drawable { fun drawable(direction: String?, resources: Resources, drawableDataMap: CwfResDataMap): Drawable {
val arrow = values().firstOrNull { it.symbol == direction } ?:NONE val arrow = values().firstOrNull { it.symbol == direction } ?: NONE
return arrow.customDrawable?. let {drawableDataMap[arrow.customDrawable.fileName]}?.toDrawable(resources) ?:resources.getDrawable(arrow.icon) 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) -> resDataMap.filter { (_, resData) ->
resData.format == ResFormat.TTF || resData.format == ResFormat.OTF resData.format == ResFormat.TTF || resData.format == ResFormat.OTF
}.forEach { (key, resData) -> }.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.LayoutInflater
import android.view.View import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ActivityDigitalstyleBinding import info.nightscout.androidaps.databinding.ActivityDigitalstyleBinding
import info.nightscout.shared.extensions.toVisibility
import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
class DigitalStyleWatchface : BaseWatchFace() { class DigitalStyleWatchface : BaseWatchFace() {
private lateinit var binding: ActivityDigitalstyleBinding private lateinit var binding: ActivityDigitalstyleBinding

View file

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