\ No newline at end of file
diff --git a/auto/build.gradle b/auto/build.gradle
index c811b7d6..884fcb75 100644
--- a/auto/build.gradle
+++ b/auto/build.gradle
@@ -25,6 +25,8 @@ android {
resValue "string", "app_name", "GlucoDataAuto"
}
dev_release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-dev-rules.pro'
versionNameSuffix '-dev'
resValue "string", "app_name", "GlucoDataAuto"
}
diff --git a/auto/src/main/java/de/michelinside/glucodataauto/GlucoDataServiceAuto.kt b/auto/src/main/java/de/michelinside/glucodataauto/GlucoDataServiceAuto.kt
index bd45ffd1..7dcf8aa6 100644
--- a/auto/src/main/java/de/michelinside/glucodataauto/GlucoDataServiceAuto.kt
+++ b/auto/src/main/java/de/michelinside/glucodataauto/GlucoDataServiceAuto.kt
@@ -163,7 +163,7 @@ class GlucoDataServiceAuto: Service() {
Log.d(LOG_ID, "onStartCommand called")
super.onStartCommand(intent, flags, startId)
val isForeground = intent.getBooleanExtra(Constants.SHARED_PREF_FOREGROUND_SERVICE, false)
- if (isForeground && !isForegroundService && Utils.checkPermission(this, android.Manifest.permission.POST_NOTIFICATIONS, Build.VERSION_CODES.TIRAMISU)) {
+ if (isForeground && !isForegroundService) {
Log.i(LOG_ID, "Starting service in foreground!")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
diff --git a/auto/src/main/java/de/michelinside/glucodataauto/MainActivity.kt b/auto/src/main/java/de/michelinside/glucodataauto/MainActivity.kt
index f7cfb66f..3a41f489 100644
--- a/auto/src/main/java/de/michelinside/glucodataauto/MainActivity.kt
+++ b/auto/src/main/java/de/michelinside/glucodataauto/MainActivity.kt
@@ -110,6 +110,7 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
InternalNotifier.addNotifier( this, this, mutableSetOf(
NotifySource.BROADCAST,
NotifySource.IOB_COB_CHANGE,
+ NotifySource.IOB_COB_TIME,
NotifySource.MESSAGECLIENT,
NotifySource.CAPILITY_INFO,
NotifySource.NODE_BATTERY_LEVEL,
diff --git a/build.gradle b/build.gradle
index 237f9244..785748c8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,12 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '8.1.2' apply false
- id 'com.android.library' version '8.1.2' apply false
- id 'org.jetbrains.kotlin.android' version '1.8.20' apply false
+ id 'com.android.application' version '8.3.1' apply false
+ id 'com.android.library' version '8.3.1' apply false
+ id 'org.jetbrains.kotlin.android' version '1.9.23' apply false
}
-project.ext.set("versionCode", 26)
-project.ext.set("versionName", "0.9.11")
+project.ext.set("versionCode", 30)
+project.ext.set("versionName", "0.9.12")
project.ext.set("compileSdk", 34)
project.ext.set("targetSdk", 33)
project.ext.set("minSdk", 26)
diff --git a/common/build.gradle b/common/build.gradle
index e606c365..a665af1c 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -25,6 +25,8 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
dev_release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-dev-rules.pro'
}
}
compileOptions {
diff --git a/common/proguard-dev-rules.pro b/common/proguard-dev-rules.pro
new file mode 100644
index 00000000..9f50960e
--- /dev/null
+++ b/common/proguard-dev-rules.pro
@@ -0,0 +1,32 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+-dontwarn **
+-keep class **
+-keepclassmembers class *{*;}
+-keepattributes *
+
+# --------------------------------------------------------------------
+# REMOVE all debug log messages
+# --------------------------------------------------------------------
+-assumenosideeffects class android.util.Log {
+ public static *** v(...);
+}
\ No newline at end of file
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/Constants.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/Constants.kt
index 2fb9a266..c66b123e 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/Constants.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/Constants.kt
@@ -63,6 +63,7 @@ object Constants {
const val SHARED_PREF_COLOR_OK = "color_ok"
//const val SHARED_PREF_PERMANENT_NOTIFICATION = "permanent_notification"
const val SHARED_PREF_PERMANENT_NOTIFICATION_ICON = "status_bar_notification_icon"
+ const val SHARED_PREF_PERMANENT_NOTIFICATION_COLORED_ICON = "status_bar_notification_colored_icon"
const val SHARED_PREF_PERMANENT_NOTIFICATION_EMPTY = "permanent_notification_empty"
const val SHARED_PREF_SECOND_PERMANENT_NOTIFICATION = "second_permanent_notification"
const val SHARED_PREF_SECOND_PERMANENT_NOTIFICATION_ICON = "second_status_bar_notification_icon"
@@ -105,6 +106,7 @@ object Constants {
const val SHARED_PREF_LIBRE_TOKEN="source_libre_token"
const val SHARED_PREF_LIBRE_TOKEN_EXPIRE="source_libre_token_expire"
const val SHARED_PREF_LIBRE_REGION="source_libre_region"
+ const val SHARED_PREF_LIBRE_PATIENT_ID="source_libre_patient_id"
const val SHARED_PREF_NIGHTSCOUT_ENABLED="src_ns_enabled"
const val SHARED_PREF_NIGHTSCOUT_URL="src_ns_url"
@@ -114,6 +116,9 @@ object Constants {
const val SHARED_PREF_DUMMY_VALUES = "dummy_values"
+ const val SHARED_PREF_LOCKSCREEN_WP_ENABLED = "lockscreen_enabled"
+ const val SHARED_PREF_LOCKSCREEN_WP_Y_POS = "lockscreen_y_pos"
+
// Android Auto
const val AA_MEDIA_ICON_STYLE_TREND = "trend"
const val AA_MEDIA_ICON_STYLE_GLUCOSE_TREND = "glucose_trend"
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/GlucoDataService.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/GlucoDataService.kt
index 523b1faf..bb96f061 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/GlucoDataService.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/GlucoDataService.kt
@@ -18,7 +18,6 @@ import de.michelinside.glucodatahandler.common.tasks.TimeTaskService
import de.michelinside.glucodatahandler.common.utils.GlucoDataUtils
import de.michelinside.glucodatahandler.common.notification.ChannelType
import de.michelinside.glucodatahandler.common.notification.Channels
-import de.michelinside.glucodatahandler.common.utils.Utils
enum class AppSource {
NOT_SET,
@@ -63,8 +62,10 @@ abstract class GlucoDataService(source: AppSource) : WearableListenerService() {
}
fun start(source: AppSource, context: Context, cls: Class<*>, force: Boolean = false) {
- if (!running || force) {
+ if (!running || !foreground) {
+ Log.v(LOG_ID, "start called (running: $running - foreground: $foreground)")
try {
+ isRunning = true
appSource = source
val serviceIntent = Intent(
context,
@@ -80,20 +81,30 @@ abstract class GlucoDataService(source: AppSource) : WearableListenerService() {
// default on wear and phone
true//sharedPref.getBoolean(Constants.SHARED_PREF_FOREGROUND_SERVICE, true)
)
- context.startService(serviceIntent)
+ if (foreground) {
+ context.startService(serviceIntent)
+ } else {
+ Log.v(LOG_ID, "start foreground service")
+ context.startForegroundService(serviceIntent)
+ }
} catch (exc: Exception) {
Log.e(
LOG_ID,
"start exception: " + exc.message.toString()
)
+ isRunning = false
}
}
}
- fun checkForConnectedNodes() {
+ fun checkForConnectedNodes(refreshDataOnly: Boolean = false) {
try {
- if (connection!=null)
- connection!!.checkForConnectedNodes()
+ Log.d(LOG_ID, "checkForConnectedNodes called for dataOnly=$refreshDataOnly - connection: ${connection!=null}")
+ if (connection!=null) {
+ if(!refreshDataOnly)
+ connection!!.checkForConnectedNodes()
+ connection!!.checkForNodesWithoutData()
+ }
} catch (exc: Exception) {
Log.e(
LOG_ID,
@@ -112,10 +123,10 @@ abstract class GlucoDataService(source: AppSource) : WearableListenerService() {
@RequiresApi(Build.VERSION_CODES.Q)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
try {
- Log.d(LOG_ID, "onStartCommand called")
+ Log.v(LOG_ID, "onStartCommand called")
super.onStartCommand(intent, flags, startId)
val isForeground = true // intent?.getBooleanExtra(Constants.SHARED_PREF_FOREGROUND_SERVICE, true) --> always use foreground!!!
- if (isForeground && !isForegroundService && Utils.checkPermission(this, android.Manifest.permission.POST_NOTIFICATIONS, Build.VERSION_CODES.TIRAMISU)) {
+ if (isForeground && !isForegroundService) {
Log.i(LOG_ID, "Starting service in foreground!")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)
@@ -199,6 +210,7 @@ abstract class GlucoDataService(source: AppSource) : WearableListenerService() {
super.onDestroy()
service = null
isRunning = false
+ isForegroundService = false
} catch (exc: Exception) {
Log.e(LOG_ID, "onDestroy exception: " + exc.toString())
}
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/ReceiveData.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/ReceiveData.kt
index 322a0a95..9a0d21a3 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/ReceiveData.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/ReceiveData.kt
@@ -475,14 +475,23 @@ object ReceiveData: SharedPreferences.OnSharedPreferenceChangeListener {
System.currentTimeMillis()
if(!isIobCob() || (newTime > iobCobTime && (newTime-iobCobTime) > 30000)) {
- Log.i(LOG_ID, "Only IOB/COB changed: " + extras.getFloat(IOB, Float.NaN) + "/" + extras.getFloat(COB, Float.NaN))
- iob = extras.getFloat(IOB, Float.NaN)
- cob = extras.getFloat(COB, Float.NaN)
+ var iobCobChange = false
+ if(iob != extras.getFloat(IOB, Float.NaN) || cob != extras.getFloat(COB, Float.NaN)) {
+ Log.i(LOG_ID, "Only IOB/COB changed: " + extras.getFloat(IOB, Float.NaN) + "/" + extras.getFloat(COB, Float.NaN))
+ iob = extras.getFloat(IOB, Float.NaN)
+ cob = extras.getFloat(COB, Float.NaN)
+ iobCobChange = true
+ } else {
+ Log.d(LOG_ID, "Only IOB/COB time changed")
+ }
iobCobTime = newTime
// do not forward extras as interApp to prevent sending back to source...
val bundle: Bundle? = if(interApp) null else createExtras() // re-create extras to have all changed value inside for sending to receiver
- InternalNotifier.notify(context, NotifySource.IOB_COB_CHANGE, bundle)
+ if(iobCobChange)
+ InternalNotifier.notify(context, NotifySource.IOB_COB_CHANGE, bundle)
+ else
+ InternalNotifier.notify(context, NotifySource.IOB_COB_TIME, bundle)
saveExtras(context)
}
}
@@ -622,7 +631,7 @@ object ReceiveData: SharedPreferences.OnSharedPreferenceChangeListener {
private fun saveExtras(context: Context) {
try {
- Log.d(LOG_ID, "Saving extras")
+ Log.v(LOG_ID, "Saving extras")
// use own tag to prevent trigger onChange event at every time!
val sharedGlucosePref = context.getSharedPreferences(Constants.GLUCODATA_BROADCAST_ACTION, Context.MODE_PRIVATE)
with(sharedGlucosePref.edit()) {
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/SourceState.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/SourceState.kt
index 9ac9f19f..112bfbb9 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/SourceState.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/SourceState.kt
@@ -2,6 +2,7 @@ package de.michelinside.glucodatahandler.common
import android.content.Context
import android.os.Handler
+import android.util.Log
import de.michelinside.glucodatahandler.common.notifier.DataSource
import de.michelinside.glucodatahandler.common.notifier.InternalNotifier
import de.michelinside.glucodatahandler.common.notifier.NotifySource
@@ -13,6 +14,7 @@ enum class SourceState(val resId: Int) {
ERROR(R.string.source_state_error);
}
object SourceStateData {
+ private val LOG_ID = "GDH.SourceStateData"
var lastSource: DataSource = DataSource.NONE
var lastState: SourceState = SourceState.NONE
@@ -27,6 +29,10 @@ object SourceStateData {
lastSource = source
lastState = state
+ if(state == SourceState.ERROR) {
+ Log.e(LOG_ID, "Error state for source $source: $error" )
+ }
+
if (GlucoDataService.context != null) {
Handler(GlucoDataService.context!!.mainLooper).post {
InternalNotifier.notify(GlucoDataService.context!!, NotifySource.SOURCE_STATE_CHANGE, null)
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/WearPhoneConnection.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/WearPhoneConnection.kt
index 45b4a7ce..fcc66ce5 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/WearPhoneConnection.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/WearPhoneConnection.kt
@@ -73,6 +73,7 @@ class WearPhoneConnection : MessageClient.OnMessageReceivedListener, CapabilityC
val filter = mutableSetOf(
NotifySource.BROADCAST,
NotifySource.IOB_COB_CHANGE,
+ NotifySource.IOB_COB_TIME,
NotifySource.BATTERY_LEVEL) // to trigger re-start for the case of stopped by the system
if (sendSettings) {
filter.add(NotifySource.SETTINGS) // only send setting changes from phone to wear!
@@ -122,6 +123,19 @@ class WearPhoneConnection : MessageClient.OnMessageReceivedListener, CapabilityC
}
}
+ fun checkForNodesWithoutData() {
+ try {
+ Log.d(LOG_ID, "check for connected nodes without data")
+ connectedNodes.forEach { node ->
+ if (!nodeBatteryLevel.containsKey(node.key)) {
+ sendDataRequest(node.value.id)
+ }
+ }
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "checkForNodesWithoutData exception: " + exc.toString())
+ }
+ }
+
private fun setConnectedNodes(nodes: MutableSet) {
val curNodes = connectedNodes.keys.toSortedSet()
val newNodes = nodes.map { it.id }.toSortedSet()
@@ -141,13 +155,17 @@ class WearPhoneConnection : MessageClient.OnMessageReceivedListener, CapabilityC
}
}
}
- val extras = ReceiveData.createExtras()
- InternalNotifier.notify(context, NotifySource.CAPILITY_INFO, ReceiveData.createExtras())
- sendMessage(NotifySource.CAPILITY_INFO, extras) // send data request for new node
+ sendDataRequest()
}
}
- fun checkConnectedNode(nodeId: String) {
+ private fun sendDataRequest(filterReceiverId: String? = null) {
+ val extras = ReceiveData.createExtras()
+ InternalNotifier.notify(context, NotifySource.CAPILITY_INFO, ReceiveData.createExtras())
+ sendMessage(NotifySource.CAPILITY_INFO, extras, filterReceiverId = filterReceiverId) // send data request for new node
+ }
+
+ private fun checkConnectedNode(nodeId: String) {
if (!connectedNodes.containsKey(nodeId)) {
Log.i(LOG_ID, "Node with id " + nodeId + " not yet connected, check connection!")
checkForConnectedNodes()
@@ -180,10 +198,10 @@ class WearPhoneConnection : MessageClient.OnMessageReceivedListener, CapabilityC
else -> Constants.GLUCODATA_INTENT_MESSAGE_PATH
}
- fun sendMessage(dataSource: NotifySource, extras: Bundle?, ignoreReceiverId: String? = null, filterReiverId: String? = null)
+ fun sendMessage(dataSource: NotifySource, extras: Bundle?, ignoreReceiverId: String? = null, filterReceiverId: String? = null)
{
try {
- Log.v(LOG_ID, "sendMessage called for $dataSource filter receiver $filterReiverId ignoring receiver $ignoreReceiverId with extras $extras")
+ Log.v(LOG_ID, "sendMessage called for $dataSource filter receiver $filterReceiverId ignoring receiver $ignoreReceiverId with extras $extras")
if( nodesConnected && dataSource != NotifySource.NODE_BATTERY_LEVEL ) {
Log.d(LOG_ID, connectedNodes.size.toString() + " nodes found for sending message for " + dataSource.toString())
if (extras != null && dataSource != NotifySource.BATTERY_LEVEL && BatteryReceiver.batteryPercentage > 0) {
@@ -198,7 +216,7 @@ class WearPhoneConnection : MessageClient.OnMessageReceivedListener, CapabilityC
connectedNodes.forEach { node ->
Thread {
try {
- if ((ignoreReceiverId == null && filterReiverId == null) || ignoreReceiverId != node.value.id || filterReiverId == node.value.id) {
+ if ((ignoreReceiverId == null && filterReceiverId == null) || ignoreReceiverId != node.value.id || filterReceiverId == node.value.id) {
if (dataSource == NotifySource.CAPILITY_INFO)
Thread.sleep(1000) // wait a bit after the connection has changed
sendMessage(node.value, getPath(dataSource), Utils.bundleToBytes(extras), dataSource)
@@ -391,7 +409,7 @@ class WearPhoneConnection : MessageClient.OnMessageReceivedListener, CapabilityC
override fun OnNotifyData(context: Context, dataSource: NotifySource, extras: Bundle?) {
try {
- if (dataSource != NotifySource.IOB_COB_CHANGE || extras != null) { // do not send IOB change without extras
+ if ((dataSource != NotifySource.IOB_COB_CHANGE && dataSource != NotifySource.IOB_COB_TIME) || extras != null) { // do not send IOB change without extras
Log.d(LOG_ID, "OnNotifyData for source " + dataSource.toString() + " and extras " + extras.toString())
sendMessage(dataSource, extras)
}
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/InternalNotifier.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/InternalNotifier.kt
index 7fec0c65..616ae87e 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/InternalNotifier.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/InternalNotifier.kt
@@ -7,19 +7,31 @@ import android.util.Log
object InternalNotifier {
private const val LOG_ID = "GDH.InternalNotifier"
private var notifiers = mutableMapOf?>()
+ private var timeNotifierCount = 0
+ private var obsoleteNotifierCount = 0
+ val hasTimeNotifier: Boolean get() = timeNotifierCount>0
+ val hasObsoleteNotifier: Boolean get() = obsoleteNotifierCount>0
+
fun addNotifier(context: Context, notifier: NotifierInterface, sourceFilter: MutableSet)
{
- Log.i(LOG_ID, "add notifier " + notifier.toString() + " - filter: " + sourceFilter.toString() )
+ val timeChanged = hasSource(notifier, NotifySource.TIME_VALUE) != sourceFilter.contains(NotifySource.TIME_VALUE) ||
+ hasSource(notifier, NotifySource.OBSOLETE_VALUE) != sourceFilter.contains(NotifySource.OBSOLETE_VALUE)
+ Log.i(LOG_ID, "add notifier $notifier - filter: $sourceFilter - timechanged: $timeChanged")
notifiers[notifier] = sourceFilter
Log.d(LOG_ID, "notifier size: " + notifiers.size.toString() )
- notify(context, NotifySource.NOTIFIER_CHANGE, null)
+ if(timeChanged)
+ checkTimeNotifierChanged(context)
+
}
fun remNotifier(context: Context, notifier: NotifierInterface)
{
Log.i(LOG_ID, "rem notifier " + notifier.toString() )
+ val timeChanged = hasSource(notifier, NotifySource.TIME_VALUE) ||
+ hasSource(notifier, NotifySource.OBSOLETE_VALUE)
notifiers.remove(notifier)
Log.d(LOG_ID, "notifier size: " + notifiers.size.toString() )
- notify(context, NotifySource.NOTIFIER_CHANGE, null)
+ if(timeChanged)
+ checkTimeNotifierChanged(context)
}
fun hasNotifier(notifier: NotifierInterface): Boolean {
@@ -28,11 +40,11 @@ object InternalNotifier {
fun notify(context: Context, notifySource: NotifySource, extras: Bundle?)
{
- Log.d(LOG_ID, "Sending new data to " + notifiers.size.toString() + " notifier(s).")
+ Log.d(LOG_ID, "Sending new data from " + notifySource.toString() + " to " + getNotifierCount(notifySource) + " notifier(s).")
notifiers.forEach{
try {
if (it.value == null || it.value!!.contains(notifySource)) {
- Log.d(LOG_ID, "Sending new data from " + notifySource.toString() + " to " + it.toString())
+ Log.v(LOG_ID, "Sending new data from " + notifySource.toString() + " to " + it.toString())
it.key.OnNotifyData(context, notifySource, extras)
}
} catch (exc: Exception) {
@@ -50,4 +62,34 @@ object InternalNotifier {
}
return count
}
+
+ private fun checkTimeNotifierChanged(context: Context) {
+ Log.v(LOG_ID, "checkTimeNotifierChanged called cur-time-count=$timeNotifierCount - cur-obsolete-count=$obsoleteNotifierCount")
+ var trigger = false
+ val newTimeCount = getNotifierCount(NotifySource.TIME_VALUE)
+ if(timeNotifierCount != newTimeCount) {
+ Log.d(LOG_ID, "Time notifier have changed from $timeNotifierCount to $newTimeCount")
+ val curCount = timeNotifierCount
+ timeNotifierCount = newTimeCount
+ if(curCount == 0 || newTimeCount == 0)
+ trigger = true
+ }
+ val newObsoleteCount = getNotifierCount(NotifySource.OBSOLETE_VALUE)
+ if(obsoleteNotifierCount != newObsoleteCount) {
+ Log.d(LOG_ID, "Obsolete notifier have changed from $obsoleteNotifierCount to $newObsoleteCount")
+ val curCount = obsoleteNotifierCount
+ obsoleteNotifierCount = newObsoleteCount
+ if(curCount == 0 || newObsoleteCount == 0)
+ trigger = true
+ }
+ if(trigger)
+ notify(context, NotifySource.TIME_NOTIFIER_CHANGE, null)
+ }
+
+ private fun hasSource(notifier: NotifierInterface, notifySource: NotifySource): Boolean {
+ if(hasNotifier(notifier)) {
+ return notifiers[notifier]!!.contains(notifySource)
+ }
+ return false
+ }
}
\ No newline at end of file
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/NotifySource.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/NotifySource.kt
index 79f4e731..04867907 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/NotifySource.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/notifier/NotifySource.kt
@@ -12,7 +12,9 @@ enum class NotifySource {
TIME_VALUE,
SOURCE_SETTINGS,
SOURCE_STATE_CHANGE,
- NOTIFIER_CHANGE,
+ TIME_NOTIFIER_CHANGE,
IOB_COB_CHANGE,
- LOGCAT_REQUEST;
+ IOB_COB_TIME,
+ LOGCAT_REQUEST,
+ PATIENT_DATA_CHANGED;
}
\ No newline at end of file
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/BackgroundTaskService.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/BackgroundTaskService.kt
index 4769fb83..668dde2f 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/BackgroundTaskService.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/BackgroundTaskService.kt
@@ -148,9 +148,12 @@ abstract class BackgroundTaskService(val alarmReqId: Int, protected val LOG_ID:
private fun getInterval(): Long {
var newInterval = -1L
backgroundTaskList.forEach {
- if (it.active(elapsedTimeMinute) && newInterval <= 0L || it.getIntervalMinute() < newInterval)
+ if (it.active(elapsedTimeMinute) && newInterval <= 0L || it.getIntervalMinute() < newInterval) {
newInterval = it.getIntervalMinute()
+ Log.v(LOG_ID, "New interval $newInterval set from task ${it.javaClass.simpleName}")
+ }
}
+ Log.v(LOG_ID, "using interval $newInterval")
return newInterval
}
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/DataSourceTask.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/DataSourceTask.kt
index 390bf6c7..8dcbfb63 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/DataSourceTask.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/DataSourceTask.kt
@@ -110,7 +110,11 @@ abstract class DataSourceTask(private val enabledKey: String, protected val sour
}
fun setState(state: SourceState, error: String = "", code: Int = -1) {
- Log.v(LOG_ID,"Set state for source " + source + ": " + state + " - " + error + " (" + code + ")")
+ if(state == SourceState.NONE) {
+ Log.v(LOG_ID,"Set state for source " + source + ": " + state + " - " + error + " (" + code + ")")
+ } else {
+ Log.w(LOG_ID,"Set state for source " + source + ": " + state + " - " + error + " (" + code + ")")
+ }
lastErrorCode = code
lastState = state
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ElapsedTimeTask.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ElapsedTimeTask.kt
index 11fbd9a2..7f1157e8 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ElapsedTimeTask.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ElapsedTimeTask.kt
@@ -11,7 +11,7 @@ import de.michelinside.glucodatahandler.common.notifier.NotifySource
class ElapsedTimeTask : BackgroundTask() {
companion object {
private val LOG_ID = "GDH.Task.Time.ElapsedTask"
- private var relativeTimeValue = false
+ private var relativeTimeValue = true
private var interval = 0L
val relativeTime: Boolean get() {return relativeTimeValue}
fun setInterval(new_interval: Long) {
@@ -22,7 +22,10 @@ class ElapsedTimeTask : BackgroundTask() {
}
override fun getIntervalMinute(): Long {
- return if (relativeTimeValue) 1L else interval
+ return if (relativeTimeValue) 1L
+ else if(interval > 0) interval
+ else if(InternalNotifier.hasTimeNotifier) 1L
+ else 0
}
override fun execute(context: Context) {
@@ -33,14 +36,15 @@ class ElapsedTimeTask : BackgroundTask() {
}
override fun active(elapsetTimeMinute: Long): Boolean {
- return (relativeTimeValue || interval > 0) && elapsetTimeMinute <= 60 && InternalNotifier.getNotifierCount(NotifySource.TIME_VALUE) > 0
+ Log.v(LOG_ID, "Check active for elapsed time $elapsetTimeMinute min - has notifier: ${InternalNotifier.hasTimeNotifier} - relativeTime: $relativeTime - interval: $interval")
+ return (relativeTime || interval > 0 || InternalNotifier.hasTimeNotifier) && elapsetTimeMinute <= 60
}
override fun checkPreferenceChanged(sharedPreferences: SharedPreferences, key: String?, context: Context): Boolean {
if ((key == null || key == Constants.SHARED_PREF_RELATIVE_TIME)) {
- if( relativeTimeValue != sharedPreferences.getBoolean(Constants.SHARED_PREF_RELATIVE_TIME, false) ) {
- relativeTimeValue = sharedPreferences.getBoolean(Constants.SHARED_PREF_RELATIVE_TIME, false)
- Log.d(LOG_ID, "relative time setting changed to " + relativeTimeValue)
+ if( relativeTimeValue != sharedPreferences.getBoolean(Constants.SHARED_PREF_RELATIVE_TIME, true) ) {
+ relativeTimeValue = sharedPreferences.getBoolean(Constants.SHARED_PREF_RELATIVE_TIME, true)
+ Log.d(LOG_ID, "relative time setting changed to " + relativeTime)
InternalNotifier.notify(context, NotifySource.TIME_VALUE, null)
return true
}
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/LibreViewSourceTask.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/LibreViewSourceTask.kt
index cb0987ec..6116d6a6 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/LibreViewSourceTask.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/LibreViewSourceTask.kt
@@ -3,6 +3,7 @@ package de.michelinside.glucodatahandler.common.tasks
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
+import android.os.Handler
import android.util.Log
import de.michelinside.glucodatahandler.common.BuildConfig
import de.michelinside.glucodatahandler.common.Constants
@@ -13,6 +14,7 @@ import de.michelinside.glucodatahandler.common.SourceState
import de.michelinside.glucodatahandler.common.notifier.DataSource
import de.michelinside.glucodatahandler.common.notifier.InternalNotifier
import de.michelinside.glucodatahandler.common.notifier.NotifySource
+import org.json.JSONArray
import org.json.JSONObject
import java.text.DateFormat
import java.text.SimpleDateFormat
@@ -32,6 +34,9 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
private var token = ""
private var tokenExpire = 0L
private var region = ""
+ private var patientId = ""
+ private var dataReceived = false // mark this endpoint as already received data
+ val patientData = mutableMapOf()
const val server = "https://api.libreview.io"
const val region_server = "https://api-%s.libreview.io"
const val LOGIN_ENDPOINT = "/llu/auth/login"
@@ -67,6 +72,23 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
Log.i(LOG_ID, "reset called")
token = ""
region = ""
+ dataReceived = false
+ patientData.clear()
+ saveRegion()
+ }
+
+ val sensitivData = mutableSetOf("id", "patienId", "firstName", "lastName", "did", "sn", "token", "deviceId", "email", "primaryValue", "secondaryValue" )
+
+ private fun replaceSensitiveData(body: String): String {
+ var result = body
+ sensitivData.forEach {
+ val groups = Regex("\"$it\":\"(.*?)\"").find(result)?.groupValues
+ if(!groups.isNullOrEmpty() && groups.size > 1 && groups[1].isNotEmpty()) {
+ val replaceValue = groups[0].replace(groups[1], "---")
+ result = result.replace(groups[0], replaceValue)
+ }
+ }
+ return result
}
private fun checkResponse(body: String?): JSONObject? {
@@ -76,7 +98,7 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
return null
}
if (BuildConfig.DEBUG) { // do not log personal data
- Log.i(LOG_ID, "Handle json response: " + body)
+ Log.i(LOG_ID, "Handle json response: " + replaceSensitiveData(body))
}
val jsonObj = JSONObject(body)
if (jsonObj.has("status")) {
@@ -95,6 +117,7 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
return jsonObj
}
setLastError("Missing data in response!", 500)
+ reset()
return null
}
@@ -130,6 +153,7 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
if (data.has("region")) {
region = data.optString("region", "")
Log.i(LOG_ID, "Handle redirect to region: " + region)
+ saveRegion()
return login()
} else {
setLastError("redirect without region!!!", 500)
@@ -160,6 +184,18 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
return token.isNotEmpty()
}
+ private fun saveRegion() {
+ try {
+ Log.d(LOG_ID, "Save region $region")
+ with (GlucoDataService.sharedPref!!.edit()) {
+ putString(Constants.SHARED_PREF_LIBRE_REGION, region)
+ apply()
+ }
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "saveRegion exception: " + exc.toString() )
+ }
+ }
+
private fun login(): Boolean {
if (token.isNotEmpty() && (reconnect || tokenExpire <= System.currentTimeMillis())) {
if (!reconnect)
@@ -240,10 +276,20 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
val array = jsonObject.optJSONArray("data")
if (array != null) {
if (array.length() == 0) {
- setLastError(GlucoDataService.context!!.getString(R.string.src_libre_setup_librelink))
+ Log.w(LOG_ID, "Empty data array in response: ${replaceSensitiveData(body!!)}")
+ if(dataReceived) {
+ setLastError("Missing data! Please send logs to developer.")
+ reset()
+ } else {
+ setLastError(GlucoDataService.context!!.getString(R.string.src_libre_setup_librelink))
+ }
+ return
+ }
+ val data = getPatientData(array)
+ if (data == null) {
+ setState(SourceState.NO_NEW_VALUE)
return
}
- val data = array.optJSONObject(0)
if(data.has("glucoseMeasurement")) {
val glucoseData = data.optJSONObject("glucoseMeasurement")
if (glucoseData != null) {
@@ -271,13 +317,57 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
if (sensor != null && sensor.has("sn"))
glucoExtras.putString(ReceiveData.SERIAL, sensor.optString("sn"))
}
+ dataReceived = true
handleResult(glucoExtras)
}
}
+ } else {
+ Log.e(LOG_ID, "No data array found in response: ${replaceSensitiveData(body!!)}")
+ setLastError("Invalid response! Please send logs to developer.")
+ reset()
+ return
}
}
}
+ private fun getPatientData(dataArray: JSONArray): JSONObject? {
+ if(dataArray.length() > patientData.size) {
+ // create patientData map
+ val checkPatienId = patientData.isEmpty() && patientId.isEmpty()
+ patientData.clear()
+ for (i in 0 until dataArray.length()) {
+ val data = dataArray.getJSONObject(i)
+ if(data.has("patientId") && data.has("firstName") && data.has("lastName")) {
+ val id = data.getString("patientId")
+ val name = data.getString("firstName") + " " + data.getString("lastName")
+ Log.v(LOG_ID, "New patient found: $name")
+ patientData[id] = name
+ }
+ }
+ if (checkPatienId && !patientData.keys.contains(patientId)) {
+ patientId = ""
+ with (GlucoDataService.sharedPref!!.edit()) {
+ putString(Constants.SHARED_PREF_LIBRE_PATIENT_ID, "")
+ apply()
+ }
+ }
+ Handler(GlucoDataService.context!!.mainLooper).post {
+ InternalNotifier.notify(GlucoDataService.context!!, NotifySource.PATIENT_DATA_CHANGED, null)
+ }
+ }
+ if(patientId.isNotEmpty()) {
+ for (i in 0 until dataArray.length()) {
+ val data = dataArray.getJSONObject(i)
+ if (data.has("patientId") && data.getString("patientId") == patientId) {
+ return data
+ }
+ }
+ return null
+ }
+ // default: use first one
+ return dataArray.optJSONObject(0)
+ }
+
private fun getConnection(firstCall: Boolean = true) {
if (login()) {
handleGlucoseResponse(httpGet(getUrl(CONNECTION_ENDPOINT), getHeader()))
@@ -287,13 +377,17 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
}
override fun checkPreferenceChanged(sharedPreferences: SharedPreferences, key: String?, context: Context): Boolean {
+ Log.v(LOG_ID, "checkPreferenceChanged called for $key")
var trigger = false
if (key == null) {
user = sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_USER, "")!!.trim()
password = sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_PASSWORD, "")!!.trim()
token = sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_TOKEN, "")!!
+ if(token.isNotEmpty())
+ dataReceived = true
tokenExpire = sharedPreferences.getLong(Constants.SHARED_PREF_LIBRE_TOKEN_EXPIRE, 0L)
region = sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_REGION, "")!!
+ patientId = sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_PATIENT_ID, "")!!
InternalNotifier.notify(GlucoDataService.context!!, NotifySource.SOURCE_STATE_CHANGE, null)
trigger = true
} else {
@@ -317,6 +411,14 @@ class LibreViewSourceTask : DataSourceTask(Constants.SHARED_PREF_LIBRE_ENABLED,
Constants.SHARED_PREF_LIBRE_RECONNECT -> {
if (reconnect != sharedPreferences.getBoolean(Constants.SHARED_PREF_LIBRE_RECONNECT, false)) {
reconnect = sharedPreferences.getBoolean(Constants.SHARED_PREF_LIBRE_RECONNECT, false)
+ Log.d(LOG_ID, "Reconnect triggered")
+ trigger = true
+ }
+ }
+ Constants.SHARED_PREF_LIBRE_PATIENT_ID -> {
+ if (patientId != sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_PATIENT_ID, "")) {
+ patientId = sharedPreferences.getString(Constants.SHARED_PREF_LIBRE_PATIENT_ID, "")!!
+ Log.d(LOG_ID, "PatientID changed to $patientId")
trigger = true
}
}
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/NightscoutSourceTask.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/NightscoutSourceTask.kt
index d427bcd1..4bf02b79 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/NightscoutSourceTask.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/NightscoutSourceTask.kt
@@ -27,7 +27,13 @@ class NightscoutSourceTask: DataSourceTask(Constants.SHARED_PREF_NIGHTSCOUT_ENAB
override fun hasIobCobSupport(): Boolean = active(1L) && iob_cob_support
- override fun needsInternet(): Boolean = false
+ override fun needsInternet(): Boolean {
+ if(url.contains("127.0.0.1") || url.lowercase().contains("localhost")) {
+ Log.v(LOG_ID, "Localhost detected!")
+ return false
+ }
+ return true
+ }
override fun executeRequest(context: Context) {
if (!handlePebbleResponse(httpGet(getUrl(PEBBLE_ENDPOINT), getHeader()))) {
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ObsoleteTask.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ObsoleteTask.kt
index 132fa27b..107ab8eb 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ObsoleteTask.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/ObsoleteTask.kt
@@ -25,6 +25,7 @@ class ObsoleteTask : BackgroundTask() {
}
override fun active(elapsetTimeMinute: Long): Boolean {
- return elapsetTimeMinute <= 10 && (InternalNotifier.getNotifierCount(NotifySource.OBSOLETE_VALUE) > 0 || (!ElapsedTimeTask.relativeTime && InternalNotifier.getNotifierCount(NotifySource.TIME_VALUE) > 0) )
+ Log.v(LOG_ID, "Check active for elapsed time $elapsetTimeMinute min - has notifier: ${InternalNotifier.hasObsoleteNotifier} - time active: ${(!ElapsedTimeTask.relativeTime && InternalNotifier.hasTimeNotifier)}")
+ return elapsetTimeMinute <= 10 && (InternalNotifier.hasObsoleteNotifier || (!ElapsedTimeTask.relativeTime && InternalNotifier.hasTimeNotifier) )
}
}
\ No newline at end of file
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/TimeTaskService.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/TimeTaskService.kt
index 4068264d..13cc6cfd 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/TimeTaskService.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/tasks/TimeTaskService.kt
@@ -11,8 +11,8 @@ import de.michelinside.glucodatahandler.common.notifier.NotifySource
object TimeTaskService: BackgroundTaskService(42, "GDH.Task.Time.TaskService") {
override fun getAlarmReceiver() : Class<*> = TimeAlarmReceiver::class.java
- // also notify for NOTIFIER_CHANGE as if there is no receiver, the alarm manager is not needed
- override fun getNotifySourceFilter() : MutableSet = mutableSetOf(NotifySource.NOTIFIER_CHANGE)
+ // also notify for TIME_NOTIFIER_CHANGE as if there is no receiver, the alarm manager is not needed
+ override fun getNotifySourceFilter() : MutableSet = mutableSetOf(NotifySource.TIME_NOTIFIER_CHANGE)
override fun getBackgroundTasks(): MutableList =
mutableListOf(ElapsedTimeTask(), ObsoleteTask())
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/utils/BitmapUtils.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/utils/BitmapUtils.kt
index d8f0207d..1e38fc60 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/utils/BitmapUtils.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/utils/BitmapUtils.kt
@@ -1,5 +1,6 @@
package de.michelinside.glucodatahandler.common.utils
+import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
@@ -15,9 +16,24 @@ import de.michelinside.glucodatahandler.common.GlucoDataService
import de.michelinside.glucodatahandler.common.ReceiveData
import kotlin.math.abs
+
object BitmapUtils {
private val LOG_ID = "GDH.Utils.Bitmap"
+
+ fun getScreenWidth(): Int {
+ return Resources.getSystem().displayMetrics.widthPixels
+ }
+
+ fun getScreenHeight(): Int {
+ return Resources.getSystem().displayMetrics.heightPixels
+ }
+
+ fun getScreenDpi(): Int {
+ return Resources.getSystem().displayMetrics.densityDpi
+ }
+
+
private fun isShortText(text: String): Boolean = text.length <= (if (text.contains(".")) 3 else 2)
private fun calcMaxTextSizeForBitmap(bitmap: Bitmap, text: String, roundTarget: Boolean, maxTextSize: Float, top: Boolean, bold: Boolean): Float {
diff --git a/common/src/main/java/de/michelinside/glucodatahandler/common/utils/Utils.kt b/common/src/main/java/de/michelinside/glucodatahandler/common/utils/Utils.kt
index 4a1eafb0..2d298973 100644
--- a/common/src/main/java/de/michelinside/glucodatahandler/common/utils/Utils.kt
+++ b/common/src/main/java/de/michelinside/glucodatahandler/common/utils/Utils.kt
@@ -5,7 +5,6 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
-import android.graphics.*
import android.net.Uri
import android.os.Build
import android.os.Bundle
@@ -121,7 +120,7 @@ object Utils {
}
fun isPackageAvailable(context: Context, packageName: String): Boolean {
- return context.packageManager.getInstalledApplications(0).find { info -> info.packageName.startsWith(packageName) } != null
+ return context.packageManager.getLaunchIntentForPackage(packageName) != null
}
fun getBackgroundColor(transparancyFactor: Int) : Int {
@@ -200,7 +199,7 @@ object Utils {
fun saveLogs(outputStream: OutputStream) {
try {
- val cmd = "logcat -t 3000"
+ val cmd = "logcat -t 4000"
Log.i(LOG_ID, "Getting logcat with command: $cmd")
val process = Runtime.getRuntime().exec(cmd)
val thread = Thread {
diff --git a/common/src/main/res/drawable-night/icon_overlay.xml b/common/src/main/res/drawable-night/icon_overlay.xml
new file mode 100644
index 00000000..5512e57e
--- /dev/null
+++ b/common/src/main/res/drawable-night/icon_overlay.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/common/src/main/res/drawable/icon_overlay.xml b/common/src/main/res/drawable/icon_overlay.xml
new file mode 100644
index 00000000..f7c2c024
--- /dev/null
+++ b/common/src/main/res/drawable/icon_overlay.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/common/src/main/res/drawable/icon_overlay_white.xml b/common/src/main/res/drawable/icon_overlay_white.xml
new file mode 100644
index 00000000..5512e57e
--- /dev/null
+++ b/common/src/main/res/drawable/icon_overlay_white.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/common/src/main/res/values-de/strings.xml b/common/src/main/res/values-de/strings.xml
index 4a04ffc4..78e7e99b 100644
--- a/common/src/main/res/values-de/strings.xml
+++ b/common/src/main/res/values-de/strings.xml
@@ -88,7 +88,7 @@
Passwort für LibreLinkReconnectBei der nächsten Ausführung wird ein erneutes Login ausgeführt.
- Bitte erst LibreLinkUp Verbindung aktivieren!
+ Keine Daten! Bitte akzeptieren Sie zuerst die Einladung in der LibreLinkUp App!Nightscout DatenquelleAktiv
@@ -149,13 +149,13 @@
mmol/lBenutze mmol/l anstatt mg/dl.Hoher Glukosewert
- Grenze für hohe Glukosewerte.\nWenn in Juggluco der Alarm für hohen Glukosewert aktiviert ist, muss der Wert nicht gesetzt werden.
+ Grenze für hohe Glukosewerte.Obere GrenzeObere Grenze für den Zielbereich.Untere GrenzeUntere Grenze für den Zielbereich.Niedriger Glukosewert
- Grenze für niedrige Glukosewerte.\nWenn in Juggluco der Alarm für niedrigen Glukosewert aktiviert ist, muss der Wert nicht gesetzt werden.
+ Grenze für niedrige Glukosewerte.5 Minuten DeltaFür Delta Werte im 5 Minuten Intervall, ansonsten wird der Delta Wert pro Minute angezeigt.Hohe/Niedrige Werte
@@ -284,7 +284,8 @@
JugglucoKonfiguration von Juggluco:\n- Juggluco App öffnen\n- Einstellungen öffnen\n- Glucodata broadcast aktivieren\n- \"de.michelinside.glucodatahandler\" auswählenxDrip+
- Konfiguration von xDrip+:\n- xDrip+ App öffnen\n- Einstellungen öffnen\n- Inter-App Einstellungen auswählen\n- Lokaler Broadcast aktivieren\n- in Verrauschungsunterdrückung \"Send even Extremely noisy signals\" auswählen\n- Kompatible Broadcasts aktivieren\n- Identifiziere Empfänger leer lassen oder eine neue Zeile mit \"de.michelinside.glucodatahandler\" hinzufügen
+ Konfiguration von xDrip+:\n- xDrip+ App öffnen\n- Einstellungen öffnen\n- Inter-App Einstellungen auswählen\n- Lokaler Broadcast aktivieren\n- in Verrauschungsunterdrückung \"Send even Extremely noisy signals\" auswählen
+ - Kompatible Broadcasts aktivieren\n- Identifiziere Empfänger leer lassen oder eine neue Zeile mit \"de.michelinside.glucodatahandler\" hinzufügenLibreLinkUp einrichtenWICHTIG: nicht den LibreView Zugang verwenden!\nEinrichtung:\n- FreeStyle Libre App öffnen und unter Verbinden auf Teilen oder Verbundene Anwendungen klicken\n- LibreLinkUp aktivieren\n- LibreLinkUp aus dem PlayStore installieren\n- in der LibreLinkUp App einloggen und die Einladung annehmenKontakt
@@ -310,5 +311,14 @@
Für alle Zeit und Intervall spezifischen Aufgaben, benötigt die App die Berechtigung für Wecker und Erinnerungen.\nDie App verändert keine vom Benutzer eingerichteten Wecker oder Erinnerung. Sie benötigt die Berechtigung nur für interne Trigger.\nNach dem Drücken von OK leitet die App Sie zur entsprechenden Berechtigungseinstellung weiter. Bitte aktivieren Sie diese Berechtigung für GlucoDataHandler.Berechtigung für Wecker und ErinnerungenGenaue Bearbeitung von Intervallen deaktiviert!!!\nKorrekte Funktionalität von GlucoDataHandler ist nicht gewährleistet!!!\nBitte hier drücken, um zu der entsprechenden Berechtigungseinstellung zu gelangen.
-
+ Patient
+ Wenn der Libre Accout für mehr als einen Patienten verwendet wird, bitte wählen sie den entsprechenden Patienten aus, für den die Daten empfangen werden sollen.
+ Lockscreen Hintergrund
+ Aktivieren
+ Wenn sie den Hintergrund für den Lockscreen aktivieren, erstellt die App einen Hintergrund auf dem Lockscreen, welche den Glucosewert und den Trendpfeil enthält.
+ Vertikale Position
+ Vertikale position auf dem Lockscreen:\n0: am oberen Rand des Bildschirms\n100: am unteren Rand des Bildschirms
+ !!!Die Anzeige von Benachrichtigungen ist deaktiviert!!!\nKorrekte Funktionalität von GlucoDataHandler ist nicht gewährleistet und es können keine Alarme angezeigt werden!!!\nBitte hier drücken, um zu der entsprechenden Berechtigungseinstellung zu gelangen.
+ Farbiges Statusbar Icon
+ Falls dein Gerät ein farbiges Statusbar Icon unterstützt, kannst du es auch deaktivieren, um ein weißes Icon zu benutzen.
diff --git a/common/src/main/res/values-pl/strings.xml b/common/src/main/res/values-pl/strings.xml
index a497d0cd..aba474df 100644
--- a/common/src/main/res/values-pl/strings.xml
+++ b/common/src/main/res/values-pl/strings.xml
@@ -5,7 +5,7 @@
WartośćSuroweDelta
- Tempo
+ TrendZnacznik czasuCzas IOB/COBRóżnica czasu
@@ -29,10 +29,10 @@
Anuluj-
- bardzo niski
- niski
- wysoki
- bardzo wysoki
+ bardzo nisko
+ nisko
+ wysoko
+ bardzo wysoko
@@ -89,7 +89,6 @@
Hasło do LibreLinkUpPołącz ponownieW kolejnym odstępie czasowym nastąpi nowe logowanie.
- Najpierw skonfiguruj połączenie LibreLinkUp!Nightscout FollowerWłącz
@@ -129,13 +128,13 @@
Wartości fikcyjneWysokie stęż. glukozy
- Granica dla wysokich wartości stężenia glukozy.\nJeśli aktywowałeś alarm wysokiego stęż. glukozy w Juggluco, nie musisz ustawiać tej wartości.
+ Granica dla wysokich wartości stężenia glukozy.Górna granica zakresu docelowegoNajwyższa wartość dla zakresu docelowego.Dolna granica zakresu docelowegoNajniższa wartość dla zakresu docelowego.Niskie stęż. glukozy
- Granica dla niskich wartości glukozy.\nJeśli aktywowałeś alarm niskiego stęż. glukozy w Juggluco, nie musisz ustawiać tej wartości.
+ Granica dla niskich wartości stężenia glukozy.Powiadomienia w Android AutoPowiadomienie dla Android Auto.Odstęp między powiadomieniami
@@ -213,7 +212,7 @@
- Otrzymywanie danych do komplikacji
+ Odbiór danych do komplikacjiTelefon: połączony (%1$s)Telefon: rozłączony
@@ -286,7 +285,8 @@
JugglucoAby odbierać wartości z Juggluco: \n- otwórz Juggluco \n- przejdź do ustawień \n- włącz Glucodata broadcast \n- włącz \"de.michelinside.glucodatahandler \"XDrip+
- Aby odbierać wartości z xDrip+:\n- otwórz xDrip+\n- przejdź do ustawień\n- przejdź do ustawień innych aplikacji\n- włącz \"Nadawaj lokalnie\"\n- ustaw \"Blokowanie szumów\" na \"Send even Extremely noisy signals\"\n- włącz \"Kompatybilny Broadcast\"\n- sprawdź, czy pole \"Identyfikuj odbiornik\" jest puste lub dodaj nową linię z \"de.michelinside.glucodatahandler\"
+ Aby odbierać wartości z xDrip+:\n- otwórz xDrip+\n- przejdź do ustawień\n- przejdź do ustawień innych aplikacji\n- włącz \"Nadawaj lokalnie\"\n- ustaw \"Blokowanie szumów\" na \"Send even Extremely noisy signals\"
+ - włącz \"Kompatybilny Broadcast\"\n- sprawdź, czy pole \"Identyfikuj odbiornik\" jest puste lub dodaj nową linię z \"de.michelinside.glucodatahandler\"Konfiguracja LibreLinkUpWAŻNE: to nie jest konto LibreView! Aby aktywować LibreLinkUp:\n- otwórz aplikację FreeStyle Libre i wybierz w menu Udostępnianie lub Podłączone aplikacje\n- aktywuj połączenie LibreLinkUp\n- zainstaluj LibreLinkUp ze Sklepu Play\n- skonfiguruj swoje konto i czekaj na zaproszenieKontakt
@@ -313,4 +313,18 @@
Zezwalaj na ustawianie alarmów i przypomnieńUprawnienie do ustawiania alarmów i przypomnień jest wyłączone!!!\nGlucoDataHandler może nie działać poprawnie!!!\nNaciśnij tutaj, aby przejść bezpośrednio do ustawień.
+
+
+ Pacjent
+ Jeśli do konta libre podłączony jest więcej niż jeden pacjent, wybierz pacjenta, dla którego odbierane są dane.
+ Tapeta ekranu blokady
+ Włącz
+ Jeśli włączysz tapetę ekranu blokady, aplikacja zastąpi tapetę ekranu blokady obrazem zawierającym wartość stężenia glukozy i trend.
+ Położenie pionowe
+ Pozycja pionowa obrazu wartości glukozy - trendu na ekranie blokady: \n0 to góra ekranu\n100 to dół ekranu
+ Wyłączono wyśietlanie powiadomień!!!\nGlucoDataHandler może nie działać poprawnie i nie może pokazywać alarmów!!!\nNaciśnij tutaj, aby przejść bezpośrednio do ustawień uprawnień.
+
+ Brak danych! Najpierw zaakceptuj zaproszenie w aplikacji LibreLinkUp!
+ Kolorowa ikona w pasku stanu
+ Jeżeli telefon obsługuje kolorowe ikony w pasku stanu, możesz wyłączyć tę opcję, by ikona była biała.
diff --git a/common/src/main/res/values-pt/strings.xml b/common/src/main/res/values-pt/strings.xml
new file mode 100644
index 00000000..6917d421
--- /dev/null
+++ b/common/src/main/res/values-pt/strings.xml
@@ -0,0 +1,335 @@
+
+
+ Sensor
+ Valor
+ Raw
+ Delta
+ Taxa
+ Leitura
+ Leitura IOB/COB
+ Timediff
+ Alarme
+ Fonte
+ por minuto
+ Nenhum dado recebido ainda!\nConfigure uma fonte primeiro.
+
+ %1$d min
+ \> 1h
+
+ subindo muito rapidamente
+ subindo rapidamente
+ subindo
+ estável
+ descendo
+ descendo rapidamente
+ descendo muito rapidamente
+
+ OK
+ Cancelar
+
+ -
+ muito baixa
+ baixa
+ alta
+ muito alta
+
+
+
+ Otimização da bateria está habilitada.
+ Fonte: não ativa
+ Wear: conectado (%1$s)
+ Wear: desconectado
+ Android Auto: conectado
+ Android Auto: desconectado
+ A acessibilidade de alto contraste está ativa!\nPressione aqui para acessar as configurações.
+ Sem novos valores há %1$d minutos!
+
+ Configurações
+ Fontes
+ GitHub
+ Atualiações
+ Suporte
+ Ajuda
+
+ https://github.com/pachi81/GlucoDataHandler/blob/master/README.md
+
+
+
+ Novo valor de glicose
+ Valor obsoleto
+ Alarme de glicose
+ Wear conexão
+ Android Auto conexão
+ Glucose range change
+
+
+ Telefone
+ Wear
+
+ Não ativo
+ Em progresso
+ OK
+ Nenhum novo valor
+ Sem conexão com a Internet
+ Erro
+
+ Intervalo
+ Intervalo de solicitação de dados da fonte.
+ Atraso
+ Atraso em segundos antes de solicitar os dados da nuvem, pois demora algum tempo até que os dados estejam presentes lá.
+
+ LibreLink Seguidor
+ Habilita
+ Habilite o seguidor do LibreLink, se o usuário e a senha do LibreLink estiverem definidos.
+ E-mail
+ E-mail para acesso LibreLink
+ Senha
+ Senha para acesso LibreLink
+ Reconectar
+ No próximo intervalo um novo login será requerido.
+
+ Seguidor Nightscout
+ Habilita
+ Habilita Nightscout seguidor.
+ Nightscout URL
+ URL do servidor Nightscout.
+ API Secret
+ API Secret para acesso aos dados (opcional).
+ Access Token
+ Adicione seu token de acesso, se preciso for (opcional).
+
+ Sempre
+ 1 minuto
+ 5 minutos
+ 10 minutos
+ 15 minutos
+ 20 minutos
+ 30 minutos
+ Alarme somente
+
+
+
+ desabilitado
+ não conectado
+ conectado
+ erro: %s
+
+
+
+
+ Geral
+ Notificação
+ Widgets
+ Android Auto
+ Forward valores de glicose
+ Faixa do alvo
+ Coress
+
+ Valores fictícios
+ Glicose alta
+ Limite para valores altos de glicose.
+ Meta de glicose superior
+ Valor superior para o intervalo desejado.
+ Meta de glicose superior inferior
+ Valor inferior para o intervalo desejado.
+ Glicose baixa
+ Limite para valores baixos de glicose.
+ Android Auto notificações
+ Notificação para Android Auto.
+ Intervalo de notificação
+ Intervalo mínimo para exibição de notificações de um novo valor.\nOs alarmes sempre mostrarão uma notificação.
+ Apenas alarme
+ Acionará notificações de alarmes apenas em intervalos específicos.\nValores muito baixos sempre mostrarão uma notificação.
+ Leitor de mídia fictício
+ Mostre o valor da glicose como música atual para Android Auto.
+
+Enviar transmissão do Glucodata
+ Envie transmissão do Glucodata para novos valores recebidos de qualquer fonte.
+ Identificar receptores do Glucodata
+ Escolha os receptores para os quais enviar a transmissão do Glucodata. Uma transmissão global será enviada para todos os aplicativos registrados dinamicamente na transmissão.
+ Enviar transmissão Libre (pached)
+ Encaminhe os valores de glicose para xDrip+. Selecione \'Libre (patched app)\' como fonte no xDrip+.
+ Identifique receptores Libre (pached)
+ Escolha os receptores para os quais enviar a transmissão Libre (patched). Uma transmissão global será enviada para todos os aplicativos registrados dinamicamente na transmissão
+ Enviar transmissão xDrip+
+ Envie a transmissão como o xDrip+ está enviando. Por exemplo, para AAPS.
+
+Identifique receptores de transmissão xDrip+
+ Escolha os receptores para os quais enviar a transmissão xDrip+. Uma transmissão global será enviada para todos os aplicativos registrados dinamicamente na transmissão.
+ Use mmol/l
+ Use mmol/l em vez de mg/dl.
+ 5 minutos delta
+ Ative esta opção para usar valores delta para intervalos de 5 minutos; caso contrário, será usado intervalo de 1 minuto.
+ Cores Alto/Baixo
+ Cor para valores altos e baixos de glicose.
+ Cor fora do intervalo
+ Cor para valores de glicose fora da faixa alvo.
+ Cor alvo
+ Cor para valores de glicose na faixa do alvo.
+ Notificação
+ Mostra uma notificação permanente com a seta de tendência atual, valor de glicose e delta na barra de status.\nEsta notificação impede que o Android interrompa este app.
+ Ícone da barra de status
+ Estilo pequeno para o ícone da barra de status da notificação.
+ Ícone grande na barra de status
+ Ícone maior na barra de status. Você tem que verificar se o ícone está cortado, se fica muito grande.
+ Ocultar conteúdo
+ Remover o conteúdo da notificação, para ter apenas uma pequena notificação com o ícone relacionado.
+ 2. notificação
+
+Mostre uma segunda notificação sem conteúdo para ter um ícone adicional na barra de status.
+ 2. ícone da barra de status
+ Estilo do pequeno para o ícone da barra de status da segunda notificação.
+ Widget flutuante
+ Mostre glicose, tendência, delta, tempo e IOB/COB como widget flutuante.\nSe você segurar e não mover o widget por mais de 5 segundos, ele desaparecerá.
+ Tamanho
+ Alterar o tamanho do widget flutuante
+ Estilo
+ Estilo do widget flutuante.
+ Transparência
+ Transparência do fundo do widget flutuante.
+ Transparência do Widget
+ Transparência do fundo do widget.
+ Tempo relativo
+ Use o tempo relativo em vez do valor recebido de data/hora da glicose para widgets.\nO tempo relativo depende das configurações da bateria do seu dispositivo e pode não funcionar corretamente.
+
+ ïcone do App
+ Valor da Glicose
+ Seta de tendência
+ Delta
+
+ Wear: vibração
+
+ Global Transmissão
+
+ Mostrar tudo
+ Nenhum receptor encontrado!
+
+ Glicose
+ Glicose e tendência
+ Glicose, tendência e delta
+ Glicose, tendência delta e timestamp
+ Glicose, tendência, delta, tempo e IOB/COB
+
+ Suporte BangleJS
+ Encaminhar valores para o widget widbgjs para BangleJS.
+
+
+
+ Recebendo dados para complicações
+
+ Telefone: conectado (%1$s)
+ Telefone: desconectado
+
+ Primeiro plano
+ Seta de tendência grande
+ Vibração
+ Colorido AOD
+
+
+
+ Glicose
+ Glicose (grande e colorida)
+ Glicose (colorida)
+ Glicose (grande)
+ Glicose com ícone
+ Glicose e tendência
+ Glicose e tendência (grande e colorida)
+ Glicose e tendência (colorida)
+ Glicose and faixa de tendência (se suportado)
+ Glicose, delta e faixa de tendência (se suportado)
+ Delta, seta de tendência e faixa de tendência (se suportado)
+ Glicose e tendência
+ Glicose, delta e tendência
+ Glicose, delta, tendência e timestamp
+ Glicose e valor de tendência
+ Delta (grande)
+ Delta
+ Delta and tendência
+ Delta com ícone
+ Delta e timestamp
+ Tendência (grande, colorido)
+ Tendência (colorido)
+ Ícone de tendência
+ Complicação do intervalo de teste com valores negativos (para suporte ao intervalo de tendência)
+ Valor de tendência
+ Valor de tendênciae seta
+ Valor de tendência e ícone
+ Glicose timestamp
+ Nível da bateria (wear e Telefone)
+ IOB/COB
+
+
+
+ https://github.com/pachi81/GlucoDataAuto/blob/main/README.md
+ Para usar este aplicativo no Android Auto, você deve ativar as configurações do desenvolvedor no Android Auto clicando várias vezes na versão. Então você deve ativar \"Fontes desconhecidas\" no menu de configurações do desenvolvedor.\n\nSe você estiver usando GlucoDataHandler como fonte, os valores serão enviados se o telefone estiver conectado ao Android Auto.
+ Informação
+ Se você estiver usando GlucoDataHandler como fonte, essas configurações serão sobrescritas no GlucoDataHandler, portanto você não precisa alterá-las aqui.
+ GlucoDataAuto ausente
+ TPara usar o GlucoDataHandler para Android Auto, você deve instalar o GlucoDataAuto do GitHub.\nClique aqui para obter mais informações e fazer o download do GlucoDataAuto.
+ Encaminhar para GlucoDataAuto
+ Envie valores e configurações de glicose para GlucoDataAuto, se o telefone estiver conectado ao Android Auto.
+
+
+
+
+
+ Notificação em primeiro plano
+ Notificação em primeiro plano que impede a interrupção do aplicativo durante a execução em segundo plano.
+ Segunda notificação
+ Notificação adicional para mostrar um ícone extra na barra de status.
+ Worker notification
+ Worker notification shown while running tasks in the background.
+ Notificação em primeiro plano
+ Notificação em primeiro plano que impede a interrupção do aplicativo durante a execução em segundo plano.
+ Notificação para Android Auto
+ Notification which is shown in Android Auto
+ IOB
+ COB
+ NNoise-Block ativo nas configurações entre aplicativos do xDrip+!\nPor favor, altere para \"Extremamente barulhento\"!
+ Seguidor
+ Juggluco
+ Para receber valores do Juggluco:\n- abra o Juggluco\n- acesse as configurações\n- ative a transmissão do Glucodata\n- ative \"de.michelinside.glucodatahandler\"
+ xDrip+
+ Para receber valores do xDrip+:\n- abra o xDrip+\n- vá em configurações\n- vá em configurações entre aplicativos\n- ative \"Transmitir local\"\n- defina \"Bloqueio de Leituras Irregulares (Noise)\" como \"Send even Extremely noisy signals\"
+ - habilite \"Broadcast Compatível\"\n- marque \"Identificar receptores\" preencha com \"de.michelinside.glucodatahandler\" (sem aspas), use espaço como separador caso já tenha algo preenchido
+ Configurar LibreLinkUp
+ IMPORTANTE: esta não é a conta LibreView!\nPara ativar o LibreLinkUp:\n- abra seu aplicativo FreeStyle Libre e selecione no menu Compartilhar ou Aplicativos conectados\n- ative a conexão LibreLinkUp\n- instale o LibreLinkUp da PlayStore\n- configure seu conta e aguarde o convite
+ Contato
+ IOB/COB support
+ Receba também valores IOB e COB do endpoint de seixo Nightscout.
+ A notificação é obrigatória para o funcionamento correto do aplicativo.\nA partir do Android 13, você pode remover esta notificação. Se você não quiser que a notificação seja de volta, defina o \"Ícone da barra de status\" como \"Ícone do aplicativo\" e ative \"Ocultar conteúdo\".
+ AAPS
+ AndroidAPS
+ Para receber valores do AAPS:\n- abra o aplicativo AAPS\n- vá para \"Config Builder\"\n- ative \"Samsung Tizen\"
+ WatchDrip+
+ "Ative a comunicação WatchDrip+ (sem gráfico).\nIMPORTANTE: ative \"Ativar serviço\" no WatchDrip+ depois de ativar esta configuração aqui!"
+ Reaparecer
+ ntervalo em que a notificação reaparecerá caso não haja novo valor. (0 para nunca).
+ Estilo do ícone/imagem fictício do media player.
+ Estilo do ïcone/imagem
+ Mobile
+ Wear
+ Salvar logs
+ Logs salvos com sucesso
+ Falha salvando logs!
+ Logs do wear salvos com sucesso
+ Falha ao savar logs do wear!
+ Para todos os trabalhos relacionados a tempo/intervalo, este aplicativo requer permissão para alarmes e alarmes. lembretes.\nEle não irá adicionar ou alterar nenhum lembrete do usuário, é apenas para agendamento interno.\nSe você pressionar OK, você irá encaminhar para a configuração de permissão para habilitá-lo para GlucoDataHandler.
+ Alarmes & permissão de lembretes
+ O alarme de agendamento exato está desabilitado!!!\nGlucoDataHandler pode não funcionar corretamente!!!\nPressione aqui para ir direto para a configuração de permissão.
+
+
+
+ Paciente
+ Se houver mais de um paciente conectado à conta libre, selecione o paciente para o qual receberá os dados.
+ Papel de parede da tela de bloqueio
+ Habilitado
+ Se você ativar o papel de parede da tela de bloqueio, o aplicativo substituirá o papel de parede da tela de bloqueio por uma imagem contendo o valor e a tendência.
+ Posição vertical
+ Posição vertical da seta de tendência na tela de bloqueio:\n0 na parte superior da tela\n100 na parte inferior da tela
+ Notificação está desabilitada!\nGlucoDataHandler pode não funcionar corretamente e pode não gerar alarmes!!!\n Pressione aqui para ir para a tela de configuração de permissões.
+
+ Por favor, aceite o convite no aplicativo LibreLinkUp primeiro!
+ Ícone colorido da barra de status
+ Se o seu dispositivo suportar ícones coloridos da barra de status, você também poderá desativá-lo para usar um item branco.
+
diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml
index 8d1b13de..e3270c74 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -99,7 +99,6 @@
Password for LibreLinkReconnectOn next interval a new login will be executed.
- Please set up LibreLinkUp connection first!Nightscout FollowerEnable
@@ -140,13 +139,13 @@
Dummy valuesHigh glucose
- Border for high glucose values.\nIf you have activated high alarm in Juggluco, you do not have to set this value.
+ Border for high glucose values.Target top glucoseTop value for the target range.Target bottom glucoseBottom value for the target range.Low glucose
- Border for low glucose values.\nIf you have activated low alarm in Juggluco, you do not have to set this value.
+ Border for low glucose values.Android Auto notificationsNotification for Android Auto.Notification interval
@@ -299,7 +298,8 @@
JugglucoTo receive values from Juggluco:\n- open Juggluco\n- go to settings\n- enable Glucodata broadcast\n- enable \"de.michelinside.glucodatahandler\"xDrip+
- To receive values from xDrip+:\n- open xDrip+\n- go to settings\n- go to Inter-app settings\n- enable \"Broadcast locally\"\n- set \"Noise Blocking\" to \"Send even Extremely noisy signals\"\n- enable \"Compatible Broadcast\"\n- check \"Identify receivers\" to be empty or add a new line with \"de.michelinside.glucodatahandler\"
+ To receive values from xDrip+:\n- open xDrip+\n- go to settings\n- go to Inter-app settings\n- enable \"Broadcast locally\"\n- set \"Noise Blocking\" to \"Send even Extremely noisy signals\"
+ - enable \"Compatible Broadcast\"\n- check \"Identify receivers\" to be empty or add a new line with \"de.michelinside.glucodatahandler\"Setup LibreLinkUpIMPORTANT: this is not the LibreView account!\nTo activate LibreLinkUp:\n- open your FreeStyle Libre App and select in the menu Share or Connected Apps\n- activate LibreLinkUp connection\n- install LibreLinkUp from PlayStore\n- setup your account and wait for the invitationContact
@@ -325,4 +325,19 @@
For all time/interval related work, this app requires the permission for alarms & reminders.\nIt will not add or change any user reminders, it is only for internal scheduling.\nIf you press OK, you will forward to the permission setting to enable it for GlucoDataHandler.Alarms & reminders permissionSchedule exact alarm is disabled!!!\nGlucoDataHandler may not work correct!!!\nPress here to go direct to the permission setting.
+
+
+
+ Patient
+ If there is more then one patient connected to the libre account, please select the patient receiving the data for.
+ Lockscreen Wallpaper
+ Enable
+ If you enable lockscreen wallpaper, the app replaces the wallpaper on lockscreen with an Image containing the value and trend.
+ Vertical position
+ Vertical position of the glucose-trend image on lockscreen:\n0 is the top of the display\n100 is the botton of the display
+ Showing notifications is disabled!!!\nGlucoDataHandler may not work correct and can not show alarms!!!\nPress here to go direct to the permission setting.
+
+ Missing data! Please accept invitation in LibreLinkUp app first!
+ Colored status bar icon
+ If your device supports colored status bar icons, you can also deactivate it to use a white item.
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 403669e7..132b9f03 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Wed Dec 14 15:11:00 CET 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
diff --git a/images/LockscreenWallpaper.png b/images/LockscreenWallpaper.png
new file mode 100644
index 00000000..d7bba8b7
Binary files /dev/null and b/images/LockscreenWallpaper.png differ
diff --git a/mobile/build.gradle b/mobile/build.gradle
index 1c480f8a..abbd6677 100644
--- a/mobile/build.gradle
+++ b/mobile/build.gradle
@@ -25,17 +25,18 @@ android {
resValue "string", "app_name", "GlucoDataHandler"
}
dev_release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-dev-rules.pro'
versionNameSuffix '-dev'
resValue "string", "app_name", "GlucoDataHandler"
}
debug {
- applicationIdSuffix '.debug'
minifyEnabled false
resValue "string", "app_name", "GlucoDataHandler"
}
second {
applicationIdSuffix '.second'
- versionNameSuffix '-SECOND'
+ versionNameSuffix '_SECOND'
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
resValue "string", "app_name", "GDH Second"
diff --git a/mobile/proguard-dev-rules.pro b/mobile/proguard-dev-rules.pro
new file mode 100644
index 00000000..9f50960e
--- /dev/null
+++ b/mobile/proguard-dev-rules.pro
@@ -0,0 +1,32 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+-dontwarn **
+-keep class **
+-keepclassmembers class *{*;}
+-keepattributes *
+
+# --------------------------------------------------------------------
+# REMOVE all debug log messages
+# --------------------------------------------------------------------
+-assumenosideeffects class android.util.Log {
+ public static *** v(...);
+}
\ No newline at end of file
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
index 4b237cef..162b3105 100644
--- a/mobile/src/main/AndroidManifest.xml
+++ b/mobile/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
diff --git a/mobile/src/main/java/de/michelinside/glucodatahandler/GlucoDataServiceMobile.kt b/mobile/src/main/java/de/michelinside/glucodatahandler/GlucoDataServiceMobile.kt
index 46bd666b..ab1f32c5 100644
--- a/mobile/src/main/java/de/michelinside/glucodatahandler/GlucoDataServiceMobile.kt
+++ b/mobile/src/main/java/de/michelinside/glucodatahandler/GlucoDataServiceMobile.kt
@@ -15,6 +15,7 @@ import de.michelinside.glucodatahandler.tasker.setWearConnectionState
import de.michelinside.glucodatahandler.watch.WatchDrip
import de.michelinside.glucodatahandler.widget.FloatingWidget
import de.michelinside.glucodatahandler.widget.GlucoseBaseWidget
+import de.michelinside.glucodatahandler.widget.LockScreenWallpaper
class GlucoDataServiceMobile: GlucoDataService(AppSource.PHONE_APP), NotifierInterface {
@@ -35,7 +36,7 @@ class GlucoDataServiceMobile: GlucoDataService(AppSource.PHONE_APP), NotifierInt
fun sendLogcatRequest() {
if(connection != null) {
Log.d(LOG_ID, "sendLogcatRequest called")
- connection!!.sendMessage(NotifySource.LOGCAT_REQUEST, null, filterReiverId = connection!!.pickBestNodeId())
+ connection!!.sendMessage(NotifySource.LOGCAT_REQUEST, null, filterReceiverId = connection!!.pickBestNodeId())
}
}
}
@@ -57,12 +58,14 @@ class GlucoDataServiceMobile: GlucoDataService(AppSource.PHONE_APP), NotifierInt
GlucoseBaseWidget.updateWidgets(applicationContext)
WatchDrip.init(applicationContext)
floatingWidget.create()
+ LockScreenWallpaper.create(this)
} catch (exc: Exception) {
Log.e(LOG_ID, "onCreate exception: " + exc.message.toString() )
}
}
override fun getNotification(): Notification {
+ Log.v(LOG_ID, "getNotification called")
return PermanentNotification.getNotification(
!sharedPref!!.getBoolean(Constants.SHARED_PREF_PERMANENT_NOTIFICATION_EMPTY, false),
Constants.SHARED_PREF_PERMANENT_NOTIFICATION_ICON, true
@@ -76,6 +79,7 @@ class GlucoDataServiceMobile: GlucoDataService(AppSource.PHONE_APP), NotifierInt
CarModeReceiver.cleanup(applicationContext)
WatchDrip.close(applicationContext)
floatingWidget.destroy()
+ LockScreenWallpaper.destroy(this)
super.onDestroy()
} catch (exc: Exception) {
Log.e(LOG_ID, "onDestroy exception: " + exc.message.toString() )
diff --git a/mobile/src/main/java/de/michelinside/glucodatahandler/MainActivity.kt b/mobile/src/main/java/de/michelinside/glucodatahandler/MainActivity.kt
index c73b31a0..fc513339 100644
--- a/mobile/src/main/java/de/michelinside/glucodatahandler/MainActivity.kt
+++ b/mobile/src/main/java/de/michelinside/glucodatahandler/MainActivity.kt
@@ -53,9 +53,11 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
private lateinit var txtBatteryOptimization: TextView
private lateinit var txtHighContrastEnabled: TextView
private lateinit var txtScheduleExactAlarm: TextView
+ private lateinit var txtNotificationPermission: TextView
private lateinit var btnSources: Button
private lateinit var sharedPref: SharedPreferences
private lateinit var optionsMenu: Menu
+ private var floatingWidgetItem: MenuItem? = null
private val LOG_ID = "GDH.Main"
private var requestNotificationPermission = false
@@ -76,6 +78,7 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
txtBatteryOptimization = findViewById(R.id.txtBatteryOptimization)
txtHighContrastEnabled = findViewById(R.id.txtHighContrastEnabled)
txtScheduleExactAlarm = findViewById(R.id.txtScheduleExactAlarm)
+ txtNotificationPermission = findViewById(R.id.txtNotificationPermission)
btnSources = findViewById(R.id.btnSources)
PreferenceManager.setDefaultValues(this, R.xml.preferences, false)
@@ -158,8 +161,10 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
if (requestNotificationPermission && Utils.checkPermission(this, android.Manifest.permission.POST_NOTIFICATIONS, Build.VERSION_CODES.TIRAMISU)) {
Log.i(LOG_ID, "Notification permission granted")
requestNotificationPermission = false
+ txtNotificationPermission.visibility = View.GONE
GlucoDataServiceMobile.start(this, true)
}
+ GlucoDataService.checkForConnectedNodes(true)
} catch (exc: Exception) {
Log.e(LOG_ID, "onResume exception: " + exc.message.toString() )
}
@@ -167,13 +172,20 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
fun requestPermission() : Boolean {
requestNotificationPermission = false
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- if (!Utils.checkPermission(this, android.Manifest.permission.POST_NOTIFICATIONS, Build.VERSION_CODES.TIRAMISU)) {
- Log.i(LOG_ID, "Request notification permission...")
- requestNotificationPermission = true
- this.requestPermissions(arrayOf(android.Manifest.permission.POST_NOTIFICATIONS), 3)
- return false
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !Utils.checkPermission(this, android.Manifest.permission.POST_NOTIFICATIONS, Build.VERSION_CODES.TIRAMISU)) {
+ Log.i(LOG_ID, "Request notification permission...")
+ requestNotificationPermission = true
+ /*
+ txtNotificationPermission.visibility = View.VISIBLE
+ txtNotificationPermission.setOnClickListener {
+ val intent: Intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
+ .putExtra(Settings.EXTRA_APP_PACKAGE, this.packageName)
+ startActivity(intent)
+ }*/
+ this.requestPermissions(arrayOf(android.Manifest.permission.FOREGROUND_SERVICE, android.Manifest.permission.POST_NOTIFICATIONS), 3)
+ return false
+ } else {
+ txtNotificationPermission.visibility = View.GONE
}
requestExactAlarmPermission()
return true
@@ -225,7 +237,7 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
try {
val pm = getSystemService(POWER_SERVICE) as PowerManager
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
- Log.w(LOG_ID, "Battery optimization is inactive")
+ Log.w(LOG_ID, "Battery optimization is active")
txtBatteryOptimization.visibility = View.VISIBLE
txtBatteryOptimization.setOnClickListener {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
@@ -234,7 +246,7 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
}
} else {
txtBatteryOptimization.visibility = View.GONE
- Log.i(LOG_ID, "Battery optimization is active")
+ Log.i(LOG_ID, "Battery optimization is inactive")
}
} catch (exc: Exception) {
Log.e(LOG_ID, "checkBatteryOptimization exception: " + exc.message.toString() )
@@ -265,6 +277,8 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
inflater.inflate(R.menu.menu_items, menu)
MenuCompat.setGroupDividerEnabled(menu!!, true)
optionsMenu = menu
+ floatingWidgetItem = optionsMenu.findItem(R.id.action_floating_widget_toggle)
+ updateMenuItems()
return true
} catch (exc: Exception) {
Log.e(LOG_ID, "onCreateOptionsMenu exception: " + exc.message.toString() )
@@ -272,6 +286,11 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
return true
}
+ private fun updateMenuItems() {
+ if(floatingWidgetItem!=null)
+ floatingWidgetItem!!.isVisible = Settings.canDrawOverlays(this)
+ }
+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
try {
Log.v(LOG_ID, "onOptionsItemSelected for " + item.itemId.toString())
@@ -308,7 +327,10 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
val mailIntent = Intent(Intent.ACTION_SENDTO, Uri.fromParts(
"mailto","GlucoDataHandler@michel-inside.de", null))
mailIntent.putExtra(Intent.EXTRA_SUBJECT, "GlucoDataHander v" + BuildConfig.VERSION_NAME)
- startActivity(mailIntent)
+ mailIntent.putExtra(Intent.EXTRA_TEXT, "")
+ if (mailIntent.resolveActivity(packageManager) != null) {
+ startActivity(mailIntent)
+ }
return true
}
R.id.action_save_mobile_logs -> {
@@ -324,6 +346,13 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
val menuIt: MenuItem = optionsMenu.findItem(R.id.action_save_wear_logs)
menuIt.isEnabled = WearPhoneConnection.nodesConnected && !LogcatReceiver.isActive
}
+ R.id.action_floating_widget_toggle -> {
+ Log.v(LOG_ID, "Floating widget toggle")
+ with(sharedPref.edit()) {
+ putBoolean(Constants.SHARED_PREF_FLOATING_WIDGET, !sharedPref.getBoolean(Constants.SHARED_PREF_FLOATING_WIDGET, false))
+ apply()
+ }
+ }
else -> return super.onOptionsItemSelected(item)
}
} catch (exc: Exception) {
@@ -363,6 +392,8 @@ class MainActivity : AppCompatActivity(), NotifierInterface {
}
txtSourceInfo.text = SourceStateData.getState(this)
+
+ updateMenuItems()
} catch (exc: Exception) {
Log.e(LOG_ID, "update exception: " + exc.message.toString() )
}
diff --git a/mobile/src/main/java/de/michelinside/glucodatahandler/PermanentNotification.kt b/mobile/src/main/java/de/michelinside/glucodatahandler/PermanentNotification.kt
index 2c0f8266..4d717ef5 100644
--- a/mobile/src/main/java/de/michelinside/glucodatahandler/PermanentNotification.kt
+++ b/mobile/src/main/java/de/michelinside/glucodatahandler/PermanentNotification.kt
@@ -76,6 +76,9 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
private fun createNofitication(context: Context) {
createNotificationChannel(context)
+ Channels.getNotificationManager().cancel(GlucoDataService.NOTIFICATION_ID)
+ Channels.getNotificationManager().cancel(SECOND_NOTIFICATION_ID)
+
notificationCompat = Notification.Builder(context, ChannelType.MOBILE_SECOND.channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(Utils.getAppIntent(context, MainActivity::class.java, 5, false))
@@ -84,6 +87,7 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
.setAutoCancel(false)
.setShowWhen(true)
.setColorized(true)
+ .setGroup(ChannelType.MOBILE_SECOND.channelId)
.setCategory(Notification.CATEGORY_STATUS)
.setVisibility(Notification.VISIBILITY_PUBLIC)
@@ -95,6 +99,7 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
.setAutoCancel(false)
.setShowWhen(true)
.setColorized(true)
+ .setGroup(ChannelType.MOBILE_FOREGROUND.channelId)
.setCategory(Notification.CATEGORY_STATUS)
.setVisibility(Notification.VISIBILITY_PUBLIC)
}
@@ -108,10 +113,11 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
private fun getStatusBarIcon(iconKey: String): Icon {
val bigIcon = sharedPref.getBoolean(Constants.SHARED_PREF_PERMANENT_NOTIFICATION_USE_BIG_ICON, false)
+ val coloredIcon = sharedPref.getBoolean(Constants.SHARED_PREF_PERMANENT_NOTIFICATION_COLORED_ICON, true)
return when(sharedPref.getString(iconKey, StatusBarIcon.APP.pref)) {
- StatusBarIcon.GLUCOSE.pref -> BitmapUtils.getGlucoseAsIcon(roundTarget=!bigIcon)
- StatusBarIcon.TREND.pref -> BitmapUtils.getRateAsIcon(roundTarget=true, resizeFactor = if (bigIcon) 1.5F else 1F)
- StatusBarIcon.DELTA.pref -> BitmapUtils.getDeltaAsIcon(roundTarget=!bigIcon)
+ StatusBarIcon.GLUCOSE.pref -> BitmapUtils.getGlucoseAsIcon(roundTarget=!bigIcon, color = if(coloredIcon) ReceiveData.getClucoseColor() else Color.WHITE)
+ StatusBarIcon.TREND.pref -> BitmapUtils.getRateAsIcon(roundTarget=true, color = if(coloredIcon) ReceiveData.getClucoseColor() else Color.WHITE, resizeFactor = if (bigIcon) 1.5F else 1F)
+ StatusBarIcon.DELTA.pref -> BitmapUtils.getDeltaAsIcon(roundTarget=!bigIcon, color = if(coloredIcon) ReceiveData.getClucoseColor(true) else Color.WHITE)
else -> Icon.createWithResource(GlucoDataService.context, R.mipmap.ic_launcher)
}
}
@@ -144,7 +150,7 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
}
val notificationBuilder = if(foreground) foregroundNotificationCompat else notificationCompat
- val notification = notificationBuilder
+ notificationBuilder
.setSmallIcon(getStatusBarIcon(iconKey))
.setWhen(ReceiveData.time)
.setCustomContentView(remoteViews)
@@ -153,9 +159,17 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
.setStyle(Notification.DecoratedCustomViewStyle())
.setContentTitle(if (withContent) ReceiveData.getClucoseAsString() else "")
.setContentText(if (withContent) "Delta: " + ReceiveData.getDeltaAsString() else "")
- .build()
- notification.visibility
+ when(sharedPref.getString(iconKey, StatusBarIcon.APP.pref)) {
+ StatusBarIcon.GLUCOSE.pref,
+ StatusBarIcon.TREND.pref -> {
+ notificationBuilder.setColor(Color.TRANSPARENT)
+ }
+ }
+
+ val notification = notificationBuilder.build()
+
+ notification.visibility = Notification.VISIBILITY_PUBLIC
notification.flags = notification.flags or Notification.FLAG_NO_CLEAR
return notification
}
@@ -257,6 +271,7 @@ object PermanentNotification: NotifierInterface, SharedPreferences.OnSharedPrefe
Constants.SHARED_PREF_SECOND_PERMANENT_NOTIFICATION,
Constants.SHARED_PREF_SECOND_PERMANENT_NOTIFICATION_ICON,
Constants.SHARED_PREF_PERMANENT_NOTIFICATION_USE_BIG_ICON,
+ Constants.SHARED_PREF_PERMANENT_NOTIFICATION_COLORED_ICON,
Constants.SHARED_PREF_PERMANENT_NOTIFICATION_EMPTY -> {
updatePreferences()
}
diff --git a/mobile/src/main/java/de/michelinside/glucodatahandler/preferences/SourceFragment.kt b/mobile/src/main/java/de/michelinside/glucodatahandler/preferences/SourceFragment.kt
index b09fe855..0f1acd7d 100644
--- a/mobile/src/main/java/de/michelinside/glucodatahandler/preferences/SourceFragment.kt
+++ b/mobile/src/main/java/de/michelinside/glucodatahandler/preferences/SourceFragment.kt
@@ -1,5 +1,6 @@
package de.michelinside.glucodatahandler.preferences
+import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import android.text.InputType
@@ -8,11 +9,13 @@ import androidx.preference.*
import de.michelinside.glucodatahandler.R
import de.michelinside.glucodatahandler.common.Constants
import de.michelinside.glucodatahandler.common.notifier.InternalNotifier
+import de.michelinside.glucodatahandler.common.notifier.NotifierInterface
import de.michelinside.glucodatahandler.common.notifier.NotifySource
import de.michelinside.glucodatahandler.common.tasks.DataSourceTask
+import de.michelinside.glucodatahandler.common.tasks.LibreViewSourceTask
-class SourceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener {
+class SourceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener, NotifierInterface {
private val LOG_ID = "GDH.SourceFragment"
private var settingsChanged = false
@@ -33,6 +36,7 @@ class SourceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPre
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
}
+ setupLibrePatientData()
} catch (exc: Exception) {
Log.e(LOG_ID, "onCreatePreferences exception: " + exc.toString())
}
@@ -56,6 +60,7 @@ class SourceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPre
try {
preferenceManager.sharedPreferences?.registerOnSharedPreferenceChangeListener(this)
updateEnableStates(preferenceManager.sharedPreferences!!)
+ InternalNotifier.addNotifier(requireContext(), this, mutableSetOf(NotifySource.PATIENT_DATA_CHANGED))
super.onResume()
} catch (exc: Exception) {
Log.e(LOG_ID, "onResume exception: " + exc.toString())
@@ -66,6 +71,7 @@ class SourceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPre
Log.d(LOG_ID, "onPause called")
try {
preferenceManager.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(this)
+ InternalNotifier.remNotifier(requireContext(), this)
super.onPause()
} catch (exc: Exception) {
Log.e(LOG_ID, "onPause exception: " + exc.toString())
@@ -128,4 +134,27 @@ class SourceFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPre
Log.e(LOG_ID, "updateEnableStates exception: " + exc.toString())
}
}
+
+ private fun setupLibrePatientData() {
+ try {
+ val listPreference = findPreference(Constants.SHARED_PREF_LIBRE_PATIENT_ID)
+ // force "global broadcast" to be the first entry
+ listPreference!!.entries = LibreViewSourceTask.patientData.values.toTypedArray()
+ listPreference.entryValues = LibreViewSourceTask.patientData.keys.toTypedArray()
+ listPreference.isVisible = LibreViewSourceTask.patientData.size > 1
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "setupLibrePatientData exception: $exc")
+ }
+ }
+
+ override fun OnNotifyData(context: Context, dataSource: NotifySource, extras: Bundle?) {
+ try {
+ Log.v(LOG_ID, "OnNotifyData called for source $dataSource")
+ if (dataSource == NotifySource.PATIENT_DATA_CHANGED)
+ setupLibrePatientData()
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "OnNotifyData exception for source $dataSource: $exc")
+ }
+ }
+
}
\ No newline at end of file
diff --git a/mobile/src/main/java/de/michelinside/glucodatahandler/widget/LockScreenWallpaper.kt b/mobile/src/main/java/de/michelinside/glucodatahandler/widget/LockScreenWallpaper.kt
new file mode 100644
index 00000000..1031494d
--- /dev/null
+++ b/mobile/src/main/java/de/michelinside/glucodatahandler/widget/LockScreenWallpaper.kt
@@ -0,0 +1,153 @@
+package de.michelinside.glucodatahandler.widget
+
+import android.app.WallpaperManager
+import android.content.Context
+import android.content.SharedPreferences
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.drawable.BitmapDrawable
+import android.os.Bundle
+import android.util.Log
+import de.michelinside.glucodatahandler.common.Constants
+import de.michelinside.glucodatahandler.common.GlucoDataService
+import de.michelinside.glucodatahandler.common.notifier.InternalNotifier
+import de.michelinside.glucodatahandler.common.notifier.NotifierInterface
+import de.michelinside.glucodatahandler.common.notifier.NotifySource
+import de.michelinside.glucodatahandler.common.utils.BitmapUtils
+import kotlin.math.max
+
+
+object LockScreenWallpaper : NotifierInterface, SharedPreferences.OnSharedPreferenceChangeListener {
+ private val LOG_ID = "GDH.LockScreenWallpaper"
+ private var enabled = false
+ private var yPos = 75
+
+
+ fun create(context: Context) {
+ try {
+ Log.d(LOG_ID, "create called")
+ val sharedPref = context.getSharedPreferences(Constants.SHARED_PREF_TAG, Context.MODE_PRIVATE)
+ sharedPref.registerOnSharedPreferenceChangeListener(this)
+ onSharedPreferenceChanged(sharedPref, null)
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "create exception: " + exc.message.toString() )
+ }
+ }
+
+ fun destroy(context: Context) {
+ try {
+ Log.d(LOG_ID, "destroy called")
+ val sharedPref = context.getSharedPreferences(Constants.SHARED_PREF_TAG, Context.MODE_PRIVATE)
+ sharedPref.unregisterOnSharedPreferenceChangeListener(this)
+ disable(context)
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "destroy exception: " + exc.message.toString() )
+ }
+ }
+
+ private fun enable(context: Context) {
+ if (!enabled) {
+ Log.d(LOG_ID, "enable called")
+ enabled = true
+ val filter = mutableSetOf(
+ NotifySource.BROADCAST,
+ NotifySource.MESSAGECLIENT,
+ NotifySource.OBSOLETE_VALUE,
+ NotifySource.SETTINGS
+ )
+ InternalNotifier.addNotifier(context, this, filter)
+ updateLockScreen(context)
+ }
+ }
+
+ private fun disable(context: Context) {
+ if (enabled) {
+ Log.d(LOG_ID, "disable called")
+ enabled = false
+ InternalNotifier.remNotifier(context, this)
+ setWallpaper(null, context)
+ }
+ }
+
+ fun updateLockScreen(context: Context) {
+ try {
+ Log.v(LOG_ID, "updateLockScreen called - enabled=$enabled")
+ if (enabled) {
+ setWallpaper(getBitmapForWallpaper(context), context)
+ }
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "updateLockScreen exception: " + exc.message.toString() )
+ }
+ }
+
+ private fun setWallpaper(bitmap: Bitmap?, context: Context) {
+ try {
+ Log.v(LOG_ID, "updateLockScreen called for bitmap $bitmap")
+ val wallpaperManager = WallpaperManager.getInstance(context)
+ val wallpaper = if(bitmap != null) createWallpaper(bitmap, context) else null
+ wallpaperManager.setBitmap(wallpaper, null, false, WallpaperManager.FLAG_LOCK)
+ //wallpaper?.recycle()
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "updateLockScreen exception: " + exc.message.toString() )
+ }
+
+ }
+
+ private fun createWallpaper(bitmap: Bitmap, context: Context): Bitmap? {
+ try {
+ Log.v(LOG_ID, "creatWallpaper called")
+ val screenWidth = BitmapUtils.getScreenWidth()
+ val screenHeigth = BitmapUtils.getScreenHeight()
+ val screenDPI = BitmapUtils.getScreenDpi().toFloat()
+ val wallpaper = Bitmap.createBitmap(screenWidth, screenHeigth, Bitmap.Config.ARGB_8888)
+ val canvas = Canvas(wallpaper)
+ val drawable = BitmapDrawable(context.resources, bitmap)
+ drawable.setBounds(0, 0, screenWidth, screenHeigth)
+ val xOffset = ((screenWidth-bitmap.width)/2F) //*1.2F-(screenDPI*0.3F)
+ val yOffset = max(0F, ((screenHeigth-bitmap.height)*yPos/100F)) //-(screenDPI*0.3F))
+ Log.d(LOG_ID, "Create wallpaper at x=$xOffset/$screenWidth and y=$yOffset/$screenHeigth DPI=$screenDPI")
+ canvas.drawBitmap(bitmap, xOffset, yOffset, Paint(Paint.ANTI_ALIAS_FLAG))
+ bitmap.recycle()
+ return wallpaper
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "updateLockScreen exception: " + exc.message.toString() )
+ }
+ return null
+ }
+
+ private fun getBitmapForWallpaper(context: Context): Bitmap? {
+ return BitmapUtils.getGlucoseTrendBitmap(width = 400, height = 400)
+ }
+
+ override fun OnNotifyData(context: Context, dataSource: NotifySource, extras: Bundle?) {
+ try {
+ Log.v(LOG_ID, "OnNotifyData called for source $dataSource")
+ updateLockScreen(context)
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "OnNotifyData exception: " + exc.message.toString() )
+ }
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
+ try {
+ Log.v(LOG_ID, "onSharedPreferenceChanged called for key $key")
+ var changed = false
+ if (yPos != sharedPreferences.getInt(Constants.SHARED_PREF_LOCKSCREEN_WP_Y_POS, 75)) {
+ yPos = sharedPreferences.getInt(Constants.SHARED_PREF_LOCKSCREEN_WP_Y_POS, 75)
+ Log.d(LOG_ID, "New Y pos: $yPos")
+ changed = true
+ }
+ if (enabled != sharedPreferences.getBoolean(Constants.SHARED_PREF_LOCKSCREEN_WP_ENABLED, false)) {
+ if (sharedPreferences.getBoolean(Constants.SHARED_PREF_LOCKSCREEN_WP_ENABLED, false))
+ enable(GlucoDataService.context!!)
+ else
+ disable(GlucoDataService.context!!)
+ } else if (changed) {
+ updateLockScreen(GlucoDataService.context!!)
+ }
+ } catch (exc: Exception) {
+ Log.e(LOG_ID, "onSharedPreferenceChanged exception: " + exc.message.toString() )
+ }
+ }
+}
\ No newline at end of file
diff --git a/mobile/src/main/res/layout/activity_main.xml b/mobile/src/main/res/layout/activity_main.xml
index 3dd63c46..e64a4490 100644
--- a/mobile/src/main/res/layout/activity_main.xml
+++ b/mobile/src/main/res/layout/activity_main.xml
@@ -86,13 +86,26 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UselessParent">
+
@@ -22,7 +22,8 @@
android:layout_height="match_parent"
android:layout_marginStart="6dp"
app:srcCompat="@drawable/icon_question"
- android:contentDescription="@string/info_label_rate" />
+ android:contentDescription="@string/info_label_rate"
+ android:translationZ="2dp" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mobile/src/main/res/menu/menu_items.xml b/mobile/src/main/res/menu/menu_items.xml
index 9f014844..a4a6c322 100644
--- a/mobile/src/main/res/menu/menu_items.xml
+++ b/mobile/src/main/res/menu/menu_items.xml
@@ -1,6 +1,12 @@