Skip to content
Merged
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
41 changes: 37 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</div>

[![Build](https://github.com/alorma/FireAndForget/actions/workflows/main.yml/badge.svg)](https://github.com/alorma/FireAndForget/actions/workflows/main.yml)
[![Maven Central](https://img.shields.io/maven-central/v/com.alorma.fireandforget/core.svg)](https://central.sonatype.com/namespace/com.alorma.fireandforget)
[![Maven Central](https://img.shields.io/maven-central/v/com.github.alorma.fire-and-forget/core.svg)](https://central.sonatype.com/namespace/com.github.alorma.fire-and-forget)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Kotlin](https://img.shields.io/badge/Kotlin-2.3.0-blue.svg?logo=kotlin)

Expand Down Expand Up @@ -148,7 +148,7 @@ For temporary state that doesn't need to persist:
class InMemoryRunner : FireAndForgetRunner() {
private val map = mutableMapOf<String, Boolean>()

override fun isEnabled(fireAndForget: FireAndForget): Boolean {
override fun checkEnabled(fireAndForget: FireAndForget): Boolean {
return map[fireAndForget.name] ?: fireAndForget.defaultValue
}

Expand All @@ -170,7 +170,7 @@ Implement `FireAndForgetRunner` with your preferred storage solution (Room, Data
class DataStoreRunner(
private val dataStore: DataStore<Preferences>
) : FireAndForgetRunner() {
override fun isEnabled(fireAndForget: FireAndForget): Boolean {
override fun checkEnabled(fireAndForget: FireAndForget): Boolean {
// Your DataStore implementation
}

Expand Down Expand Up @@ -213,6 +213,7 @@ abstract class FireAndForget(
val fireAndForgetRunner: FireAndForgetRunner,
val name: String,
val defaultValue: Boolean = true,
val autoDisable: Boolean = false,
)
```

Expand All @@ -221,6 +222,7 @@ abstract class FireAndForget(
- `fireAndForgetRunner`: The runner implementation that handles state persistence
- `name`: Unique identifier for this flag (used as storage key)
- `defaultValue`: Initial state (default: `true` = enabled)
- `autoDisable`: When `true`, automatically disables the flag on first call to `isEnabled()` (default: `false`)

#### Methods

Expand All @@ -232,12 +234,15 @@ abstract class FireAndForget(

```kotlin
abstract class FireAndForgetRunner {
abstract fun isEnabled(fireAndForget: FireAndForget): Boolean
fun isEnabled(fireAndForget: FireAndForget): Boolean
protected abstract fun checkEnabled(fireAndForget: FireAndForget): Boolean
abstract fun disable(fireAndForget: FireAndForget)
abstract fun reset(fireAndForget: FireAndForget)
}
```

**Implementation Note**: When creating a custom runner, you must override `checkEnabled()` instead of `isEnabled()`. The `isEnabled()` method is final and handles the `autoDisable` logic internally, ensuring it cannot be bypassed by runner implementations.

## Usage Examples

### Basic One-Time Execution
Expand Down Expand Up @@ -338,6 +343,34 @@ fun initializeApp() {
}
```

### Auto-Disable Feature

Use `autoDisable = true` to automatically disable the flag on first access without manually calling `disable()`:

```kotlin
class QuickTip(runner: FireAndForgetRunner) : FireAndForget(
fireAndForgetRunner = runner,
name = "quick_tip",
autoDisable = true // Automatically disables after first isEnabled() call
)

fun showScreen() {
val runner = SettingsFireAndForgetRunner(Settings())
val quickTip = QuickTip(runner)

// First call: returns true and automatically disables
if (quickTip.isEnabled()) {
showTooltip("Here's a quick tip!")
// No need to call quickTip.disable()
}

// Subsequent calls: returns false
quickTip.isEnabled() // false
}
```

This is perfect for fire-and-forget operations where you don't have a natural completion callback to call `disable()`. The flag automatically marks itself as executed when accessed for the first time.

## Project Structure

This repository contains:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.alorma.fireandforget

abstract class FireAndForget(
open class FireAndForget(
val fireAndForgetRunner: FireAndForgetRunner,
val name: String,
val defaultValue: Boolean = true,
val autoDisable: Boolean = false,
) {
fun isEnabled() = fireAndForgetRunner.isEnabled(this)
open fun isEnabled() = fireAndForgetRunner.isEnabled(this)

fun disable() = fireAndForgetRunner.disable(this)
open fun disable() = fireAndForgetRunner.disable(this)

fun reset() = fireAndForgetRunner.reset(this)
open fun reset() = fireAndForgetRunner.reset(this)
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package com.alorma.fireandforget

abstract class FireAndForgetRunner {
abstract fun isEnabled(fireAndForget: FireAndForget): Boolean
fun isEnabled(fireAndForget: FireAndForget): Boolean {
val enabled = checkEnabled(fireAndForget)
if (enabled && fireAndForget.autoDisable) {
disable(fireAndForget)
}
return enabled
}

protected abstract fun checkEnabled(fireAndForget: FireAndForget): Boolean
abstract fun disable(fireAndForget: FireAndForget)
abstract fun reset(fireAndForget: FireAndForget)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class SettingsFireAndForgetRunner(
val settings: Settings,
) : FireAndForgetRunner() {

override fun isEnabled(fireAndForget: FireAndForget): Boolean {
override fun checkEnabled(fireAndForget: FireAndForget): Boolean {
return settings.getBoolean(
key = buildKey(fireAndForget = fireAndForget),
defaultValue = fireAndForget.defaultValue,
Expand Down
8 changes: 8 additions & 0 deletions samples/androidApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
alias(libs.plugins.androidApplication)
alias(libs.plugins.kotlinAndroid)
Expand Down Expand Up @@ -45,6 +47,12 @@ android {
}
}

kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}

dependencies {
implementation(projects.samples.shared)
implementation(libs.androidx.activity.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ class FireTwo(
) : FireAndForget(
fireAndForgetRunner = fireAndForgetRunner,
name = "fire_two",
autoDisable = true, // Automatically disables on first access
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class InMemoryFireAndForgetRunner: FireAndForgetRunner() {

val map: MutableMap<String, Boolean> = mutableMapOf()

override fun isEnabled(fireAndForget: FireAndForget): Boolean {
override fun checkEnabled(fireAndForget: FireAndForget): Boolean {
return map[fireAndForget.name] ?: fireAndForget.defaultValue
}

Expand Down