From fccf8d60f0c3e4890bd0ce8075081150b1ab6c16 Mon Sep 17 00:00:00 2001 From: LeanBitLab Date: Sat, 2 May 2026 21:04:31 +0530 Subject: [PATCH 01/13] workflow: remove cron schedule from fleet analyze and merge --- .github/workflows/fleet-analyze.yml | 2 -- .github/workflows/fleet-merge.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/fleet-analyze.yml b/.github/workflows/fleet-analyze.yml index 73efc52..32ba997 100644 --- a/.github/workflows/fleet-analyze.yml +++ b/.github/workflows/fleet-analyze.yml @@ -4,8 +4,6 @@ name: Fleet Analyze on: - schedule: - - cron: '0 */6 * * *' workflow_dispatch: inputs: goal: diff --git a/.github/workflows/fleet-merge.yml b/.github/workflows/fleet-merge.yml index 87ac367..751c9c3 100644 --- a/.github/workflows/fleet-merge.yml +++ b/.github/workflows/fleet-merge.yml @@ -4,8 +4,6 @@ name: Fleet Merge on: - schedule: - - cron: '0 */3 * * *' workflow_dispatch: inputs: mode: From d15645e0fc36a557f9bfac9f21d67f745da5f2b2 Mon Sep 17 00:00:00 2001 From: LeanBitLab Date: Sat, 2 May 2026 21:05:21 +0530 Subject: [PATCH 02/13] chore: remove example goal file --- .fleet/goals/example.md | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 .fleet/goals/example.md diff --git a/.fleet/goals/example.md b/.fleet/goals/example.md deleted file mode 100644 index a473a00..0000000 --- a/.fleet/goals/example.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -milestone: "1" ---- - -# Example Fleet Goal - -Analyze the codebase for potential improvements and create -issues for the engineering team. - -## Tools -- Test Coverage: `npx vitest --coverage --json` - -## Assessment Hints -- Focus on missing error handling in API routes -- Look for hardcoded configuration values - -## Insight Hints -- Report on overall test coverage metrics -- Note any unusually complex functions (cyclomatic complexity) - -## Constraints -- Do NOT propose changes already covered by open issues -- Do NOT propose changes rejected in recently closed issues -- Keep tasks small and isolated — one logical change per issue From 9d818563f218d486be81ae9785b78e3146a19083 Mon Sep 17 00:00:00 2001 From: LeanBitLab Date: Sun, 3 May 2026 00:31:46 +0530 Subject: [PATCH 03/13] feat(widget): improve aesthetic and performance Increase AlarmManager interval to 15m to save battery. Add drop shadows to clock texts for better readability. --- app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt | 4 ++-- app/src/main/res/layout/widget_layout.xml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt b/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt index 9bc7daa..189def0 100644 --- a/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt +++ b/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt @@ -138,8 +138,8 @@ class AwidgetProvider : AppWidgetProvider() { android.app.PendingIntent.FLAG_UPDATE_CURRENT or android.app.PendingIntent.FLAG_IMMUTABLE ) - // 1 minute - val intervalMillis = 1L * 60L * 1000L + // 15 minutes + val intervalMillis = 15L * 60L * 1000L alarmManager.setInexactRepeating( android.app.AlarmManager.RTC, diff --git a/app/src/main/res/layout/widget_layout.xml b/app/src/main/res/layout/widget_layout.xml index 03eb365..b1e36e6 100644 --- a/app/src/main/res/layout/widget_layout.xml +++ b/app/src/main/res/layout/widget_layout.xml @@ -152,6 +152,10 @@ android:format24Hour="EEEE, MMMM dd" android:textSize="18sp" android:textColor="#CCFFFFFF" + android:shadowColor="#80000000" + android:shadowRadius="3" + android:shadowDx="1" + android:shadowDy="1" /> Date: Sun, 3 May 2026 00:41:04 +0530 Subject: [PATCH 04/13] feat(settings): reorganize settings, add manual update interval slider, improve permissions section --- .../com/leanbitlab/lwidget/AwidgetProvider.kt | 5 +- .../com/leanbitlab/lwidget/MainActivity.kt | 64 +- app/src/main/res/layout/activity_main.xml | 1714 +++++++++-------- 3 files changed, 927 insertions(+), 856 deletions(-) diff --git a/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt b/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt index 189def0..5d0ee2a 100644 --- a/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt +++ b/app/src/main/java/com/leanbitlab/lwidget/AwidgetProvider.kt @@ -138,8 +138,9 @@ class AwidgetProvider : AppWidgetProvider() { android.app.PendingIntent.FLAG_UPDATE_CURRENT or android.app.PendingIntent.FLAG_IMMUTABLE ) - // 15 minutes - val intervalMillis = 15L * 60L * 1000L + val prefs = context.getSharedPreferences("com.leanbitlab.lwidget.PREFS", Context.MODE_PRIVATE) + val intervalMinutes = prefs.getFloat("update_interval", 15f) + val intervalMillis = (intervalMinutes * 60f * 1000f).toLong().coerceAtLeast(60000L) // min 1 min alarmManager.setInexactRepeating( android.app.AlarmManager.RTC, diff --git a/app/src/main/java/com/leanbitlab/lwidget/MainActivity.kt b/app/src/main/java/com/leanbitlab/lwidget/MainActivity.kt index 56ba133..50e780e 100644 --- a/app/src/main/java/com/leanbitlab/lwidget/MainActivity.kt +++ b/app/src/main/java/com/leanbitlab/lwidget/MainActivity.kt @@ -108,6 +108,9 @@ class MainActivity : AppCompatActivity() { setupSections() updateLivePreview() + // Advanced Section + bindSlider(R.id.row_update_interval, "Update Interval (m)", "update_interval", 15f, 1f, 60f, "m") + // Setup Changelog val versionName = try { packageManager.getPackageInfo(packageName, 0).versionName @@ -205,37 +208,56 @@ class MainActivity : AppCompatActivity() { var widgetNeedsUpdate = false // Check Calendar - if (prefs.getBoolean("show_events", false) && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) { + val calMissing = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED + if (prefs.getBoolean("show_events", false) && calMissing) { prefs.edit().putBoolean("show_events", false).apply() findViewById(R.id.row_events_toggle).findViewById(R.id.row_switch).isChecked = false findViewById(R.id.row_events_size).visibility = View.GONE widgetNeedsUpdate = true } + findViewById(R.id.perm_row_calendar).visibility = if (calMissing) View.VISIBLE else View.GONE + findViewById(R.id.btn_grant_calendar).setOnClickListener { + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALENDAR), 100) + } // Check Tasks - if (prefs.getBoolean("show_tasks", false) && ContextCompat.checkSelfPermission(this, AwidgetProvider.PERMISSION_READ_TASKS_ORG) != PackageManager.PERMISSION_GRANTED) { + val tasksMissing = ContextCompat.checkSelfPermission(this, AwidgetProvider.PERMISSION_READ_TASKS_ORG) != PackageManager.PERMISSION_GRANTED + if (prefs.getBoolean("show_tasks", false) && tasksMissing) { prefs.edit().putBoolean("show_tasks", false).apply() findViewById(R.id.row_tasks_toggle).findViewById(R.id.row_switch).isChecked = false findViewById(R.id.row_tasks_size).visibility = View.GONE widgetNeedsUpdate = true } + findViewById(R.id.perm_row_tasks).visibility = if (tasksMissing) View.VISIBLE else View.GONE + findViewById(R.id.btn_grant_tasks).setOnClickListener { + ActivityCompat.requestPermissions(this, arrayOf(AwidgetProvider.PERMISSION_READ_TASKS_ORG), 101) + } // Check Steps var stepMissing = false - if (prefs.getBoolean("show_steps", false)) { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission(this, Manifest.permission.ACTIVITY_RECOGNITION) != PackageManager.PERMISSION_GRANTED) { - stepMissing = true - } - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { - stepMissing = true - } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q && ContextCompat.checkSelfPermission(this, Manifest.permission.ACTIVITY_RECOGNITION) != PackageManager.PERMISSION_GRANTED) { + stepMissing = true + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + stepMissing = true } - if (stepMissing) { + if (prefs.getBoolean("show_steps", false) && stepMissing) { prefs.edit().putBoolean("show_steps", false).apply() findViewById(R.id.row_steps_toggle).findViewById(R.id.row_switch).isChecked = false findViewById(R.id.row_steps_size).visibility = View.GONE widgetNeedsUpdate = true } + findViewById(R.id.perm_row_steps).visibility = if (stepMissing) View.VISIBLE else View.GONE + findViewById(R.id.btn_grant_steps).setOnClickListener { + val neededPermissions = mutableListOf() + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { + neededPermissions.add(Manifest.permission.ACTIVITY_RECOGNITION) + } + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) { + neededPermissions.add(Manifest.permission.POST_NOTIFICATIONS) + } + ActivityCompat.requestPermissions(this, neededPermissions.toTypedArray(), 102) + } // Check Screen Time if (prefs.getBoolean("show_screen_time", false) && !hasUsageStatsPermission()) { @@ -252,6 +274,14 @@ class MainActivity : AppCompatActivity() { findViewById(R.id.row_data_size).visibility = View.GONE widgetNeedsUpdate = true } + + val usageMissing = !hasUsageStatsPermission() + findViewById(R.id.perm_row_data).visibility = if (usageMissing) View.VISIBLE else View.GONE + findViewById(R.id.btn_grant_data).setOnClickListener { + try { + startActivity(Intent(android.provider.Settings.ACTION_USAGE_ACCESS_SETTINGS)) + } catch (e: Exception) {} + } // Check Breezy Weather if (prefs.getBoolean("show_weather_condition", false)) { @@ -263,7 +293,13 @@ class MainActivity : AppCompatActivity() { } } - cardPermissionList.visibility = View.GONE + // We removed cardPermissionList.visibility = View.GONE so the card is always visible if there are missing permissions. + // Let's actually hide the card if ALL permissions are granted. + if (!calMissing && !tasksMissing && !stepMissing && !usageMissing) { + cardPermissionList.visibility = View.GONE + } else { + cardPermissionList.visibility = View.VISIBLE + } if (widgetNeedsUpdate) { updateWidget() @@ -1482,7 +1518,7 @@ class MainActivity : AppCompatActivity() { private fun bindSlider( viewId: Int, title: String, prefKey: String, defValue: Float, - minValue: Float, maxValue: Float + minValue: Float, maxValue: Float, suffix: String = "%" ) { val row = findViewById(viewId) val tvTitle = row.findViewById(R.id.row_label) @@ -1495,11 +1531,11 @@ class MainActivity : AppCompatActivity() { slider.valueFrom = minValue slider.valueTo = maxValue slider.value = currentValue.coerceIn(minValue, maxValue) - tvValue.text = "${currentValue.toInt()}%" + tvValue.text = "${currentValue.toInt()}$suffix" slider.addOnChangeListener { _, value, fromUser -> if (fromUser) { - tvValue.text = "${value.toInt()}%" + tvValue.text = "${value.toInt()}$suffix" prefs.edit().putFloat(prefKey, value).apply() updateWidget() } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index dc98ffe..0ac3c3e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -95,397 +95,516 @@ android:layout_marginBottom="16dp" android:clipChildren="false"/> - - - - - - - - - - - - -