Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />


<application
Expand Down Expand Up @@ -77,6 +78,15 @@
android:resource="@xml/mensinator_widget_provider4" />
</receiver>

<receiver
android:name=".widgets.BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

</application>

</manifest>
24 changes: 24 additions & 0 deletions app/src/main/java/com/mensinator/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@ import android.view.WindowManager
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.glance.appwidget.updateAll
import androidx.lifecycle.lifecycleScope
import com.mensinator.app.NotificationChannelConstants.channelDescription
import com.mensinator.app.NotificationChannelConstants.channelId
import com.mensinator.app.NotificationChannelConstants.channelName
import com.mensinator.app.ui.navigation.MensinatorApp
import com.mensinator.app.ui.theme.MensinatorTheme
import com.mensinator.app.widgets.MidnightTrigger
import com.mensinator.app.widgets.WidgetInstances
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import org.koin.androidx.compose.KoinAndroidContext

@Suppress("ConstPropertyName")
Expand All @@ -36,6 +44,7 @@ class MainActivity : AppCompatActivity() {
}
}
createNotificationChannel(this)
updateWidgetsOnAppStart()
}

private fun createNotificationChannel(context: Context) {
Expand All @@ -48,6 +57,21 @@ class MainActivity : AppCompatActivity() {
notificationManager.createNotificationChannel(notificationChannel)
}

private fun updateWidgetsOnAppStart() {
lifecycleScope.launch(Dispatchers.IO) {
try {
// Emit to midnight trigger to force widget data refresh
MidnightTrigger.midnightTrigger.emit(Unit)
// Update all widgets concurrently for better performance
WidgetInstances.map { receiver ->
async { receiver.glanceAppWidget.updateAll(this@MainActivity) }
}.awaitAll()
} catch (e: Exception) {
Log.e("MainActivity", "Failed to update widgets on app start", e)
}
}
}

private fun handleScreenProtection(isScreenProtectionEnabled: Boolean) {
Log.d("screenProtectionUI", "protect screen value $isScreenProtectionEnabled")
// Sets the flags for screen protection if
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/com/mensinator/app/widgets/BootReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.mensinator.app.widgets

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
// Use goAsync() to ensure work completes even if receiver would otherwise be killed
val pendingResult = goAsync()
// Use standalone coroutine scope for this one-time operation
CoroutineScope(SupervisorJob() + Dispatchers.IO).launch {
try {
MidnightWorker.scheduleNextMidnight(context)
} finally {
pendingResult.finish()
}
}
}
}
}
22 changes: 18 additions & 4 deletions app/src/main/java/com/mensinator/app/widgets/WidgetInstances.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.mensinator.app.widgets

import android.appwidget.AppWidgetManager
import android.content.Context
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import org.koin.core.context.GlobalContext.get
Expand All @@ -21,31 +23,43 @@ val WidgetInstances
get().get<WidgetPeriodDaysWithoutLabelWithoutBackgroundReceiver>(),
)

class WidgetPeriodDaysWithLabelWithBackgroundReceiver : GlanceAppWidgetReceiver() {
abstract class BaseWidgetReceiver : GlanceAppWidgetReceiver() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
// Schedule midnight worker - WorkManager's REPLACE policy ensures no duplicates
MidnightWorker.scheduleNextMidnight(context)
}
}

class WidgetPeriodDaysWithLabelWithBackgroundReceiver : BaseWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = BaseWidget(
widgetType = WidgetType.Period,
showLabel = true,
showBackground = true
)
}

class WidgetPeriodDaysWithoutLabelWithBackgroundReceiver : GlanceAppWidgetReceiver() {
class WidgetPeriodDaysWithoutLabelWithBackgroundReceiver : BaseWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = BaseWidget(
widgetType = WidgetType.Period,
showLabel = false,
showBackground = true
)
}

class WidgetPeriodDaysWithLabelWithoutBackgroundReceiver : GlanceAppWidgetReceiver() {
class WidgetPeriodDaysWithLabelWithoutBackgroundReceiver : BaseWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = BaseWidget(
widgetType = WidgetType.Period,
showLabel = true,
showBackground = false
)
}

class WidgetPeriodDaysWithoutLabelWithoutBackgroundReceiver : GlanceAppWidgetReceiver() {
class WidgetPeriodDaysWithoutLabelWithoutBackgroundReceiver : BaseWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = BaseWidget(
widgetType = WidgetType.Period,
showLabel = false,
Expand Down
Empty file modified gradlew
100644 → 100755
Empty file.