Skip to content

Commit 27e59c2

Browse files
author
LeanBitLab
committed
chore: release v1.8 (targetSdk 35, widget padding, dynamic color fixes, step counter refactoring)
1 parent bec4b68 commit 27e59c2

40 files changed

Lines changed: 1164 additions & 288 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,6 @@ Thumbs.db
3232

3333
# Private Docs
3434
pdocs/
35+
36+
# CLI Context
37+
GEMINI.md

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- **Configurable**: Adjust text sizes and visibility for all elements.
2929
- **Time & Date**: Clear, customizable display.
3030
- **Battery & Temperature**: Real-time device status.
31+
- **Step Counter**: Track your daily steps directly from the widget.
3132
- **Calendar Events**: Upcoming agenda at a glance.
3233
- **Task Integration**: Seamless integration with [Tasks.org](https://tasks.org/).
3334
- **World Clock**: Track time in another zone.
@@ -58,6 +59,7 @@ Lwidget is licensed under **GNU General Public License v3.0**.
5859

5960
See [LICENSE](LICENSE) file.
6061

62+
6163
## Credits
6264

6365
- Built with ❤️ by [LeanBitLab](https://github.com/LeanBitLab)

app/build.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ android {
1313
defaultConfig {
1414
applicationId = "com.leanbitlab.lwidget"
1515
minSdk = 26
16-
targetSdk = 34
17-
versionCode = 8
18-
versionName = "1.7"
16+
targetSdk = 35
17+
versionCode = 10
18+
versionName = "1.8"
1919
}
2020

2121
signingConfigs {
@@ -57,4 +57,5 @@ dependencies {
5757
implementation("androidx.core:core-ktx:1.12.0")
5858
implementation("androidx.appcompat:appcompat:1.6.1")
5959
implementation("com.google.android.material:material:1.11.0")
60+
implementation("androidx.work:work-runtime-ktx:2.9.0")
6061
}

app/src/main/AndroidManifest.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
88
<uses-permission android:name="org.tasks.permission.READ_TASKS" />
99
<uses-permission android:name="com.todoroo.astrid.READ" />
10+
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
11+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
12+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH" />
13+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1014

1115
<!-- Required for Android 11+ (API 30) to see these packages -->
1216
<queries>
@@ -58,12 +62,25 @@
5862
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
5963
<action android:name="android.intent.action.BOOT_COMPLETED" />
6064
<action android:name="com.leanbitlab.lwidget.ACTION_BATTERY_UPDATE" />
65+
<action android:name="com.leanbitlab.lwidget.ACTION_STEP_UPDATE" />
66+
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
67+
</intent-filter>
68+
<intent-filter>
69+
<action android:name="android.intent.action.PROVIDER_CHANGED" />
70+
<data android:scheme="content" android:host="com.android.calendar" />
71+
<data android:scheme="content" android:host="org.tasks" />
6172
</intent-filter>
6273
<meta-data
6374
android:name="android.appwidget.provider"
6475
android:resource="@xml/awidget_info" />
6576
</receiver>
6677

78+
<service
79+
android:name=".StepCounterService"
80+
android:enabled="true"
81+
android:exported="false"
82+
android:foregroundServiceType="health" />
83+
6784
</application>
6885

6986
</manifest>

app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt

Lines changed: 333 additions & 81 deletions
Large diffs are not rendered by default.

app/src/main/java/com/leanbitlab/lwidget/MainActivity.kt

Lines changed: 240 additions & 41 deletions
Large diffs are not rendered by default.
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package com.leanbitlab.lwidget
2+
3+
import android.app.Notification
4+
import android.app.NotificationChannel
5+
import android.app.NotificationManager
6+
import android.app.PendingIntent
7+
import android.app.Service
8+
import android.content.Context
9+
import android.content.Intent
10+
import android.hardware.Sensor
11+
import android.hardware.SensorEvent
12+
import android.hardware.SensorEventListener
13+
import android.hardware.SensorManager
14+
import android.os.Build
15+
import android.os.IBinder
16+
import androidx.core.app.NotificationCompat
17+
import androidx.core.content.ContextCompat
18+
19+
class StepCounterService : Service(), SensorEventListener {
20+
21+
private lateinit var sensorManager: SensorManager
22+
private var stepSensor: Sensor? = null
23+
24+
companion object {
25+
const val CHANNEL_ID = "StepCounterChannel"
26+
const val NOTIFICATION_ID = 42100
27+
const val ACTION_STEP_UPDATE = "com.leanbitlab.lwidget.ACTION_STEP_UPDATE"
28+
}
29+
30+
override fun onCreate() {
31+
super.onCreate()
32+
createNotificationChannel()
33+
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACTIVITY_RECOGNITION) != android.content.pm.PackageManager.PERMISSION_GRANTED) {
34+
stopSelf()
35+
return
36+
}
37+
38+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
39+
startForeground(NOTIFICATION_ID, createNotification(), android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH)
40+
} else {
41+
startForeground(NOTIFICATION_ID, createNotification())
42+
}
43+
44+
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
45+
stepSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
46+
47+
stepSensor?.let {
48+
sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_NORMAL)
49+
}
50+
}
51+
52+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
53+
// Sticky so it restarts if killed
54+
return START_STICKY
55+
}
56+
57+
override fun onBind(intent: Intent?): IBinder? {
58+
return null
59+
}
60+
61+
override fun onDestroy() {
62+
sensorManager.unregisterListener(this)
63+
super.onDestroy()
64+
}
65+
66+
override fun onSensorChanged(event: SensorEvent?) {
67+
if (event == null) return
68+
69+
val totalSteps = event.values[0]
70+
val prefs = getSharedPreferences("com.leanbitlab.lwidget.PREFS", Context.MODE_PRIVATE)
71+
val lastTotalSteps = prefs.getFloat("last_total_steps", 0f)
72+
var baselineSteps = prefs.getFloat("step_baseline", 0f)
73+
74+
// Hardware rebooted and reset the total steps to 0
75+
if (totalSteps < lastTotalSteps) {
76+
baselineSteps = totalSteps - (lastTotalSteps - baselineSteps)
77+
prefs.edit().putFloat("step_baseline", baselineSteps).apply()
78+
}
79+
80+
prefs.edit().putFloat("last_total_steps", totalSteps).apply()
81+
82+
// Daily reset logic
83+
val today = java.time.LocalDate.now().toString()
84+
val savedDate = prefs.getString("step_date", "")
85+
86+
if (savedDate != today) {
87+
prefs.edit()
88+
.putString("step_date", today)
89+
.putFloat("step_baseline", totalSteps)
90+
.apply()
91+
}
92+
93+
// Notify widget provider to update
94+
val updateIntent = Intent(this, AwidgetProvider::class.java).apply {
95+
action = ACTION_STEP_UPDATE
96+
}
97+
sendBroadcast(updateIntent)
98+
}
99+
100+
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
101+
// Not used
102+
}
103+
104+
private fun createNotification(): Notification {
105+
val settingsIntent = Intent(this, MainActivity::class.java)
106+
val pendingIntent = PendingIntent.getActivity(this, 0, settingsIntent, PendingIntent.FLAG_IMMUTABLE)
107+
108+
val builder = NotificationCompat.Builder(this, CHANNEL_ID)
109+
.setContentTitle("Lwidget Step Counter")
110+
.setContentText("Listening for steps in the background")
111+
.setSmallIcon(R.drawable.ic_steps)
112+
.setContentIntent(pendingIntent)
113+
.setOngoing(true)
114+
115+
// Use a less intrusive priority
116+
builder.setPriority(NotificationCompat.PRIORITY_MIN)
117+
118+
return builder.build()
119+
}
120+
121+
private fun createNotificationChannel() {
122+
val channel = NotificationChannel(
123+
CHANNEL_ID,
124+
"Step Counter Service",
125+
NotificationManager.IMPORTANCE_MIN
126+
).apply {
127+
description = "Keeps the step counter running in the background."
128+
setShowBadge(false)
129+
}
130+
val manager = getSystemService(NotificationManager::class.java)
131+
manager.createNotificationChannel(channel)
132+
}
133+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.leanbitlab.lwidget
2+
3+
import android.appwidget.AppWidgetManager
4+
import android.content.ComponentName
5+
import android.content.Context
6+
import androidx.work.CoroutineWorker
7+
import androidx.work.WorkerParameters
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.withContext
10+
11+
class WidgetUpdateWorker(
12+
private val context: Context,
13+
workerParams: WorkerParameters
14+
) : CoroutineWorker(context, workerParams) {
15+
16+
override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
17+
return@withContext try {
18+
val appWidgetManager = AppWidgetManager.getInstance(context)
19+
val thisAppWidget = ComponentName(context, AwidgetProvider::class.java)
20+
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
21+
22+
// Perform the periodic TICK update (Battery, Temp, Data, Storage)
23+
for (appWidgetId in appWidgetIds) {
24+
AwidgetProvider.updateAppWidget(context, appWidgetManager, appWidgetId, UpdateMode.TICK)
25+
}
26+
Result.success()
27+
} catch (e: Exception) {
28+
e.printStackTrace()
29+
Result.retry()
30+
}
31+
}
32+
}

app/src/main/res/color/glow_05.xml

Lines changed: 0 additions & 4 deletions
This file was deleted.

app/src/main/res/color/glow_30.xml

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)