@@ -146,9 +146,19 @@ class AwidgetProvider : AppWidgetProvider() {
146146 val showData = prefs.getBoolean(" show_data_usage" , false )
147147 val sizeData = prefs.getFloat(" size_data" , 14f )
148148
149+ val showWorldClock = prefs.getBoolean(" show_world_clock" , false )
150+ val sizeWorldClock = prefs.getFloat(" size_world_clock" , 18f )
151+ val worldClockZoneStr = prefs.getString(" world_clock_zone_str" , " UTC" ) ? : " UTC"
152+
153+ val showStorage = prefs.getBoolean(" show_storage" , false )
154+ val sizeStorage = prefs.getFloat(" size_storage" , 14f )
155+
149156 val showTasks = prefs.getBoolean(" show_tasks" , false )
150157 val sizeTasks = prefs.getFloat(" size_tasks" , 14f )
151158
159+ val showNextAlarm = prefs.getBoolean(" show_next_alarm" , true )
160+ val sizeNextAlarm = prefs.getFloat(" size_next_alarm" , 14f )
161+
152162 val fontStyle = prefs.getInt(" font_style" , 0 )
153163
154164 // --- Theme & Font Setup ---
@@ -243,8 +253,15 @@ class AwidgetProvider : AppWidgetProvider() {
243253 else -> " h:mm" to " H:mm"
244254 }
245255 views.setCharSequence(R .id.clock_time, " setFormat12Hour" , timeFormat12)
256+ views.setCharSequence(R .id.clock_time, " setFormat12Hour" , timeFormat12)
246257 views.setCharSequence(R .id.clock_time, " setFormat24Hour" , timeFormat24)
247258
259+ // --- World Clock ---
260+ views.setViewVisibility(R .id.text_world_clock, if (showWorldClock) android.view.View .VISIBLE else android.view.View .GONE )
261+ if (showWorldClock) {
262+ loadWorldClock(context, views, sizeWorldClock, secondaryColor, worldClockZoneStr, timeFormat12.contains(" a" ))
263+ }
264+
248265 // --- Apply Date ---
249266 views.setViewVisibility(R .id.clock_date, if (showDate) android.view.View .VISIBLE else android.view.View .GONE )
250267 views.setTextViewTextSize(R .id.clock_date, android.util.TypedValue .COMPLEX_UNIT_SP , sizeDate)
@@ -289,6 +306,14 @@ class AwidgetProvider : AppWidgetProvider() {
289306 views.setTextColor(R .id.text_data_usage, secondaryColor)
290307 updateDataUsage(context, views)
291308 }
309+
310+ // --- Storage ---
311+ views.setViewVisibility(R .id.text_storage, if (showStorage) android.view.View .VISIBLE else android.view.View .GONE )
312+ if (showStorage) {
313+ views.setTextViewTextSize(R .id.text_storage, android.util.TypedValue .COMPLEX_UNIT_SP , sizeStorage)
314+ views.setTextColor(R .id.text_storage, secondaryColor)
315+ updateStorageStats(context, views)
316+ }
292317
293318 // --- Click Actions ---
294319 val clockPackages = listOf (" com.android.deskclock" , " com.google.android.deskclock" , " com.simplemobiletools.clock" , " org.fossify.clock" )
@@ -309,6 +334,10 @@ class AwidgetProvider : AppWidgetProvider() {
309334 val batteryPendingIntent = PendingIntent .getActivity(context, 2 , batteryIntent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE )
310335 views.setOnClickPendingIntent(R .id.text_battery, batteryPendingIntent)
311336 views.setOnClickPendingIntent(R .id.text_temp, batteryPendingIntent)
337+
338+ val storageIntent = Intent (android.provider.Settings .ACTION_INTERNAL_STORAGE_SETTINGS )
339+ val storagePendingIntent = PendingIntent .getActivity(context, 3 , storageIntent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE )
340+ views.setOnClickPendingIntent(R .id.text_storage, storagePendingIntent)
312341
313342 // --- Calendar Events OR Tasks ---
314343 views.setViewVisibility(R .id.events_container, if (showEvents || showTasks) android.view.View .VISIBLE else android.view.View .GONE )
@@ -319,11 +348,30 @@ class AwidgetProvider : AppWidgetProvider() {
319348 loadTasks(context, views, sizeTasks, primaryColor, secondaryColor)
320349 }
321350
351+ // --- Next Alarm ---
352+ views.setViewVisibility(R .id.text_next_alarm, if (showNextAlarm) android.view.View .VISIBLE else android.view.View .GONE )
353+ if (showNextAlarm) {
354+ loadNextAlarm(context, views, sizeNextAlarm, secondaryColor)
355+ }
356+ // Click action for Next Alarm (same as Clock)
357+ views.setOnClickPendingIntent(R .id.text_next_alarm, alarmPendingIntent)
358+
322359 val refreshIntent = Intent (context, AwidgetProvider ::class .java).apply {
323360 action = ACTION_BATTERY_UPDATE
324361 }
325362 val refreshPendingIntent = PendingIntent .getBroadcast(context, 10 , refreshIntent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE )
326- views.setOnClickPendingIntent(R .id.events_container, refreshPendingIntent)
363+
364+ if (showTasks) {
365+ val tasksIntent = context.packageManager.getLaunchIntentForPackage(" org.tasks" )
366+ if (tasksIntent != null ) {
367+ val tasksPendingIntent = PendingIntent .getActivity(context, 11 , tasksIntent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE )
368+ views.setOnClickPendingIntent(R .id.events_container, tasksPendingIntent)
369+ } else {
370+ views.setOnClickPendingIntent(R .id.events_container, refreshPendingIntent)
371+ }
372+ } else {
373+ views.setOnClickPendingIntent(R .id.events_container, refreshPendingIntent)
374+ }
327375
328376 val settingsIntent = Intent (context, MainActivity ::class .java)
329377 val settingsPendingIntent = PendingIntent .getActivity(context, 0 , settingsIntent, PendingIntent .FLAG_UPDATE_CURRENT or PendingIntent .FLAG_IMMUTABLE )
@@ -494,13 +542,35 @@ class AwidgetProvider : AppWidgetProvider() {
494542 R .id.text_event_10
495543 )
496544
545+ // Debugging: Check permission again contextually
546+ val hasPerm = context.checkSelfPermission(" org.tasks.permission.READ_TASKS" ) == android.content.pm.PackageManager .PERMISSION_GRANTED ||
547+ context.checkSelfPermission(" com.todoroo.astrid.READ" ) == android.content.pm.PackageManager .PERMISSION_GRANTED
548+
549+ if (! hasPerm) {
550+ views.setTextViewText(eventViews[0 ], " Missing Permission" )
551+ views.setViewVisibility(eventViews[0 ], android.view.View .VISIBLE )
552+ return
553+ }
554+
497555 val taskUri = android.net.Uri .parse(" content://org.tasks/tasks" )
556+ // Try simpler selection or none to test
498557 val selection = " completed = 0"
499558
500559 try {
501560 context.contentResolver.query(taskUri, null , selection, null , " due ASC" )?.use { cursor ->
502561 val titleIdx = cursor.getColumnIndex(" title" )
503562
563+ if (cursor.count == 0 ) {
564+ // views.setTextViewText(eventViews[0], "No active tasks found")
565+ // views.setViewVisibility(eventViews[0], android.view.View.VISIBLE)
566+ // views.setTextColor(eventViews[0], secondaryColor)
567+ // Just hide all
568+ for (viewId in eventViews) {
569+ views.setViewVisibility(viewId, android.view.View .GONE )
570+ }
571+ return
572+ }
573+
504574 var i = 0
505575 while (cursor.moveToNext() && i < eventViews.size) {
506576 if (titleIdx != - 1 ) {
@@ -525,13 +595,76 @@ class AwidgetProvider : AppWidgetProvider() {
525595 return
526596 }
527597 } catch (e: Exception ) {
598+ // Fail silently or log
599+ for (viewId in eventViews) {
600+ views.setViewVisibility(viewId, android.view.View .GONE )
601+ }
528602 }
529603
530- for (viewId in eventViews) {
531- views.setViewVisibility(viewId, android.view.View .GONE )
604+ // If we reached here (query null?), show generic message
605+ // views.setTextViewText(eventViews[0], "Query Failed")
606+ // views.setViewVisibility(eventViews[0], android.view.View.VISIBLE)
607+ }
608+
609+ private fun loadWorldClock (context : Context , views : RemoteViews , textSizeSp : Float , textColor : Int , zoneIdStr : String , is12Hour : Boolean ) {
610+ try {
611+ val zoneId = ZoneId .of(zoneIdStr)
612+ val zdt = java.time.ZonedDateTime .now(zoneId)
613+ val pattern = if (is12Hour) " h:mm a" else " H:mm"
614+ val formatter = DateTimeFormatter .ofPattern(pattern, Locale .getDefault())
615+ val timeStr = zdt.format(formatter)
616+
617+
618+
619+ // If label is too long, maybe truncate? For now, let it be.
620+ // Format: "10:30 AM" (Time only, subtle)
621+ views.setTextViewText(R .id.text_world_clock, timeStr)
622+ views.setTextViewTextSize(R .id.text_world_clock, android.util.TypedValue .COMPLEX_UNIT_SP , textSizeSp)
623+ views.setTextColor(R .id.text_world_clock, textColor)
624+ views.setViewVisibility(R .id.text_world_clock, android.view.View .VISIBLE )
625+
626+ } catch (e: Exception ) {
627+ views.setViewVisibility(R .id.text_world_clock, android.view.View .GONE )
628+ }
629+ }
630+
631+ private fun loadNextAlarm (context : Context , views : RemoteViews , textSizeSp : Float , textColor : Int ) {
632+ val alarmManager = context.getSystemService(Context .ALARM_SERVICE ) as AlarmManager
633+ val nextAlarm = alarmManager.nextAlarmClock
634+
635+ if (nextAlarm != null ) {
636+ val nextAlarmTime = LocalDateTime .ofInstant(Instant .ofEpochMilli(nextAlarm.triggerTime), ZoneId .systemDefault())
637+ val timeFormatter = DateTimeFormatter .ofPattern(" h:mm a" , Locale .getDefault())
638+ val timeText = nextAlarmTime.format(timeFormatter)
639+
640+ // Format: "| ⏰ 7:00 AM"
641+ val fullText = " | ⏰ $timeText "
642+ views.setTextViewText(R .id.text_next_alarm, fullText)
643+ views.setTextViewTextSize(R .id.text_next_alarm, android.util.TypedValue .COMPLEX_UNIT_SP , textSizeSp)
644+ views.setTextColor(R .id.text_next_alarm, textColor)
645+ views.setViewVisibility(R .id.text_next_alarm, android.view.View .VISIBLE )
646+ } else {
647+ views.setViewVisibility(R .id.text_next_alarm, android.view.View .GONE )
532648 }
533649 }
534650
651+ private fun updateStorageStats (context : Context , views : RemoteViews ) {
652+ try {
653+ val path = android.os.Environment .getDataDirectory()
654+ val stat = android.os.StatFs (path.path)
655+ val freeBytes = stat.availableBlocksLong * stat.blockSizeLong
656+
657+ val gb = freeBytes / (1024f * 1024f * 1024f )
658+
659+ // Concisely: "12GB"
660+ val text = String .format(" %.0fGB" , gb)
661+
662+ views.setTextViewText(R .id.text_storage, text)
663+ } catch (e: Exception ) {
664+ views.setTextViewText(R .id.text_storage, " Err" )
665+ }
666+ }
667+
535668 private fun getBestIntent (context : Context , packages : List <String >, fallback : Intent ): Intent {
536669 val pm = context.packageManager
537670 for (pkg in packages) {
0 commit comments