├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ ├── drawable
│ │ │ ├── ic_launcher_monochrome.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ └── ic_launcher_foreground.xml
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ ├── styles.xml
│ │ │ └── dimens.xml
│ │ └── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── kotlin
│ │ └── mini
│ │ │ └── android
│ │ │ └── sample
│ │ │ ├── ui
│ │ │ └── theme
│ │ │ │ ├── Type.kt
│ │ │ │ └── Color.kt
│ │ │ ├── SampleActions.kt
│ │ │ ├── SampleScreen.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── StoreSampleActivity.kt
│ │ │ └── ViewModelSampleActivity.kt
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle.kts
├── jitpack.yml
├── mini-common
├── .gitignore
├── src
│ ├── main
│ │ ├── resources
│ │ │ └── META-INF
│ │ │ │ └── proguard
│ │ │ │ └── mini-common.pro
│ │ └── java
│ │ │ └── mini
│ │ │ ├── Misc.kt
│ │ │ ├── State.kt
│ │ │ ├── AndroidInterop.kt
│ │ │ ├── CloseableTracker.kt
│ │ │ ├── CompositeCloseable.kt
│ │ │ ├── Middleware.kt
│ │ │ ├── Annotations.kt
│ │ │ ├── NestedStateContainer.kt
│ │ │ ├── Threading.kt
│ │ │ ├── Store.kt
│ │ │ ├── StateContainer.kt
│ │ │ ├── Mini.kt
│ │ │ ├── LoggerMiddleware.kt
│ │ │ ├── DiffAdapter.kt
│ │ │ └── StoreFlow.kt
│ └── test
│ │ └── kotlin
│ │ └── mini
│ │ ├── TestAction.kt
│ │ ├── SampleStore.kt
│ │ ├── CompositeCloseableTest.kt
│ │ ├── LoggerMiddlewareTest.kt
│ │ ├── StoreTest.kt
│ │ ├── TestDispatcher.kt
│ │ ├── DispatcherTest.kt
│ │ ├── ResourceTest.kt
│ │ └── StoreFlowTest.kt
└── build.gradle.kts
├── mini-kodein
├── .gitignore
├── src
│ └── main
│ │ ├── resources
│ │ └── META-INF
│ │ │ └── proguard
│ │ │ └── mini-kodein.pro
│ │ └── java
│ │ └── mini
│ │ └── kodein
│ │ └── KodeinUtils.kt
└── build.gradle.kts
├── convention-plugins
├── .gitignore
├── settings.gradle.kts
├── src
│ └── main
│ │ └── kotlin
│ │ └── mini
│ │ └── android
│ │ └── plugins
│ │ ├── JavaLibConventionPlugin.kt
│ │ ├── AndroidLibConventionPlugin.kt
│ │ ├── AndroidAppConventionPlugin.kt
│ │ └── extensions
│ │ ├── MavenPomExtensions.kt
│ │ └── ProjectExtensions.kt
└── build.gradle.kts
├── mini-android
├── .gitignore
├── mini-android.pro
├── proguard-rules.pro
├── src
│ └── main
│ │ └── java
│ │ └── mini
│ │ └── android
│ │ ├── FluxViewModel.kt
│ │ ├── FluxFragment.kt
│ │ ├── FluxActivity.kt
│ │ └── FluxStoreViewModel.kt
└── build.gradle.kts
├── mini-processor
├── .gitignore
├── src
│ └── main
│ │ ├── java
│ │ └── mini
│ │ │ └── processor
│ │ │ ├── ksp
│ │ │ ├── MiniSymbolProcessorProvider.kt
│ │ │ ├── reducers
│ │ │ │ └── KspReducersGeneratorDelegate.kt
│ │ │ ├── actions
│ │ │ │ ├── KspActionTypesGeneratorDelegate.kt
│ │ │ │ └── KspActionModel.kt
│ │ │ ├── SymbolProcessorUtils.kt
│ │ │ └── MiniSymbolProcessor.kt
│ │ │ ├── common
│ │ │ ├── ProcessorException.kt
│ │ │ ├── ModelGeneratorDelegate.kt
│ │ │ ├── actions
│ │ │ │ ├── ActionTypesGeneratorDelegate.kt
│ │ │ │ ├── ActionModels.kt
│ │ │ │ └── ActionTypesGenerator.kt
│ │ │ ├── reducers
│ │ │ │ ├── ReducersGeneratorDelegate.kt
│ │ │ │ ├── ReducersModels.kt
│ │ │ │ └── ReducersGenerator.kt
│ │ │ └── ContainerBuilders.kt
│ │ │ └── kapt
│ │ │ ├── actions
│ │ │ ├── KaptActionTypesGeneratorDelegate.kt
│ │ │ └── KaptActionModel.kt
│ │ │ ├── reducers
│ │ │ └── KaptReducersGeneratorDelegate.kt
│ │ │ ├── MiniAnnotationProcessor.java
│ │ │ ├── Processor.kt
│ │ │ └── ProcessorUtils.kt
│ │ └── resources
│ │ └── META-INF
│ │ └── services
│ │ ├── javax.annotation.processing.Processor
│ │ └── com.google.devtools.ksp.processing.SymbolProcessorProvider
└── build.gradle.kts
├── mini-testing
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── mini
│ └── testing
│ ├── TestDispatcherMiddleware.kt
│ ├── CleanStateRule.kt
│ └── TestDispatcherRule.kt
├── mini-kodein-android
├── .gitignore
├── mini-kodein-android.pro
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ └── java
│ │ └── mini
│ │ └── kodein
│ │ └── android
│ │ ├── TypedViewModel.kt
│ │ └── FluxTypedViewModel.kt
├── proguard-rules.pro
└── build.gradle.kts
├── mini-processor-test
├── .gitignore
├── src
│ ├── main
│ │ └── java
│ │ │ └── mini
│ │ │ └── test
│ │ │ ├── BasicState.kt
│ │ │ ├── AnyAction.kt
│ │ │ └── ReducersStore.kt
│ └── test
│ │ └── java
│ │ └── mini
│ │ └── test
│ │ └── ReducersStoreTest.kt
└── build.gradle.kts
├── mini-kodein-android-compose
├── .gitignore
├── mini-kodein-android-compose.pro
├── proguard-rules.pro
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── mini
│ └── kodein
│ └── android
│ └── compose
│ └── KodeinAndroidComposeUtils.kt
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── kotlinc.xml
├── vcs.xml
└── copyright
│ ├── profiles_settings.xml
│ └── Apache_2_0.xml
├── .github
├── dependabot.yml
├── pull_request_template.md
├── CODEOWNERS
└── ISSUE_TEMPLATE
│ ├── enhancement.md
│ └── bug_report.md
├── settings.gradle.kts
├── gradle.properties
├── gradlew.bat
└── .gitignore
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk17
--------------------------------------------------------------------------------
/mini-common/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mini-kodein/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/convention-plugins/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/mini-android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mini-processor/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mini-testing/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mini-kodein-android/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mini-processor-test/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/mini-kodein-android-compose/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/mini-kodein-android/mini-kodein-android.pro:
--------------------------------------------------------------------------------
1 | # Needed for injection
2 | -keep class * extends androidx.lifecycle.ViewModel { *; }
--------------------------------------------------------------------------------
/mini-kodein-android-compose/mini-kodein-android-compose.pro:
--------------------------------------------------------------------------------
1 | # Needed for injection
2 | -keep class * extends androidx.lifecycle.ViewModel { *; }
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyperdevs-team/mini-kotlin/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # Updates for Gradle dependencies used in the app
4 | - package-ecosystem: gradle
5 | directory: "/"
6 | schedule:
7 | interval: "monthly"
8 | open-pull-requests-limit: 10
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_monochrome.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/mini-android/mini-android.pro:
--------------------------------------------------------------------------------
1 | # ViewModel's empty constructor is considered to be unused by proguard, so keep it
2 | -keepclassmembers,allowobfuscation class * extends androidx.lifecycle.ViewModel {
3 | ();
4 | }
5 | -keepclassmembers,allowobfuscation class * extends androidx.lifecycle.AndroidViewModel {
6 | (android.app.Application);
7 | }
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### Github issue (delete if this does not apply)
2 | https://github.com/hyperdevs-team/mini-kotlin/issues/change_me_issue_number
3 |
4 | ### PR's key points
5 |
6 | ### How to review this PR?
7 |
8 | ### Related Issues (delete if this does not apply)
9 |
10 | ### Definition of Done
11 | - [ ] Tests pass
12 | - [ ] Works with Proguard
13 | - [ ] There is no outcommented or debug code left
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This is a comment.
2 | # Each line is a file pattern followed by one or more owners.
3 |
4 | # Current code owners
5 | # - @nicolasmertanen -> Nicolás Mertanen
6 | # - @finxo -> Alejandro Lopez
7 | # - @FrangSierra -> Francisco García
8 | # - @adriangl -> Adrián García
9 | # - @yamidragut -> Estefanía Sarasola
10 | # - @Babelia13 -> Sara Lucía Pérez
11 |
12 | * @nicolasmertanen @finxo @FrangSierra @adriangl @yamidragut @Babelia13
--------------------------------------------------------------------------------
/mini-kodein/src/main/resources/META-INF/proguard/mini-kodein.pro:
--------------------------------------------------------------------------------
1 | -keep, allowobfuscation, allowoptimization class org.kodein.type.TypeReference
2 | -keep, allowobfuscation, allowoptimization class org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
3 | -keep, allowobfuscation, allowoptimization class * extends org.kodein.type.TypeReference
4 | -keep, allowobfuscation, allowoptimization class * extends org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/enhancement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Enhancement
3 | about: Create a report to propose new features and improvements
4 | title: ''
5 | labels: enhancement
6 | assignees: adriangl
7 |
8 | ---
9 |
10 | **Summary and context of the enhancement**
11 | A clear and concise description of what the enhancement is and why it
12 | should be added.
13 |
14 | **Suggested implementation**
15 | Add suggestions about how the enhancement should be implemented.
16 |
17 | **Additional documentation**
18 | Useful links to review the enhancement.
--------------------------------------------------------------------------------
/mini-common/src/main/resources/META-INF/proguard/mini-common.pro:
--------------------------------------------------------------------------------
1 | -keep class mini.codegen.** { *; }
2 |
3 | -keepnames class * extends mini.Store { *; }
4 | -keepnames class * extends mini.State { *; }
5 | -keepnames @mini.Action class * { *; }
6 |
7 | -keep class mini.Action
8 | -keep @mini.Action class * { *; }
9 |
10 | -keep class mini.Resource { *; }
11 |
12 | -keep class mini.State { *; }
13 | -keep class * implements mini.State { *; }
14 |
15 | -keep class mini.StateContainer { *; }
16 | -keep class * implements mini.StateContainer { *; }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/ksp/MiniSymbolProcessorProvider.kt:
--------------------------------------------------------------------------------
1 | package mini.processor.ksp
2 |
3 | import com.google.devtools.ksp.processing.SymbolProcessor
4 | import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
5 | import com.google.devtools.ksp.processing.SymbolProcessorProvider
6 |
7 | class MiniSymbolProcessorProvider : SymbolProcessorProvider {
8 | override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
9 | logger = environment.logger
10 |
11 | return MiniSymbolProcessor(
12 | codeGenerator = environment.codeGenerator
13 | )
14 | }
15 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/pablo/development/android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.kts.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/mini-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2021 HyperDevs
3 | #
4 | # Copyright 2020 BQ
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | mini.processor.kapt.MiniAnnotationProcessor
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/ProcessorException.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common
18 |
19 | class ProcessorException : IllegalStateException()
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/TestAction.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | @Action
22 | data class TestAction(val value: String = "dummy")
--------------------------------------------------------------------------------
/.idea/copyright/Apache_2_0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/mini-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2021 HyperDevs
3 | #
4 | # Copyright 2020 BQ
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | mini.processor.ksp.MiniSymbolProcessorProvider
--------------------------------------------------------------------------------
/mini-processor-test/src/main/java/mini/test/BasicState.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.test
18 |
19 | import mini.State
20 |
21 | data class BasicState(val value: String = "initial") : State
--------------------------------------------------------------------------------
/app/src/main/kotlin/mini/android/sample/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.sample.ui.theme
18 |
19 | import androidx.compose.material3.Typography
20 |
21 | val AppTypography = Typography()
22 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/ModelGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common
18 |
19 | interface ModelGeneratorDelegate {
20 | fun provideModels(): List
21 | }
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/Misc.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | fun Any?.toQuotedString(): String =
22 | if (this is String) "\"$this\""
23 | else this.toString()
--------------------------------------------------------------------------------
/mini-kodein-android/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 | mini-kodein-android
21 |
22 |
--------------------------------------------------------------------------------
/mini-processor-test/src/main/java/mini/test/AnyAction.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.test
20 |
21 | import mini.Action
22 |
23 | @Action
24 | data class AnyAction(val value: String)
--------------------------------------------------------------------------------
/mini-android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/State.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2023 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | /**
20 | * Class that represents a state in the app.
21 | *
22 | * All state objects need to implement this interface.
23 | */
24 | interface State {
25 | }
--------------------------------------------------------------------------------
/mini-kodein-android-compose/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/mini-kodein-android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/actions/ActionTypesGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common.actions
18 |
19 | import mini.processor.common.ModelGeneratorDelegate
20 |
21 | interface ActionTypesGeneratorDelegate : ModelGeneratorDelegate
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/reducers/ReducersGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common.reducers
18 |
19 | import mini.processor.common.ModelGeneratorDelegate
20 |
21 | interface ReducersGeneratorDelegate : ModelGeneratorDelegate
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: adriangl
7 |
8 | ---
9 |
10 | **Device and SW details (please complete the following information):**
11 | - Device: [e.g. Google Pixel 3]
12 | - OS: [e.g. Android 9]
13 | - Library Version [e.g. 1.0.0]
14 |
15 | **Summary and background of the bug**
16 | A clear and concise description of what the bug is.
17 |
18 | **Steps to reproduce**
19 | Steps to reproduce the behavior:
20 | 1. Go to '...'
21 | 2. Click on '....'
22 | 3. Scroll down to '....'
23 | 4. See error
24 |
25 | Also attach notes or stack traces if applicable.
26 |
27 | **Expected behavior**
28 | A clear and concise description of what you expected to happen.
29 |
30 | **Current behavior**
31 | The summary of what currently happens in your use case.
32 |
33 | **Additional context**
34 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 | Mini Sample
21 | Store Sample
22 | View Model Sample
23 |
24 |
--------------------------------------------------------------------------------
/mini-kodein-android/src/main/java/mini/kodein/android/TypedViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.kodein.android
18 |
19 | import androidx.lifecycle.ViewModel
20 |
21 | /**
22 | * Generic [ViewModel] that adds support for adding a single [params] object to ease parameter
23 | * injection.
24 | */
25 | open class TypedViewModel(private val params: T) : ViewModel()
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/SampleStore.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | data class SampleState(val value: String): State
22 |
23 | class SampleStore : Store() {
24 |
25 | companion object {
26 | const val INITIAL_STATE = "initial"
27 | }
28 |
29 | override fun initialState(): SampleState = SampleState(INITIAL_STATE)
30 | }
--------------------------------------------------------------------------------
/convention-plugins/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @file:Suppress("UnstableApiUsage")
18 |
19 | dependencyResolutionManagement {
20 | repositories {
21 | google()
22 | mavenCentral()
23 | gradlePluginPortal()
24 | }
25 |
26 | versionCatalogs {
27 | create("libs") {
28 | from(files("../gradle/libs.versions.toml"))
29 | }
30 | }
31 | }
32 |
33 | rootProject.name = "convention-plugins"
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 | #393838
22 | #393838
23 | #8BC34A
24 | @color/white
25 |
26 | #ffffff
27 | #7dffffff
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
25 |
28 |
29 |
--------------------------------------------------------------------------------
/mini-kodein-android/src/main/java/mini/kodein/android/FluxTypedViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.kodein.android
18 |
19 | import androidx.annotation.CallSuper
20 | import mini.CloseableTracker
21 | import mini.DefaultCloseableTracker
22 |
23 | abstract class FluxTypedViewModel(params: T) : TypedViewModel(params),
24 | CloseableTracker by DefaultCloseableTracker() {
25 |
26 | @CallSuper
27 | override fun onCleared() {
28 | super.onCleared()
29 | close()
30 | }
31 | }
--------------------------------------------------------------------------------
/mini-android/src/main/java/mini/android/FluxViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.android
20 |
21 | import androidx.annotation.CallSuper
22 | import androidx.lifecycle.ViewModel
23 | import mini.CloseableTracker
24 | import mini.DefaultCloseableTracker
25 |
26 | abstract class FluxViewModel : ViewModel(),
27 | CloseableTracker by DefaultCloseableTracker() {
28 |
29 | @CallSuper
30 | override fun onCleared() {
31 | super.onCleared()
32 | close()
33 | }
34 | }
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/AndroidInterop.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | /**
22 | * Check if running on android device / emulator or jvm
23 | */
24 | internal val isAndroid by lazy {
25 | try {
26 | android.os.Build.VERSION.SDK_INT != 0
27 | } catch (e: Throwable) {
28 | false
29 | }
30 | }
31 |
32 | fun requireAndroid() {
33 | if (!isAndroid) {
34 | throw UnsupportedOperationException("This method can only be called from android environment")
35 | }
36 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/kapt/actions/KaptActionTypesGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.kapt.actions
18 |
19 | import mini.processor.common.actions.ActionTypesGeneratorDelegate
20 | import javax.lang.model.element.Element
21 | import javax.lang.model.element.Modifier
22 |
23 | class KaptActionTypesGeneratorDelegate(private val elements: Set) :
24 | ActionTypesGeneratorDelegate {
25 | override fun provideModels() = elements
26 | .filter { Modifier.ABSTRACT !in it.modifiers }
27 | .map { KaptActionModel(it) }
28 | }
--------------------------------------------------------------------------------
/convention-plugins/src/main/kotlin/mini/android/plugins/JavaLibConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.plugins
18 |
19 | import mini.android.plugins.extensions.applyJavaLibPublishing
20 | import mini.android.plugins.extensions.applyVersioning
21 | import org.gradle.api.Plugin
22 | import org.gradle.api.Project
23 |
24 | class JavaLibConventionPlugin : Plugin {
25 | override fun apply(target: Project) {
26 | with(target) {
27 | val (versionName, _) = applyVersioning()
28 | applyJavaLibPublishing(versionName)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/CloseableTracker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import java.io.Closeable
22 |
23 | interface CloseableTracker : Closeable {
24 | /**
25 | * Start tracking a disposable.
26 | */
27 | fun T.track(): T
28 | }
29 |
30 | class DefaultCloseableTracker : CloseableTracker {
31 | private val closeables = CompositeCloseable()
32 | override fun close() = closeables.close()
33 | override fun T.track(): T {
34 | closeables.add(this)
35 | return this
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/convention-plugins/src/main/kotlin/mini/android/plugins/AndroidLibConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.plugins
18 |
19 | import mini.android.plugins.extensions.applyAndroidLibPublishing
20 | import mini.android.plugins.extensions.applyVersioning
21 | import org.gradle.api.Plugin
22 | import org.gradle.api.Project
23 |
24 | class AndroidLibConventionPlugin : Plugin {
25 | override fun apply(target: Project) {
26 | with(target) {
27 | val (versionName, _) = applyVersioning()
28 | applyAndroidLibPublishing(versionName)
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/CompositeCloseable.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import java.io.Closeable
20 |
21 | /**
22 | * A collection of closeables.
23 | */
24 | class CompositeCloseable : Closeable {
25 | private val items = ArrayList()
26 |
27 | override fun close() {
28 | synchronized(this) {
29 | items.forEach { it.close() }
30 | items.clear()
31 | }
32 | }
33 |
34 | fun add(closeable: T): T {
35 | synchronized(this) {
36 | items.add(closeable)
37 | }
38 | return closeable
39 | }
40 | }
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/Middleware.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | /**
22 | * Middleware that will be called for every dispatch to modify the
23 | * action or perform side effects like logging.
24 | *
25 | * Call chain.proceed(action) with the new action or dispatcher chain will be broken.
26 | */
27 | interface Middleware {
28 | suspend fun intercept(action: Any, chain: Chain): Any
29 | }
30 |
31 | /**
32 | * A chain of interceptors. Call [proceed] with
33 | * the intercepted action or directly handle it.
34 | */
35 | interface Chain {
36 | suspend fun proceed(action: Any): Any
37 | }
38 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/kapt/reducers/KaptReducersGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.kapt.reducers
18 |
19 | import mini.processor.common.reducers.ReducerModel
20 | import mini.processor.common.reducers.ReducersGeneratorDelegate
21 | import javax.lang.model.element.Element
22 | import javax.lang.model.element.ExecutableElement
23 |
24 | class KaptReducersGeneratorDelegate(private val elements: Set) :
25 | ReducersGeneratorDelegate {
26 | override fun provideModels(): List = elements
27 | .filterIsInstance()
28 | .map { KaptReducerModel(it) }
29 | }
30 |
--------------------------------------------------------------------------------
/mini-kodein/src/main/java/mini/kodein/KodeinUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.kodein
20 |
21 | import mini.Store
22 | import org.kodein.di.*
23 | import org.kodein.di.bindings.NoArgBindingDI
24 |
25 | /**
26 | * Work based on: https://proandroiddev.com/android-viewmodel-dependency-injection-with-kodein-249f80f083c9
27 | */
28 |
29 | /**
30 | * Binds a store in a Kodein module, assuming that it's a singleton dependency.
31 | */
32 | inline fun > DI.Builder.bindStore(noinline creator: NoArgBindingDI<*>.() -> T) {
33 | bind() with singleton(creator = creator)
34 | bind>().inSet() with singleton { instance() }
35 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/convention-plugins/src/main/kotlin/mini/android/plugins/AndroidAppConventionPlugin.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.plugins
18 |
19 | import mini.android.plugins.extensions.applyAndroidAppVersioning
20 | import mini.android.plugins.extensions.applyAndroidLibPublishing
21 | import mini.android.plugins.extensions.applyVersioning
22 | import org.gradle.api.Plugin
23 | import org.gradle.api.Project
24 |
25 | class AndroidAppConventionPlugin : Plugin {
26 | override fun apply(target: Project) {
27 | with(target) {
28 | val (versionName, versionCode) = applyVersioning()
29 | applyAndroidAppVersioning(versionName, versionCode)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/reducers/ReducersModels.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common.reducers
18 |
19 | import com.squareup.kotlinpoet.CodeBlock
20 | import com.squareup.kotlinpoet.TypeName
21 |
22 | interface ReducerModel {
23 | val isPure: Boolean
24 | val isSuspending: Boolean
25 |
26 | val container: ContainerModel
27 | val priority: Int
28 |
29 | val actionTypeName: TypeName
30 | val returnTypeName: TypeName
31 |
32 | fun generateCallBlock(containerParam: String, actionParam: String): CodeBlock
33 | }
34 |
35 |
36 | interface ContainerModel {
37 | val typeName: TypeName
38 | val stateTypeName: TypeName
39 | val isStatic: Boolean
40 | }
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/CompositeCloseableTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import org.amshove.kluent.`should be equal to`
22 | import org.junit.Test
23 | import java.io.Closeable
24 |
25 | class CompositeCloseableTest {
26 |
27 | @Test
28 | fun itemsAreClosed() {
29 | val c = CompositeCloseable()
30 | val dummyCloseable = DummyCloseable()
31 | c.add(dummyCloseable)
32 | c.close()
33 | c.close()
34 |
35 | dummyCloseable.closed.`should be equal to`(1)
36 | }
37 |
38 | class DummyCloseable : Closeable {
39 | var closed = 0
40 | override fun close() {
41 | closed++
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/ksp/reducers/KspReducersGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.ksp.reducers
18 |
19 | import com.google.devtools.ksp.processing.KSPLogger
20 | import com.google.devtools.ksp.symbol.KSAnnotated
21 | import com.google.devtools.ksp.symbol.KSFunctionDeclaration
22 | import mini.processor.common.reducers.ReducerModel
23 | import mini.processor.common.reducers.ReducersGeneratorDelegate
24 |
25 | class KspReducersGeneratorDelegate(
26 | private val symbols: Sequence
27 | ) : ReducersGeneratorDelegate {
28 | override fun provideModels(): List = symbols
29 | .filterIsInstance()
30 | .map { KspReducerModel(it) }
31 | .toList()
32 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/mini/android/sample/SampleActions.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.sample
18 |
19 | import mini.Action
20 | import mini.Reducer
21 | import mini.SuspendingAction
22 | import java.io.Serializable
23 |
24 | data class MainState(
25 | val text: String = "0",
26 | val loading: Boolean = false
27 | ) : Serializable, mini.State
28 |
29 | @Action
30 | data class SetLoadingAction(val loading: Boolean)
31 |
32 | @Action
33 | data class SetTextAction(val text: String)
34 |
35 | @Action
36 | interface AnalyticsAction
37 |
38 | @Action
39 | class LongUseCaseAction(val userName: String) : AnalyticsAction, SuspendingAction
40 |
41 | /**
42 | * Use any name you like for suspending actions, or use reducer
43 | */
44 | typealias UseCase = Reducer
--------------------------------------------------------------------------------
/mini-testing/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.kotlin.jvm)
19 | alias(libs.plugins.convention.javaLib)
20 | }
21 |
22 | dependencies {
23 | api(project(":mini-common"))
24 | api(libs.kotlin.stdlib)
25 | api(libs.kotlin.reflect)
26 |
27 | api(libs.junit)
28 | }
29 |
30 | tasks.withType().configureEach {
31 | kotlinOptions {
32 | jvmTarget = libs.versions.java.sdk.get()
33 | }
34 | }
35 |
36 | java {
37 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
38 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
39 | }
40 |
41 | kotlin {
42 | jvmToolchain(libs.versions.java.sdk.get().toInt())
43 | }
--------------------------------------------------------------------------------
/mini-kodein/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.kotlin.jvm)
19 | alias(libs.plugins.convention.javaLib)
20 | }
21 |
22 | tasks.withType().configureEach {
23 | kotlinOptions {
24 | jvmTarget = libs.versions.java.sdk.get()
25 | }
26 | }
27 |
28 | java {
29 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
30 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
31 | }
32 |
33 | kotlin {
34 | jvmToolchain(libs.versions.java.sdk.get().toInt())
35 | }
36 |
37 | dependencies {
38 | api(project(":mini-common"))
39 | implementation(libs.kotlin.stdlib)
40 | implementation(libs.kotlin.reflect)
41 |
42 | api(libs.kodein.jvm)
43 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/actions/ActionModels.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common.actions
18 |
19 | import com.squareup.kotlinpoet.CodeBlock
20 | import com.squareup.kotlinpoet.TypeName
21 |
22 | interface ActionModel {
23 | fun listOfSupertypesCodeBlock(): CodeBlock
24 | val typeName: TypeName
25 | }
26 |
27 | class ActionSuperType(val typeName: TypeName, val depth: Int) {
28 | override fun equals(other: Any?): Boolean {
29 | if (this === other) return true
30 | if (javaClass != other?.javaClass) return false
31 | other as ActionSuperType
32 | if (typeName != other.typeName) return false
33 | return true
34 | }
35 |
36 | override fun hashCode(): Int {
37 | return typeName.hashCode()
38 | }
39 | }
--------------------------------------------------------------------------------
/mini-testing/src/main/java/mini/testing/TestDispatcherMiddleware.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.testing
20 |
21 | import mini.Action
22 | import mini.Chain
23 | import mini.Middleware
24 | import java.util.*
25 |
26 | /**
27 | * [Middleware] class for testing purposes which mute all the received actions.
28 | */
29 | internal class TestDispatcherMiddleware : Middleware {
30 | private val mutedActions = LinkedList()
31 |
32 | val actions: List get() = mutedActions
33 | override suspend fun intercept(action: Any, chain: Chain): Any {
34 | println("Muted: $action")
35 | mutedActions.add(action)
36 | return TestOnlyAction
37 | }
38 | }
39 |
40 | /**
41 | * Action for testing purposes.
42 | */
43 | @Action
44 | internal object TestOnlyAction
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/ksp/actions/KspActionTypesGeneratorDelegate.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.ksp.actions
18 |
19 | import com.google.devtools.ksp.processing.KSPLogger
20 | import com.google.devtools.ksp.symbol.KSAnnotated
21 | import com.google.devtools.ksp.symbol.KSClassDeclaration
22 | import com.google.devtools.ksp.validate
23 | import mini.processor.common.actions.ActionTypesGeneratorDelegate
24 |
25 | class KspActionTypesGeneratorDelegate(
26 | private val symbols: Sequence
27 | ) :
28 | ActionTypesGeneratorDelegate {
29 | override fun provideModels() = symbols
30 | .filterIsInstance()
31 | .filter { com.google.devtools.ksp.symbol.Modifier.ABSTRACT !in it.modifiers && it.validate() }
32 | .map { KspActionModel(it) }
33 | .toList()
34 | }
--------------------------------------------------------------------------------
/mini-processor-test/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.kotlin.jvm)
19 | alias(libs.plugins.kotlin.kapt)
20 | }
21 |
22 | dependencies {
23 | implementation(project(":mini-common"))
24 | kapt(project(":mini-processor"))
25 |
26 | implementation(libs.kotlinx.coroutines.core)
27 |
28 | testImplementation(libs.junit)
29 | testImplementation(libs.kluent)
30 | }
31 |
32 | tasks.withType().configureEach {
33 | kotlinOptions {
34 | jvmTarget = libs.versions.java.sdk.get()
35 | }
36 | }
37 |
38 | java {
39 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
40 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
41 | }
42 |
43 | kotlin {
44 | jvmToolchain(libs.versions.java.sdk.get().toInt())
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 | 12sp
23 | 14sp
24 | 16sp
25 | 20sp
26 | 24sp
27 |
28 |
29 | 4dp
30 | 8dp
31 | 12dp
32 | 16dp
33 | 20dp
34 | 24dp
35 |
36 |
37 | 4dp
38 |
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/LoggerMiddlewareTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import org.amshove.kluent.`should not be empty`
20 | import org.junit.Test
21 |
22 | class LoggerMiddlewareTest {
23 |
24 | @Test
25 | fun `logs are printed`() {
26 | val store = SampleStore()
27 | val dispatcher = newTestDispatcher()
28 | dispatcher.subscribe {
29 | store.setState(SampleState("Action sent"))
30 | }
31 |
32 | val out = StringBuilder()
33 | dispatcher.addMiddleware(LoggerMiddleware(listOf(store),
34 | logger = { priority, tag, msg ->
35 | println("[$priority][$tag] $msg")
36 | out.append(priority).append(tag).append(msg)
37 | }))
38 | dispatcher.dispatchBlocking(TestAction())
39 | out.toString().`should not be empty`()
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/mini-processor-test/src/test/java/mini/test/ReducersStoreTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.test
18 |
19 | import kotlinx.coroutines.runBlocking
20 | import mini.Dispatcher
21 | import mini.Mini
22 | import org.amshove.kluent.`should equal`
23 | import org.junit.Test
24 |
25 | internal class ReducersStoreTest {
26 |
27 | private val store = ReducersStore()
28 | private val dispatcher = Dispatcher().apply {
29 | Mini.link(this, listOf(store))
30 | }
31 |
32 | @Test
33 | fun `pure reducers are called`() {
34 | runBlocking {
35 | dispatcher.dispatch(AnyAction("changed"))
36 | store.state.value.`should equal`("changed")
37 | }
38 | }
39 |
40 | @Test
41 | fun `pure static reducers are called`() {
42 | runBlocking {
43 | dispatcher.dispatch(AnyAction("changed"))
44 | store.state.value.`should equal`("changed")
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/ContainerBuilders.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common
18 |
19 | import com.squareup.kotlinpoet.ClassName
20 | import com.squareup.kotlinpoet.FileSpec
21 | import com.squareup.kotlinpoet.TypeSpec
22 | import mini.DISPATCHER_FACTORY_CLASS_NAME
23 | import mini.Mini
24 |
25 | data class ContainerBuilders(
26 | val fileSpecBuilder: FileSpec.Builder,
27 | val typeSpecBuilder: TypeSpec.Builder
28 | )
29 |
30 | fun getContainerBuilders(): ContainerBuilders {
31 | val containerClassName = ClassName.bestGuess(DISPATCHER_FACTORY_CLASS_NAME)
32 | val containerFile =
33 | FileSpec.builder(containerClassName.packageName, containerClassName.simpleName)
34 | val container = TypeSpec.objectBuilder(containerClassName)
35 | .addKdoc("Automatically generated, do not edit.\n")
36 | .superclass(Mini::class)
37 | return ContainerBuilders(containerFile, container)
38 | }
--------------------------------------------------------------------------------
/mini-testing/src/main/java/mini/testing/CleanStateRule.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.testing
20 |
21 | import mini.Store
22 | import org.junit.rules.TestRule
23 | import org.junit.runner.Description
24 | import org.junit.runners.model.Statement
25 |
26 | /**
27 | * [TestRule] that resets the state of each Store (retrieved via function) after an evaluation.
28 | */
29 | class CleanStateRule(val storesFn: () -> List>) : TestRule {
30 | override fun apply(base: Statement, description: Description): Statement {
31 | return object : Statement() {
32 | fun reset() {
33 | val stores = storesFn()
34 | stores.forEach { it.resetState() }
35 | }
36 |
37 | override fun evaluate() {
38 | reset()
39 | base.evaluate() //Execute the test
40 | reset()
41 | }
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | @file:Suppress("UnstableApiUsage")
18 |
19 | include(":app")
20 | include(":mini-processor")
21 | include(":mini-common")
22 | include(":mini-android")
23 | include(":mini-processor-test")
24 | include(":mini-kodein")
25 | include(":mini-kodein-android")
26 | include(":mini-kodein-android-compose")
27 | include(":mini-testing")
28 |
29 | // Modules to add as composite builds
30 | includeBuild("convention-plugins")
31 |
32 | pluginManagement {
33 | repositories {
34 | google()
35 | mavenCentral()
36 | gradlePluginPortal()
37 | maven { url = java.net.URI("https://jitpack.io") }
38 | }
39 | }
40 |
41 | dependencyResolutionManagement {
42 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
43 |
44 | repositories {
45 | google()
46 | mavenCentral()
47 | gradlePluginPortal()
48 | maven { url = java.net.URI("https://jitpack.io") }
49 | }
50 | }
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/Annotations.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import java.lang.annotation.Inherited
22 |
23 | const val DEFAULT_PRIORITY = 100
24 |
25 | /**
26 | * Mark a type as action for code generation. All actions must include this annotation
27 | * or dispatcher won't work properly.
28 | */
29 | @Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS)
30 | @Retention(AnnotationRetention.RUNTIME)
31 | @Inherited
32 | annotation class Action
33 |
34 | /**
35 | * Mark a function declared in a [StateContainer] as a reducer function.
36 | *
37 | * Reducers function must have two parameters, the state that must have same time
38 | * as the [StateContainer] state, and the action being handled.
39 | *
40 | * If the reducer function is not pure, only the action parameter is allowed
41 | * and function should have no return.
42 | */
43 | @Target(AnnotationTarget.FUNCTION)
44 | @Retention(AnnotationRetention.RUNTIME)
45 | annotation class Reducer(val priority: Int = DEFAULT_PRIORITY)
46 |
47 |
--------------------------------------------------------------------------------
/mini-processor/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.kotlin.jvm)
19 | alias(libs.plugins.kotlin.kapt)
20 | alias(libs.plugins.convention.javaLib)
21 | }
22 |
23 | dependencies {
24 | api(project(":mini-common"))
25 | implementation(libs.kotlin.stdlib)
26 | implementation(libs.bundles.kotlinpoet)
27 |
28 | // Lib to add incremental annotation processing
29 | compileOnly(libs.incap)
30 | kapt(libs.incap.processor)
31 |
32 | testImplementation(libs.junit)
33 |
34 | implementation(libs.google.ksp.symbolprocessing)
35 | }
36 |
37 | tasks.withType().configureEach {
38 | kotlinOptions {
39 | jvmTarget = libs.versions.java.sdk.get()
40 | }
41 | }
42 |
43 | java {
44 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
45 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
46 | }
47 |
48 | kotlin {
49 | jvmToolchain(libs.versions.java.sdk.get().toInt())
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2021 HyperDevs
3 | #
4 | # Copyright 2020 BQ
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | # Project-wide Gradle settings.
20 | # IDE (e.g. Android Studio) users:
21 | # Gradle settings configured through the IDE *will take*
22 | # any settings specified in this file.
23 | # For more details on how to fn your build environment visit
24 | # http://www.gradle.org/docs/current/userguide/build_environment.html
25 | # Specifies the JVM arguments used for the daemon process.
26 | # The setting is particularly useful for tweaking memory settings.
27 | org.gradle.jvmargs=-Xmx1536m
28 | android.useAndroidX=true
29 | android.enableJetifier=false
30 | # When configured, Gradle will run in incubating parallel mode.
31 | # This option should only be used with decoupled projects. More details, visit
32 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
33 | # org.gradle.parallel=true
34 |
35 | # Some performance improvements
36 | org.gradle.parallel=true
37 | org.gradle.configureondemand=true
38 | org.gradle.caching=true
39 | org.gradle.daemon=true
40 | kapt.incremental.apt=true
41 | kapt.use.worker.api=true
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/NestedStateContainer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import java.io.Closeable
20 |
21 | /**
22 | * Utility class to allow splitting [StateContainer] into chunks so not all reducers live in the same
23 | * file.
24 | *
25 | * From a state container
26 | *
27 | * ```
28 | * class Reducer : NestedStateContainer() {
29 | * @Reducer
30 | * fun reduceOneAction(...)
31 | * }
32 | *
33 | * class MyStore {
34 | * val reducer = Reducer(this)
35 | *
36 | * init {
37 | * Mini.link(dispatcher, listOf(this, reducer))
38 | * }
39 | *
40 | * @Reducer
41 | * fun globalReduceFn(...)
42 | * }
43 | * ```
44 | */
45 | abstract class NestedStateContainer(
46 | var parent: StateContainer? = null
47 | ) : StateContainer {
48 | override val state: S
49 | get() = parent!!.state
50 |
51 | override fun setState(newState: S) {
52 | parent!!.setState(newState)
53 | }
54 |
55 | override fun subscribe(hotStart: Boolean, fn: (S) -> Unit): Closeable {
56 | return parent!!.subscribe(hotStart, fn)
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
24 |
27 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/mini-testing/src/main/java/mini/testing/TestDispatcherRule.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.testing
20 |
21 | import mini.Dispatcher
22 | import org.junit.rules.TestRule
23 | import org.junit.runner.Description
24 | import org.junit.runners.model.Statement
25 |
26 | /**
27 | * This [TestRule] evaluates every action received with the [TestDispatcherMiddleware] to
28 | * intercept all the actions dispatched during a test and block them, getting them not reaching the store.
29 | */
30 | class TestDispatcherRule(val dispatcherFn: () -> Dispatcher) : TestRule {
31 | private val testMiddleware = TestDispatcherMiddleware()
32 | val actions: List get() = testMiddleware.actions
33 |
34 | override fun apply(base: Statement, description: Description): Statement {
35 | return object : Statement() {
36 | override fun evaluate() {
37 | val dispatcher = dispatcherFn()
38 | dispatcher.addMiddleware(testMiddleware)
39 | base.evaluate() //Execute the test
40 | dispatcher.removeMiddleware(testMiddleware)
41 | }
42 | }
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/convention-plugins/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | `java-gradle-plugin`
19 | `kotlin-dsl`
20 | }
21 |
22 | dependencies {
23 | compileOnly(libs.android.gradle)
24 | implementation(libs.androidgitversion)
25 | }
26 |
27 | java {
28 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
29 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
30 | }
31 |
32 | tasks.withType().configureEach {
33 | kotlinOptions {
34 | jvmTarget = libs.versions.java.sdk.get()
35 | }
36 | }
37 |
38 | gradlePlugin {
39 | plugins {
40 | register("androidApp") {
41 | id = "mini.android.plugins.androidApp"
42 | implementationClass = "mini.android.plugins.AndroidAppConventionPlugin"
43 | }
44 |
45 | register("androidLib") {
46 | id = "mini.android.plugins.androidLib"
47 | implementationClass = "mini.android.plugins.AndroidLibConventionPlugin"
48 | }
49 |
50 | register("javaLib") {
51 | id = "mini.android.plugins.javaLib"
52 | implementationClass = "mini.android.plugins.JavaLibConventionPlugin"
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/mini-kodein-android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.convention.androidLib)
5 | }
6 |
7 | android {
8 | namespace = "mini.kodein.android"
9 |
10 | compileSdk = libs.versions.android.compileSdk.get().toInt()
11 | buildToolsVersion = libs.versions.android.buildTools.get()
12 |
13 | defaultConfig {
14 | minSdk = libs.versions.android.minSdk.get().toInt()
15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles("mini-kodein-android.pro")
17 | }
18 |
19 | buildTypes {
20 | release {
21 | isMinifyEnabled = false
22 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
23 | }
24 | }
25 |
26 | compileOptions {
27 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
28 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
29 | }
30 | }
31 |
32 | tasks.withType().configureEach {
33 | kotlinOptions {
34 | jvmTarget = libs.versions.java.sdk.get()
35 | }
36 | }
37 |
38 | java {
39 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
40 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
41 | }
42 |
43 | kotlin {
44 | jvmToolchain(libs.versions.java.sdk.get().toInt())
45 | }
46 |
47 | dependencies {
48 | api(project(":mini-kodein"))
49 |
50 | implementation(libs.kotlin.stdlib)
51 | implementation(libs.kotlin.reflect)
52 |
53 | api(libs.androidx.fragment)
54 | api(libs.androidx.lifecycle.viewmodel)
55 | api(libs.kodein.framework.androidx)
56 |
57 | testImplementation(libs.junit)
58 | androidTestImplementation(libs.androidx.test.runner)
59 | androidTestImplementation(libs.espresso)
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/ksp/SymbolProcessorUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.ksp
18 |
19 | import com.google.devtools.ksp.processing.KSPLogger
20 | import com.google.devtools.ksp.symbol.KSDeclaration
21 | import mini.processor.common.ProcessorException
22 | import javax.tools.Diagnostic
23 |
24 | lateinit var logger: KSPLogger
25 |
26 | fun kspCompilePrecondition(
27 | check: Boolean,
28 | message: String,
29 | declaration: KSDeclaration? = null
30 | ) {
31 | if (!check) {
32 | kspLogError(message, declaration)
33 | throw ProcessorException()
34 | }
35 | }
36 |
37 | fun kspLogError(message: String, declaration: KSDeclaration? = null) {
38 | kspLogMessage(Diagnostic.Kind.ERROR, message, declaration)
39 | }
40 |
41 | fun kspWarning(message: String, declaration: KSDeclaration? = null) {
42 | kspLogMessage(Diagnostic.Kind.MANDATORY_WARNING, message, declaration)
43 | }
44 |
45 | fun kspLogMessage(
46 | kind: Diagnostic.Kind,
47 | message: String,
48 | declaration: KSDeclaration? = null
49 | ) {
50 | when (kind) {
51 | Diagnostic.Kind.ERROR -> logger.error(message, declaration)
52 | Diagnostic.Kind.WARNING, Diagnostic.Kind.MANDATORY_WARNING -> logger.warn(
53 | message,
54 | declaration
55 | )
56 | else -> logger.logging(message, declaration)
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
21 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
43 |
44 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/Threading.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import android.os.Handler
20 | import android.os.Looper
21 | import java.util.concurrent.Semaphore
22 |
23 | val uiHandler by lazy {
24 | requireAndroid()
25 | Handler(Looper.getMainLooper())
26 | }
27 |
28 | fun assertOnUiThread() {
29 | if (!isAndroid) return
30 | if (Looper.myLooper() != Looper.getMainLooper()) {
31 | error("This method can only be called from the main application thread")
32 | }
33 | }
34 |
35 | fun assertOnBgThread() {
36 | if (!isAndroid) return
37 | if (Looper.myLooper() == Looper.getMainLooper()) {
38 | error("This method can only be called from non UI threads")
39 | }
40 | }
41 |
42 | @JvmOverloads
43 | inline fun onUi(delayMs: Long = 0, crossinline block: () -> Unit) {
44 | requireAndroid()
45 | if (delayMs > 0) uiHandler.postDelayed({ block() }, delayMs)
46 | else uiHandler.post { block() }
47 | }
48 |
49 | inline fun onUiSync(crossinline block: () -> T) {
50 | uiHandler.postSync(block)
51 | }
52 |
53 | inline fun Handler.postSync(crossinline block: () -> T) {
54 | requireAndroid()
55 | if (Looper.myLooper() == this.looper) {
56 | block()
57 | } else {
58 | val sem = Semaphore(0)
59 | post {
60 | block()
61 | sem.release()
62 | }
63 | sem.acquireUninterruptibly()
64 | }
65 | }
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/StoreTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import org.amshove.kluent.`should be equal to`
22 | import org.junit.Test
23 |
24 | class StoreTest {
25 |
26 | @Test
27 | fun `state is updated`() {
28 | val store = SampleStore()
29 | store.setState(SampleState("abc"))
30 | store.state `should be equal to` SampleState("abc")
31 | }
32 |
33 | @Test
34 | fun `observers are called`() {
35 | val store = SampleStore()
36 | var state = SampleState("")
37 | store.subscribe {
38 | state = it
39 | }
40 | store.setState(SampleState("abc"))
41 | state `should be equal to` SampleState("abc")
42 | }
43 |
44 | @Test
45 | fun `initial state is sent on subscribe`() {
46 | val store = SampleStore()
47 | var state = SampleState("initial")
48 | store.subscribe {
49 | state = it
50 | }
51 | state `should be equal to` SampleState("initial")
52 | }
53 |
54 | @Test
55 | fun `observers are removed on close`() {
56 | val store = SampleStore()
57 | var state = SampleState("")
58 | val closeable = store.subscribe(hotStart = false) {
59 | state = it
60 | }
61 | closeable.close()
62 | store.setState(SampleState("abc"))
63 | state `should be equal to` SampleState("")
64 | }
65 | }
--------------------------------------------------------------------------------
/mini-processor-test/src/main/java/mini/test/ReducersStore.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.test
18 |
19 | import kotlinx.coroutines.yield
20 | import mini.Reducer
21 | import mini.Store
22 |
23 | class ReducersStore : Store() {
24 |
25 | companion object {
26 | @Reducer
27 | fun staticImpureReducer(action: AnyAction) {
28 |
29 | }
30 |
31 | @Reducer
32 | suspend fun staticSuspendingImpureReducer(action: AnyAction) {
33 | yield()
34 | }
35 |
36 | @Reducer
37 | fun staticPureReducer(state: BasicState, action: AnyAction): BasicState {
38 | return state.copy(value = action.value)
39 | }
40 |
41 | @Reducer
42 | suspend fun staticSuspendingPureReducer(state: BasicState, action: AnyAction): BasicState {
43 | yield()
44 | return state.copy(value = action.value)
45 | }
46 | }
47 |
48 | @Reducer
49 | fun impureReducer(action: AnyAction) {
50 |
51 | }
52 |
53 | @Reducer
54 | suspend fun impureSuspendingReducer(action: AnyAction) {
55 | yield()
56 | }
57 |
58 | @Reducer
59 | fun pureReducer(state: BasicState, action: AnyAction): BasicState {
60 | return state.copy(value = action.value)
61 | }
62 |
63 | @Reducer
64 | suspend fun pureSuspendingReducer(state: BasicState, action: AnyAction): BasicState {
65 | yield()
66 | return state.copy(value = action.value)
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/mini-common/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | id("idea")
19 | alias(libs.plugins.kotlin.jvm)
20 | alias(libs.plugins.convention.javaLib)
21 | }
22 |
23 | dependencies {
24 | implementation(libs.kotlin.stdlib)
25 | implementation(libs.kotlin.reflect)
26 |
27 | // Optional Rx and Android bindings, one day these should be modules,
28 | // for now we compile against them but let user package library
29 | compileOnly(libs.android.library)
30 |
31 | compileOnly(libs.kotlinx.coroutines.core)
32 | testImplementation(libs.kotlinx.coroutines.core)
33 |
34 | testImplementation(libs.junit)
35 | testImplementation(libs.kluent)
36 | }
37 |
38 | tasks.withType().configureEach {
39 | kotlinOptions {
40 | jvmTarget = libs.versions.java.sdk.get()
41 | }
42 | }
43 |
44 | java {
45 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
46 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
47 | }
48 |
49 | kotlin {
50 | jvmToolchain(libs.versions.java.sdk.get().toInt())
51 | }
52 |
53 | idea {
54 | module {
55 | val sourceFoldersToAdd = listOf(
56 | "build/generated/source/kapt/main",
57 | "build/generated/source/kaptKotlin/main",
58 | "build/generated/source/ksp/main"
59 | ).map { File(it) }
60 |
61 | sourceDirs.addAll(sourceFoldersToAdd)
62 | generatedSourceDirs.addAll(sourceFoldersToAdd)
63 | }
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/ksp/actions/KspActionModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.ksp.actions
18 |
19 | import com.google.devtools.ksp.symbol.KSClassDeclaration
20 | import com.google.devtools.ksp.symbol.KSType
21 | import com.squareup.kotlinpoet.CodeBlock
22 | import com.squareup.kotlinpoet.ksp.toTypeName
23 | import mini.processor.common.actions.ActionModel
24 | import mini.processor.common.actions.ActionSuperType
25 |
26 | class KspActionModel(declaration: KSClassDeclaration) : ActionModel {
27 | private val type = declaration.asStarProjectedType()
28 |
29 | override val typeName = type.toTypeName()
30 | private val superTypes = collectTypes(type)
31 | .sortedBy { it.depth }
32 | .map { it.typeName }
33 |
34 | override fun listOfSupertypesCodeBlock(): CodeBlock {
35 | val format = superTypes.joinToString(",\n") { "%T::class" }
36 | val args = superTypes.toTypedArray()
37 | return CodeBlock.of("listOf($format)", *args)
38 | }
39 |
40 | private fun collectTypes(type: KSType, depth: Int = 0): Set {
41 | val rootSuperTypes = (type.declaration as? KSClassDeclaration)
42 | ?.superTypes
43 | ?.map { it.resolve() }
44 | ?.toSet()
45 | ?: emptySet()
46 |
47 | val superTypes = rootSuperTypes
48 | .map {
49 | collectTypes(it, depth + 1)
50 | }
51 | .flatten()
52 |
53 | return setOf(ActionSuperType(type.toTypeName(), depth)) + superTypes
54 | }
55 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/kapt/actions/KaptActionModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.kapt.actions
18 |
19 | import com.squareup.kotlinpoet.ANY
20 | import com.squareup.kotlinpoet.ClassName
21 | import com.squareup.kotlinpoet.CodeBlock
22 | import com.squareup.kotlinpoet.asTypeName
23 | import mini.processor.common.actions.ActionModel
24 | import mini.processor.common.actions.ActionSuperType
25 | import mini.processor.kapt.typeUtils
26 | import javax.lang.model.element.Element
27 | import javax.lang.model.type.TypeMirror
28 |
29 | class KaptActionModel(element: Element) : ActionModel {
30 | private val type = element.asType()
31 | private val javaObject = ClassName.bestGuess("java.lang.Object")
32 |
33 | override val typeName = type.asTypeName()
34 | private val superTypes = collectTypes(type)
35 | .sortedBy { it.depth }
36 | .filter { it.typeName != javaObject }
37 | .map { it.typeName }
38 | .plus(ANY)
39 |
40 | override fun listOfSupertypesCodeBlock(): CodeBlock {
41 | val format = superTypes.joinToString(",\n") { "%T::class" }
42 | val args = superTypes.toTypedArray()
43 | return CodeBlock.of("listOf($format)", *args)
44 | }
45 |
46 | private fun collectTypes(mirror: TypeMirror, depth: Int = 0): Set {
47 | //We want to add by depth
48 | val superTypes = typeUtils.directSupertypes(mirror).toSet()
49 | .map { collectTypes(it, depth + 1) }
50 | .flatten()
51 | return setOf(ActionSuperType(mirror.asTypeName(), depth)) + superTypes
52 | }
53 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/kapt/MiniAnnotationProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.kapt;
18 |
19 | import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
20 | import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
21 |
22 | import java.util.Set;
23 |
24 | import javax.annotation.processing.AbstractProcessor;
25 | import javax.annotation.processing.ProcessingEnvironment;
26 | import javax.annotation.processing.RoundEnvironment;
27 | import javax.annotation.processing.SupportedOptions;
28 | import javax.lang.model.SourceVersion;
29 | import javax.lang.model.element.TypeElement;
30 |
31 | /**
32 | * Dummy Java wrapper that delegates to Kotlin one
33 | */
34 | @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.AGGREGATING)
35 | @SupportedOptions("kapt.kotlin.generated")
36 | public class MiniAnnotationProcessor extends AbstractProcessor {
37 |
38 | private final Processor processor = new Processor();
39 |
40 | @Override
41 | public synchronized void init(ProcessingEnvironment processingEnvironment) {
42 | super.init(processingEnvironment);
43 | processor.init(processingEnvironment);
44 | }
45 |
46 | @Override
47 | public Set getSupportedAnnotationTypes() {
48 | return processor.getSupportedAnnotationTypes();
49 | }
50 |
51 | @Override
52 | public SourceVersion getSupportedSourceVersion() {
53 | return processor.getSupportedSourceVersion();
54 | }
55 |
56 | @Override
57 | public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
58 | return processor.process(roundEnvironment);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/mini/android/sample/SampleScreen.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.sample
18 |
19 | import androidx.compose.foundation.layout.Arrangement
20 | import androidx.compose.foundation.layout.Box
21 | import androidx.compose.foundation.layout.Column
22 | import androidx.compose.foundation.layout.fillMaxSize
23 | import androidx.compose.foundation.layout.fillMaxWidth
24 | import androidx.compose.foundation.layout.height
25 | import androidx.compose.foundation.layout.padding
26 | import androidx.compose.material3.Button
27 | import androidx.compose.material3.CircularProgressIndicator
28 | import androidx.compose.material3.Text
29 | import androidx.compose.runtime.Composable
30 | import androidx.compose.ui.Alignment
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.text.style.TextAlign
33 | import androidx.compose.ui.unit.dp
34 |
35 | @Composable
36 | fun SampleScreen(
37 | modifier: Modifier = Modifier,
38 | text: String,
39 | isLoading: Boolean,
40 | onStartSampleClicked: () -> Unit
41 | ) {
42 | Column(
43 | modifier = modifier.fillMaxSize().padding(16.dp),
44 | horizontalAlignment = Alignment.CenterHorizontally,
45 | verticalArrangement = Arrangement.Center
46 | ) {
47 | Text(
48 | modifier = Modifier.fillMaxWidth(),
49 | textAlign = TextAlign.Center,
50 | text = text
51 | )
52 |
53 | Box(modifier = Modifier.fillMaxWidth().height(60.dp), contentAlignment = Alignment.Center) {
54 | if (!isLoading) {
55 | Button(onClick = onStartSampleClicked) {
56 | Text("Start sample")
57 | }
58 | }
59 |
60 | if (isLoading) {
61 | CircularProgressIndicator()
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/actions/ActionTypesGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common.actions
18 |
19 | import com.squareup.kotlinpoet.*
20 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
21 | import mini.Mini
22 | import kotlin.reflect.KClass
23 |
24 | class ActionTypesGenerator(private val delegate: ActionTypesGeneratorDelegate) {
25 | fun generate(container: TypeSpec.Builder) {
26 | val actionModels = delegate.provideModels()
27 | container.apply {
28 | val anyClassTypeName = KClass::class.asTypeName().parameterizedBy(STAR)
29 | val listTypeName = List::class.asTypeName().parameterizedBy(anyClassTypeName)
30 | val mapType = Map::class
31 | .asClassName()
32 | .parameterizedBy(anyClassTypeName, listTypeName)
33 |
34 | val prop = PropertySpec.builder(Mini::actionTypes.name, mapType)
35 | .addModifiers(KModifier.OVERRIDE)
36 | .initializer(
37 | CodeBlock.builder()
38 | .add("mapOf(\n⇥")
39 | .apply {
40 | actionModels.forEach { actionModel ->
41 | val comma = if (actionModel != actionModels.last()) "," else ""
42 | add("«")
43 | add("%T::class to ", actionModel.typeName)
44 | add(actionModel.listOfSupertypesCodeBlock())
45 | add(comma)
46 | add("\n»")
47 | }
48 | }
49 | .add("⇤)")
50 | .build())
51 | addProperty(prop.build())
52 | }.build()
53 | }
54 | }
--------------------------------------------------------------------------------
/mini-kodein-android-compose/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.android.library)
19 | alias(libs.plugins.kotlin.android)
20 | alias(libs.plugins.convention.androidLib)
21 | }
22 |
23 | android {
24 | namespace = "mini.kodein.android.compose"
25 |
26 | compileSdk = libs.versions.android.compileSdk.get().toInt()
27 | buildToolsVersion = libs.versions.android.buildTools.get()
28 |
29 | defaultConfig {
30 | minSdk = libs.versions.android.minSdk.get().toInt()
31 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
32 | consumerProguardFiles("mini-kodein-android-compose.pro")
33 | }
34 |
35 | buildTypes {
36 | release {
37 | isMinifyEnabled = false
38 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
39 | }
40 | }
41 |
42 | compileOptions {
43 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
44 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
45 | }
46 | }
47 |
48 | tasks.withType().configureEach {
49 | kotlinOptions {
50 | jvmTarget = libs.versions.java.sdk.get()
51 | }
52 | }
53 |
54 | java {
55 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
56 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
57 | }
58 |
59 | kotlin {
60 | jvmToolchain(libs.versions.java.sdk.get().toInt())
61 | }
62 |
63 | dependencies {
64 | api(project(":mini-kodein-android"))
65 |
66 | api(libs.compose.navigation)
67 |
68 | testImplementation(libs.junit)
69 | androidTestImplementation(libs.androidx.test.runner)
70 | androidTestImplementation(libs.espresso)
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/mini-android/src/main/java/mini/android/FluxFragment.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android
18 |
19 | import android.os.Bundle
20 | import androidx.fragment.app.Fragment
21 | import androidx.lifecycle.lifecycleScope
22 | import kotlinx.coroutines.CoroutineScope
23 | import kotlinx.coroutines.flow.Flow
24 | import kotlinx.coroutines.flow.launchIn
25 | import kotlinx.coroutines.launch
26 | import mini.CloseableTracker
27 | import mini.DefaultCloseableTracker
28 | import kotlin.coroutines.CoroutineContext
29 |
30 | abstract class FluxFragment : Fragment(),
31 | CloseableTracker by DefaultCloseableTracker(),
32 | CoroutineScope {
33 |
34 | override val coroutineContext: CoroutineContext
35 | get() = lifecycleScope.coroutineContext
36 |
37 | override fun onCreate(savedInstanceState: Bundle?) {
38 | super.onCreate(savedInstanceState)
39 | lifecycleScope.launch { whenCreated(savedInstanceState) }
40 | }
41 |
42 | override fun onResume() {
43 | super.onResume()
44 | lifecycleScope.launch { whenResumed() }
45 | }
46 |
47 | override fun onPause() {
48 | super.onPause()
49 | lifecycleScope.launch { whenPaused() }
50 | }
51 |
52 | override fun onStop() {
53 | super.onStop()
54 | lifecycleScope.launch { whenStopped() }
55 | }
56 |
57 | override fun onDestroy() {
58 | lifecycleScope.launch { whenDestroyed() }
59 | close()
60 | super.onDestroy()
61 | }
62 |
63 | fun Flow.launchOnUi() {
64 | launchIn(lifecycleScope)
65 | }
66 |
67 | protected open suspend fun whenCreated(savedInstanceState: Bundle?) = Unit
68 | protected open suspend fun whenResumed() = Unit
69 | protected open suspend fun whenPaused() = Unit
70 | protected open suspend fun whenStopped() = Unit
71 | protected open suspend fun whenDestroyed() = Unit
72 | }
--------------------------------------------------------------------------------
/mini-android/src/main/java/mini/android/FluxActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android
18 |
19 | import android.os.Bundle
20 | import androidx.appcompat.app.AppCompatActivity
21 | import androidx.lifecycle.lifecycleScope
22 | import kotlinx.coroutines.CoroutineScope
23 | import kotlinx.coroutines.flow.Flow
24 | import kotlinx.coroutines.flow.launchIn
25 | import kotlinx.coroutines.launch
26 | import mini.CloseableTracker
27 | import mini.DefaultCloseableTracker
28 | import kotlin.coroutines.CoroutineContext
29 |
30 | abstract class FluxActivity : AppCompatActivity(),
31 | CloseableTracker by DefaultCloseableTracker(),
32 | CoroutineScope {
33 |
34 | override val coroutineContext: CoroutineContext
35 | get() = lifecycleScope.coroutineContext
36 |
37 | override fun onCreate(savedInstanceState: Bundle?) {
38 | super.onCreate(savedInstanceState)
39 | lifecycleScope.launch { whenCreated(savedInstanceState) }
40 | }
41 |
42 | override fun onResume() {
43 | super.onResume()
44 | lifecycleScope.launch { whenResumed() }
45 | }
46 |
47 | override fun onPause() {
48 | super.onPause()
49 | lifecycleScope.launch { whenPaused() }
50 | }
51 |
52 | override fun onStop() {
53 | super.onStop()
54 | lifecycleScope.launch { whenStopped() }
55 | }
56 |
57 | override fun onDestroy() {
58 | lifecycleScope.launch { whenDestroyed() }
59 | close()
60 | super.onDestroy()
61 | }
62 |
63 | fun Flow.launchInLifecycleScope() {
64 | launchIn(lifecycleScope)
65 | }
66 |
67 | protected open suspend fun whenCreated(savedInstanceState: Bundle?) = Unit
68 | protected open suspend fun whenResumed() = Unit
69 | protected open suspend fun whenPaused() = Unit
70 | protected open suspend fun whenStopped() = Unit
71 | protected open suspend fun whenDestroyed() = Unit
72 |
73 | }
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/TestDispatcher.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import kotlin.reflect.KClass
22 | import kotlin.reflect.jvm.jvmErasure
23 |
24 | fun newTestDispatcher(): Dispatcher {
25 | return Dispatcher().apply {
26 | actionTypeMap = newReflectiveMap()
27 | }
28 | }
29 |
30 | private fun reflectActionTypes(type: KClass<*>, depth: Int = 0): List {
31 | return type.supertypes
32 | .asSequence()
33 | .map { (it.jvmErasure.java as Class<*>).kotlin }
34 | .map { reflectActionTypes(it, depth + 1) }
35 | .flatten()
36 | .plus(ReflectedType(type, depth))
37 | .toList()
38 | }
39 |
40 | private class ReflectedType(val clazz: KClass<*>, val depth: Int)
41 |
42 | private fun newReflectiveMap(): Map, List>> {
43 | return object : Map, List>> {
44 | private val genericTypes = listOf(Object::class)
45 | private val map = HashMap, List>>()
46 | override val entries: Set, List>>> = map.entries
47 | override val keys: Set> = map.keys
48 | override val size: Int = map.size
49 | override val values: Collection>> = map.values
50 | override fun containsKey(key: KClass<*>): Boolean = map.containsKey(key)
51 | override fun containsValue(value: List>): Boolean = map.containsValue(value)
52 | override fun isEmpty(): Boolean = map.isEmpty()
53 | override fun get(key: KClass<*>): List> {
54 | return map.getOrPut(key) {
55 | reflectActionTypes(key)
56 | .asSequence()
57 | .sortedBy { it.depth }
58 | .map { it.clazz }
59 | .filter { it !in genericTypes }
60 | .toList()
61 | }
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/mini-android/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.android.library)
19 | alias(libs.plugins.kotlin.android)
20 | alias(libs.plugins.convention.androidLib)
21 | }
22 |
23 | android {
24 | namespace = "mini.android"
25 |
26 | compileSdk = libs.versions.android.compileSdk.get().toInt()
27 | buildToolsVersion = libs.versions.android.buildTools.get()
28 |
29 | defaultConfig {
30 | minSdk = libs.versions.android.minSdk.get().toInt()
31 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
32 | consumerProguardFiles("mini-android.pro")
33 | }
34 |
35 | buildTypes {
36 | release {
37 | isMinifyEnabled = false
38 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
39 | }
40 | }
41 |
42 | compileOptions {
43 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
44 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
45 | }
46 | }
47 |
48 | tasks.withType().configureEach {
49 | kotlinOptions {
50 | jvmTarget = libs.versions.java.sdk.get()
51 | }
52 | }
53 |
54 | java {
55 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
56 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
57 | }
58 |
59 | kotlin {
60 | jvmToolchain(libs.versions.java.sdk.get().toInt())
61 | }
62 |
63 | dependencies {
64 | api(project(":mini-common"))
65 |
66 | api(libs.kotlinx.coroutines.android)
67 | api(libs.androidx.appcompat)
68 | api(libs.androidx.activity)
69 | api(libs.androidx.fragment)
70 |
71 | api(libs.bundles.androidx.lifecycle)
72 |
73 | testImplementation(libs.junit)
74 | androidTestImplementation(libs.androidx.test.runner)
75 | androidTestImplementation(libs.espresso)
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/ksp/MiniSymbolProcessor.kt:
--------------------------------------------------------------------------------
1 | package mini.processor.ksp
2 |
3 | import com.google.devtools.ksp.processing.CodeGenerator
4 | import com.google.devtools.ksp.processing.Resolver
5 | import com.google.devtools.ksp.processing.SymbolProcessor
6 | import com.google.devtools.ksp.symbol.KSAnnotated
7 | import com.google.devtools.ksp.symbol.KSClassDeclaration
8 | import com.squareup.kotlinpoet.ksp.writeTo
9 | import mini.Action
10 | import mini.Reducer
11 | import mini.processor.common.ProcessorException
12 | import mini.processor.common.actions.ActionTypesGenerator
13 | import mini.processor.common.getContainerBuilders
14 | import mini.processor.common.reducers.ReducersGenerator
15 | import mini.processor.kapt.stackTraceString
16 | import mini.processor.ksp.actions.KspActionTypesGeneratorDelegate
17 | import mini.processor.ksp.reducers.KspReducersGeneratorDelegate
18 |
19 | class MiniSymbolProcessor(
20 | private val codeGenerator: CodeGenerator
21 | ) : SymbolProcessor {
22 | override fun process(resolver: Resolver): List {
23 | // Get elements with the @Reducer or @Action annotations
24 | val actionSymbols = resolver.getSymbolsWithAnnotation(Action::class.java.canonicalName)
25 | val reducerSymbols = resolver.getSymbolsWithAnnotation(Reducer::class.java.canonicalName)
26 |
27 | // Collect the files that contain the symbols, we will use this to set the originating files
28 | // for the generated code and incremental processing.
29 | val originatingKsFiles = (actionSymbols + reducerSymbols).
30 | filterIsInstance()
31 | .mapNotNull { it.containingFile }
32 | .distinct()
33 | .toList()
34 |
35 | if (!actionSymbols.iterator().hasNext()) return emptyList()
36 |
37 | val (containerFile, container) = getContainerBuilders()
38 |
39 | try {
40 | ActionTypesGenerator(KspActionTypesGeneratorDelegate(actionSymbols)).generate(container)
41 | ReducersGenerator(KspReducersGeneratorDelegate(reducerSymbols)).generate(container)
42 | } catch (e: Throwable) {
43 | if (e !is ProcessorException) {
44 | kspLogError(
45 | "Compiler crashed, open an issue please!\n" +
46 | " ${e.stackTraceString()}"
47 | )
48 | }
49 | }
50 |
51 | containerFile
52 | .addType(container.build())
53 | .build()
54 | .writeTo(
55 | codeGenerator = codeGenerator,
56 | aggregating = true,
57 | originatingKSFiles = originatingKsFiles
58 | )
59 |
60 | return emptyList()
61 | }
62 | }
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/Store.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import java.io.Closeable
22 | import java.util.concurrent.CopyOnWriteArrayList
23 |
24 | /**
25 | * Basic state holder.
26 | */
27 | abstract class Store : Closeable,
28 | StateContainer,
29 | CloseableTracker by DefaultCloseableTracker() {
30 |
31 | class StoreSubscription internal constructor(
32 | private val store: Store,
33 | private val fn: Any
34 | ) : Closeable {
35 | override fun close() {
36 | store.listeners.remove(fn)
37 | }
38 | }
39 |
40 | private var _state: Any? = StateContainer.Companion.NoState
41 | private val listeners = CopyOnWriteArrayList<(S) -> Unit>()
42 |
43 | /**
44 | * Initialize the store after dependency injection is complete.
45 | */
46 | open fun initialize() {
47 | //No-op
48 | }
49 |
50 | /**
51 | * Set new state and notify listeners, only callable from the main thread.
52 | */
53 | override fun setState(newState: S) {
54 | assertOnUiThread()
55 | performStateChange(newState)
56 | }
57 |
58 | override fun subscribe(hotStart: Boolean, fn: (S) -> Unit): Closeable {
59 | listeners.add(fn)
60 | if (hotStart) fn(state)
61 | return StoreSubscription(this, fn)
62 | }
63 |
64 | override val state: S
65 | get() {
66 | if (_state === StateContainer.Companion.NoState) {
67 | synchronized(this) {
68 | if (_state === StateContainer.Companion.NoState) {
69 | _state = initialState()
70 | }
71 | }
72 | }
73 | @Suppress("UNCHECKED_CAST")
74 | return _state as S
75 | }
76 |
77 | private fun performStateChange(newState: S) {
78 | //State mutation should to happen on UI thread
79 | if (_state != newState) {
80 | _state = newState
81 | listeners.forEach {
82 | it(newState)
83 | }
84 | }
85 | }
86 |
87 | override fun close() {
88 | listeners.clear() //Remove all listeners
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/common/reducers/ReducersGenerator.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2022 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.processor.common.reducers
18 |
19 | import com.squareup.kotlinpoet.*
20 | import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
21 | import mini.CompositeCloseable
22 | import mini.Dispatcher
23 | import mini.StateContainer
24 | import java.io.Closeable
25 |
26 | class ReducersGenerator(private val delegate: ReducersGeneratorDelegate) {
27 | fun generate(container: TypeSpec.Builder) {
28 | val reducers = delegate.provideModels().groupBy { it.container.typeName }
29 |
30 | val whenBlock = CodeBlock.builder()
31 | .addStatement("val c = %T()", CompositeCloseable::class)
32 | .addStatement("when (container) {").indent()
33 | .apply {
34 | reducers.forEach { (containerName, reducerFunctions) ->
35 | addStatement("is %T -> {", containerName).indent()
36 | reducerFunctions.forEach { function ->
37 | add(
38 | "c.add(dispatcher.subscribe<%T>(priority=%L) { action -> ",
39 | function.actionTypeName,
40 | function.priority
41 | )
42 | add(function.generateCallBlock("container", "action"))
43 | addStatement("})")
44 | }
45 | unindent().addStatement("}")
46 | }
47 | }
48 | .unindent()
49 | .addStatement("}") //Close when
50 | .addStatement("return c")
51 | .build()
52 |
53 | val typeParam = TypeVariableName("T", ClassName("mini", "State"))
54 | val oneParam = StateContainer::class.asTypeName().parameterizedBy(typeParam)
55 |
56 | val registerOneFn = FunSpec.builder("subscribe")
57 | .addModifiers(KModifier.OVERRIDE)
58 | .addTypeVariable(typeParam)
59 | .addParameter("dispatcher", Dispatcher::class)
60 | .addParameter("container", oneParam)
61 | .returns(Closeable::class)
62 | .addCode(whenBlock)
63 | .build()
64 |
65 | container.addFunction(registerOneFn)
66 | }
67 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/kapt/Processor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.processor.kapt
20 |
21 | import mini.Action
22 | import mini.Reducer
23 | import mini.processor.common.ProcessorException
24 | import mini.processor.common.actions.ActionTypesGenerator
25 | import mini.processor.common.getContainerBuilders
26 | import mini.processor.common.reducers.ReducersGenerator
27 | import mini.processor.kapt.actions.KaptActionTypesGeneratorDelegate
28 | import mini.processor.kapt.reducers.KaptReducersGeneratorDelegate
29 | import javax.annotation.processing.ProcessingEnvironment
30 | import javax.annotation.processing.RoundEnvironment
31 | import javax.lang.model.SourceVersion
32 |
33 | class Processor {
34 |
35 | val supportedAnnotationTypes: MutableSet = mutableSetOf(
36 | Reducer::class.java, Action::class.java
37 | )
38 | .map { it.canonicalName }.toMutableSet()
39 | val supportedSourceVersion: SourceVersion = SourceVersion.RELEASE_8
40 |
41 | fun init(environment: ProcessingEnvironment) {
42 | env = environment
43 | typeUtils = env.typeUtils
44 | elementUtils = env.elementUtils
45 | }
46 |
47 | fun process(roundEnv: RoundEnvironment): Boolean {
48 |
49 | val roundActions = roundEnv.getElementsAnnotatedWith(Action::class.java)
50 | val roundReducers = roundEnv.getElementsAnnotatedWith(Reducer::class.java)
51 |
52 | if (roundActions.isEmpty()) return false
53 |
54 | val (containerFile, container) = getContainerBuilders()
55 |
56 | try {
57 | ActionTypesGenerator(KaptActionTypesGeneratorDelegate(roundActions)).generate(container)
58 | ReducersGenerator(KaptReducersGeneratorDelegate(roundReducers)).generate(container)
59 | } catch (e: Throwable) {
60 | if (e !is ProcessorException) {
61 | kaptLogError(
62 | "Compiler crashed, open an issue please!\n" +
63 | " ${e.stackTraceString()}"
64 | )
65 | }
66 | }
67 |
68 | containerFile
69 | .addType(container.build())
70 | .build()
71 | .writeToFile(sourceElements = ((roundActions + roundReducers).toTypedArray()))
72 |
73 | return true
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/StateContainer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import org.jetbrains.annotations.TestOnly
20 | import java.io.Closeable
21 | import java.lang.reflect.ParameterizedType
22 |
23 | /**
24 | * Common interface for state containers.
25 | */
26 | interface StateContainer {
27 |
28 | companion object {
29 | /**
30 | * Token to mark a state as not initialized.
31 | */
32 | object NoState : State
33 | }
34 |
35 | val state: S
36 |
37 | fun setState(newState: S)
38 |
39 | /**
40 | * Register a observer to state changes.
41 | *
42 | * @return [Closeable] to cancel the subscription.
43 | */
44 | fun subscribe(hotStart: Boolean = true, fn: (S) -> Unit): Closeable
45 |
46 | /**
47 | * The initial state of the container. By default it will invoke the primary constructor
48 | * of the State type parameter. If this constructor is not accessible provide your own
49 | * implementation of this method.
50 | */
51 | @Suppress("UNCHECKED_CAST")
52 | fun initialState(): S {
53 | val type = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]
54 | as Class
55 | try {
56 | val constructor = type.getDeclaredConstructor()
57 | constructor.isAccessible = true
58 | return constructor.newInstance()
59 | } catch (e: Exception) {
60 | throw RuntimeException("Missing default no-args constructor for the state $type", e)
61 | }
62 | }
63 |
64 | /**
65 | * Test only method, don't use in app code.
66 | * Will force state change on UI so it can be called from
67 | * espresso thread.
68 | */
69 | @TestOnly
70 | fun setTestState(s: S) {
71 | if (isAndroid) {
72 | onUiSync {
73 | setState(s)
74 | }
75 | } else {
76 | setState(s)
77 | }
78 | }
79 |
80 | /**
81 | * Test only method, don't use in app code.
82 | * Will force state change on UI to the initial state.
83 | */
84 | @TestOnly
85 | fun resetState() {
86 | if (isAndroid) {
87 | onUiSync {
88 | setState(initialState())
89 | }
90 | } else {
91 | setState(initialState())
92 | }
93 | }
94 | }
95 |
96 |
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/DispatcherTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import org.amshove.kluent.`should be equal to`
22 | import org.junit.Test
23 |
24 | class DispatcherTest {
25 |
26 | @Test
27 | fun `subscriptions are added`() {
28 | val dispatcher = newTestDispatcher()
29 | var called = 0
30 | dispatcher.subscribe {
31 | called++
32 | }
33 | dispatcher.dispatchBlocking(TestAction())
34 | called `should be equal to` 1
35 | }
36 |
37 | @Test
38 | fun `order is respected for same priority`() {
39 | val dispatcher = newTestDispatcher()
40 | val calls = ArrayList()
41 | dispatcher.subscribe {
42 | calls.add(0)
43 | }
44 | dispatcher.subscribe {
45 | calls.add(1)
46 | }
47 | dispatcher.dispatchBlocking(TestAction())
48 | calls[0] `should be equal to` 0
49 | calls[1] `should be equal to` 1
50 | }
51 |
52 | @Test
53 | fun `order is respected for different priority`() {
54 | val dispatcher = newTestDispatcher()
55 | val calls = ArrayList()
56 | dispatcher.subscribe(priority = 10) {
57 | calls.add(0)
58 | }
59 | dispatcher.subscribe(priority = 0) {
60 | calls.add(1)
61 | }
62 | dispatcher.dispatchBlocking(TestAction())
63 | calls[0] `should be equal to` 1
64 | calls[1] `should be equal to` 0
65 | }
66 |
67 | @Test
68 | fun `disposing registration removes subscription`() {
69 | val dispatcher = newTestDispatcher()
70 | var called = 0
71 | dispatcher.subscribe {
72 | called++
73 | }.close()
74 | dispatcher.dispatchBlocking(TestAction())
75 | called `should be equal to` 0
76 | }
77 |
78 | @Test
79 | fun `interceptors are called`() {
80 | val dispatcher = newTestDispatcher()
81 | var called = 0
82 | val interceptor = object : Middleware {
83 | override suspend fun intercept(action: Any, chain: Chain): Any {
84 | called++
85 | return chain.proceed(action)
86 | }
87 | }
88 | dispatcher.addMiddleware(interceptor)
89 | dispatcher.dispatchBlocking(TestAction())
90 | called `should be equal to` 1
91 | }
92 | }
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | plugins {
18 | alias(libs.plugins.android.application)
19 | alias(libs.plugins.kotlin.android)
20 | // alias(libs.plugins.kotlin.kapt)
21 | alias(libs.plugins.kotlin.ksp)
22 | alias(libs.plugins.convention.androidApp)
23 | }
24 |
25 | android {
26 | namespace = "mini.android.sample"
27 |
28 | compileSdk = libs.versions.android.compileSdk.get().toInt()
29 | buildToolsVersion = libs.versions.android.buildTools.get()
30 |
31 | defaultConfig {
32 | applicationId = "mini.android.sample"
33 | minSdk = libs.versions.android.minSdk.get().toInt()
34 | targetSdk = libs.versions.android.targetSdk.get().toInt()
35 | multiDexEnabled = true
36 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
37 | }
38 |
39 | buildFeatures {
40 | compose = true
41 | viewBinding = true
42 | }
43 |
44 | composeOptions {
45 | kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
46 | }
47 |
48 | buildTypes {
49 | release {
50 | isMinifyEnabled = false
51 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
52 | }
53 | }
54 |
55 | compileOptions {
56 | sourceCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
57 | targetCompatibility = JavaVersion.toVersion(libs.versions.java.sdk.get())
58 | }
59 |
60 | kotlinOptions {
61 | jvmTarget = libs.versions.java.sdk.get()
62 | }
63 |
64 | lint {
65 | abortOnError = false
66 | }
67 | }
68 |
69 | dependencies {
70 | implementation(project(":mini-android"))
71 | implementation(project(":mini-kodein-android"))
72 |
73 | // kapt(project(":mini-processor"))
74 | ksp(project(":mini-processor"))
75 |
76 | // Kotlin
77 | implementation(libs.kotlin.stdlib)
78 | implementation(libs.kotlin.reflect)
79 |
80 | // Coroutines
81 | implementation(libs.kotlinx.coroutines.core)
82 |
83 | // Support
84 | implementation(libs.bundles.androidx)
85 | implementation(libs.bundles.compose)
86 | implementation(libs.androidx.activity)
87 | implementation(libs.bundles.androidx.lifecycle)
88 |
89 | // Test
90 | testImplementation(libs.junit)
91 | androidTestImplementation(libs.androidx.test.runner)
92 | androidTestImplementation(libs.espresso)
93 | androidTestImplementation(libs.androidx.test.junit)
94 | }
--------------------------------------------------------------------------------
/convention-plugins/src/main/kotlin/mini/android/plugins/extensions/MavenPomExtensions.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.plugins.extensions
18 |
19 | import org.gradle.api.publish.maven.internal.publication.DefaultMavenPom
20 |
21 | fun DefaultMavenPom.setMetadata(artifactName: String, artifactVersion: String) {
22 | name.set("Mini ($artifactName)")
23 | description.set("Mini is a minimal Flux architecture written in Kotlin that also adds a mix of useful features to build UIs fast.")
24 | url.set("https://github.com/hyperdevs-team/mini-kotlin/releases/tag/$artifactVersion")
25 | //version.set(miniVersion)
26 | inceptionYear.set("2017")
27 |
28 | licenses {
29 | license {
30 | name.set("The Apache License, Version 2.0")
31 | url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
32 | }
33 | }
34 |
35 | organization {
36 | name.set("HyperDevs")
37 | url.set("https://github.com/hyperdevs-team")
38 | }
39 |
40 | issueManagement {
41 | system.set("GitHub Issues")
42 | url.set("https://github.com/hyperdevs-team/mini-kotlin/issues")
43 | }
44 |
45 | scm {
46 | connection.set("scm:git:git@github.com:hyperdevs-team/mini-kotlin.git")
47 | url.set("https://github.com/hyperdevs-team/mini-kotlin.git")
48 | }
49 |
50 | developers {
51 | developer {
52 | name.set("Estefanía Sarasola Elvira")
53 | id.set("yamidragut")
54 | url.set("https://github.com/yamidragut")
55 | roles.set(listOf("Maintainer"))
56 | }
57 |
58 | developer {
59 | name.set("Sara Lucía Pérez")
60 | id.set("Babelia13")
61 | url.set("https://github.com/Babelia13")
62 | roles.set(listOf("Maintainer"))
63 | }
64 |
65 | developer {
66 | name.set("Adrián García")
67 | id.set("adriangl")
68 | url.set("https://github.com/adriangl")
69 | roles.set(listOf("Maintainer"))
70 | }
71 |
72 | developer {
73 | name.set("Francisco García Sierra")
74 | id.set("FrangSierra")
75 | url.set("https://github.com/FrangSierra")
76 | roles.set(listOf("Maintainer", "Initial Work"))
77 | }
78 |
79 | developer {
80 | name.set("Pablo Orgaz")
81 | id.set("pabloogc")
82 | url.set("https://github.com/pabloogc")
83 | roles.set(listOf("Initial Work"))
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/Mini.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import java.io.Closeable
22 | import kotlin.reflect.KClass
23 |
24 | const val DISPATCHER_FACTORY_CLASS_NAME = "mini.codegen.Mini_Generated"
25 |
26 | abstract class Mini {
27 |
28 | companion object {
29 |
30 | private val miniInstance: Mini by lazy {
31 | try {
32 | Class.forName(DISPATCHER_FACTORY_CLASS_NAME).getField("INSTANCE").get(null) as Mini
33 | } catch (ex: Throwable) {
34 | throw ClassNotFoundException("Failed to load generated class $DISPATCHER_FACTORY_CLASS_NAME, " +
35 | "most likely the annotation processor did not run, add it as dependency to the project", ex)
36 | }
37 | }
38 |
39 | /**
40 | * Generate all subscriptions from @[Reducer] annotated methods and bundle
41 | * into a single Closeable.
42 | */
43 | fun link(dispatcher: Dispatcher, container: StateContainer<*>): Closeable {
44 | ensureDispatcherInitialized(dispatcher)
45 | return miniInstance.subscribe(dispatcher, container)
46 | }
47 |
48 | /**
49 | * Generate all subscriptions from @[Reducer] annotated methods and bundle
50 | * into a single Closeable.
51 | */
52 | fun link(dispatcher: Dispatcher, containers: Iterable>): Closeable {
53 | ensureDispatcherInitialized(dispatcher)
54 | return miniInstance.subscribe(dispatcher, containers)
55 | }
56 |
57 | private fun ensureDispatcherInitialized(dispatcher: Dispatcher) {
58 | if (dispatcher.actionTypeMap.isEmpty()) {
59 | dispatcher.actionTypeMap = miniInstance.actionTypes
60 | }
61 | }
62 |
63 | }
64 |
65 | /**
66 | * All the types an action can be subscribed as.
67 | */
68 | abstract val actionTypes: Map, List>>
69 |
70 | /**
71 | * Link all [Reducer] functions present in the store to the dispatcher.
72 | */
73 | protected abstract fun subscribe(dispatcher: Dispatcher,
74 | container: StateContainer): Closeable
75 |
76 | /**
77 | * Link all [Reducer] functions present in the store to the dispatcher.
78 | */
79 | protected fun subscribe(dispatcher: Dispatcher, containers: Iterable>): Closeable {
80 | val c = CompositeCloseable()
81 | containers.forEach { container ->
82 | c.add(subscribe(dispatcher, container))
83 | }
84 | return c
85 | }
86 | }
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/ResourceTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import org.amshove.kluent.`should be equal to`
22 | import org.amshove.kluent.`should be null`
23 | import org.amshove.kluent.`should equal`
24 | import org.amshove.kluent.`should not be null`
25 | import org.junit.Before
26 | import org.junit.Test
27 |
28 | class ResourceTest {
29 |
30 | private var successValue: Any? = null
31 | private var errorValue: Any? = null
32 | private var loadingValue: Any? = null
33 | private var emptyValue: Any? = null
34 |
35 | @Before
36 | fun before() {
37 | successValue = null
38 | errorValue = null
39 | loadingValue = null
40 | emptyValue = null
41 | }
42 |
43 | private fun check(resource: Resource) {
44 | var called = 0
45 | resource
46 | .onSuccess {
47 | called++
48 | successValue = it
49 | }.onFailure {
50 | called++
51 | errorValue = it
52 | }.onLoading {
53 | called++
54 | loadingValue = it
55 | }.onEmpty {
56 | called++
57 | emptyValue = true
58 | }
59 | called `should be equal to` 1
60 | }
61 |
62 | @Test
63 | fun `success calls`() {
64 | check(Resource.success("abc"))
65 | successValue `should equal` "abc"
66 | }
67 |
68 | @Test
69 | fun isEmpty() {
70 | check(Resource.empty())
71 | emptyValue `should equal` true
72 | }
73 |
74 | @Test
75 | fun isFailure() {
76 | val ex = RuntimeException("ABC")
77 | check(Resource.failure(ex))
78 | errorValue `should equal` ex
79 | }
80 |
81 | @Test
82 | fun isLoading() {
83 | check(Resource.loading("abc"))
84 | loadingValue `should equal` "abc"
85 | }
86 |
87 | @Test
88 | fun getOrNull() {
89 | Resource.empty().getOrNull().`should be null`()
90 | Resource.success("abc").getOrNull().`should not be null`()
91 | }
92 |
93 | @Test
94 | fun exceptionOrNull() {
95 | Resource.failure(RuntimeException()).exceptionOrNull().`should not be null`()
96 | Resource.success("abc").exceptionOrNull().`should be null`()
97 | }
98 |
99 | @Test
100 | fun map() {
101 | Resource.success("abc")
102 | .map { 0 }
103 | .getOrNull()?.`should be equal to`(0)
104 |
105 | Resource.failure()
106 | .map { 0 }
107 | .getOrNull()?.`should be null`()
108 | }
109 | }
--------------------------------------------------------------------------------
/mini-android/src/main/java/mini/android/FluxStoreViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android
18 |
19 | import androidx.lifecycle.SavedStateHandle
20 | import androidx.lifecycle.ViewModel
21 | import mini.CloseableTracker
22 | import mini.DefaultCloseableTracker
23 | import mini.State
24 | import mini.StateContainer
25 | import mini.assertOnUiThread
26 | import java.io.Closeable
27 | import java.util.concurrent.CopyOnWriteArrayList
28 |
29 | abstract class FluxStoreViewModel(
30 | val savedStateHandle: SavedStateHandle) :
31 | ViewModel(),
32 | StateContainer,
33 | CloseableTracker by DefaultCloseableTracker() {
34 |
35 | class ViewModelSubscription internal constructor(private val vm: FluxStoreViewModel<*>,
36 | private val fn: Any) : Closeable {
37 | override fun close() {
38 | vm.listeners.remove(fn)
39 | }
40 | }
41 |
42 | private var _state: Any? = StateContainer.Companion.NoState
43 | private val listeners = CopyOnWriteArrayList<(S) -> Unit>()
44 |
45 | override val state: S
46 | get() {
47 | if (_state === StateContainer.Companion.NoState) {
48 | synchronized(this) {
49 | if (_state === StateContainer.Companion.NoState) {
50 | _state = restoreState(savedStateHandle) ?: initialState()
51 | }
52 | }
53 | }
54 | @Suppress("UNCHECKED_CAST")
55 | return _state as S
56 | }
57 |
58 | override fun setState(newState: S) {
59 | assertOnUiThread()
60 | performStateChange(newState)
61 | }
62 |
63 | private fun performStateChange(newState: S) {
64 | if (_state != newState) {
65 | _state = newState
66 | saveState(newState, savedStateHandle)
67 | listeners.forEach {
68 | it(newState)
69 | }
70 | }
71 | }
72 |
73 | /**
74 | * Persist the state, no-op by default.
75 | *
76 | * ```handle.set("state", state)```
77 | */
78 | open fun saveState(state: S, handle: SavedStateHandle) {
79 | //No-op
80 | }
81 |
82 | /**
83 | * Restore the state from the [SavedStateHandle] or null if nothing was saved.
84 | *
85 | * ```handle.get("state")```
86 | */
87 | open fun restoreState(handle: SavedStateHandle): S? {
88 | return null
89 | }
90 |
91 | override fun subscribe(hotStart: Boolean, fn: (S) -> Unit): Closeable {
92 | listeners.add(fn)
93 | if (hotStart) fn(state)
94 | return ViewModelSubscription(this, fn)
95 | }
96 |
97 | override fun onCleared() {
98 | super.onCleared()
99 | close()
100 | }
101 | }
--------------------------------------------------------------------------------
/mini-processor/src/main/java/mini/processor/kapt/ProcessorUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini.processor.kapt
20 |
21 | import com.squareup.kotlinpoet.ClassName
22 | import com.squareup.kotlinpoet.FileSpec
23 | import com.squareup.kotlinpoet.TypeName
24 | import com.squareup.kotlinpoet.asTypeName
25 | import mini.processor.common.ProcessorException
26 | import java.io.ByteArrayOutputStream
27 | import java.io.PrintStream
28 | import javax.annotation.processing.ProcessingEnvironment
29 | import javax.lang.model.element.Element
30 | import javax.lang.model.element.ExecutableElement
31 | import javax.lang.model.type.TypeMirror
32 | import javax.lang.model.util.Elements
33 | import javax.lang.model.util.Types
34 | import javax.tools.Diagnostic
35 | import javax.tools.StandardLocation
36 |
37 | lateinit var env: ProcessingEnvironment
38 | lateinit var elementUtils: Elements
39 | lateinit var typeUtils: Types
40 |
41 | fun Throwable.stackTraceString(): String {
42 | val out = ByteArrayOutputStream()
43 | printStackTrace(PrintStream(out))
44 | return out.toString()
45 | }
46 |
47 | fun ExecutableElement.isSuspending(): Boolean {
48 | return parameters.last().asType().toString().startsWith("kotlin.coroutines.Continuation")
49 | }
50 |
51 | fun TypeMirror.getAllSuperTypes(depth: Int = 0): Set {
52 | //We want to sort them by depth
53 | val superTypes = typeUtils.directSupertypes(this).toSet()
54 | .map { it.getAllSuperTypes(depth + 1) }
55 | .flatten()
56 | return setOf(this) + superTypes
57 | }
58 |
59 | fun kaptCompilePrecondition(
60 | check: Boolean,
61 | message: String,
62 | element: Element? = null
63 | ) {
64 | if (!check) {
65 | kaptLogError(message, element)
66 | throw ProcessorException()
67 | }
68 | }
69 |
70 | fun kaptLogError(message: String, element: Element? = null) {
71 | kaptLogMessage(Diagnostic.Kind.ERROR, message, element)
72 | }
73 |
74 | fun kaptWarning(message: String, element: Element? = null) {
75 | kaptLogMessage(Diagnostic.Kind.MANDATORY_WARNING, message, element)
76 | }
77 |
78 | fun kaptLogMessage(kind: Diagnostic.Kind, message: String, element: Element? = null) {
79 | env.messager.printMessage(kind, "\n" + message, element)
80 | }
81 |
82 | //KotlinPoet utils
83 |
84 | fun FileSpec.writeToFile(vararg sourceElements: Element) {
85 | val kotlinFileObject = env.filer
86 | .createResource(StandardLocation.SOURCE_OUTPUT, packageName, "$name.kt", *sourceElements)
87 | val openWriter = kotlinFileObject.openWriter()
88 | writeTo(openWriter)
89 | openWriter.close()
90 | }
91 |
92 | /**
93 | * Map [java.lang.Object] to [Any]
94 | */
95 | fun TypeName.safeAnyTypeName(): TypeName =
96 | if (this is ClassName && this == ClassName("java.lang", "Object")) {
97 | Any::class.asTypeName()
98 | } else {
99 | this
100 | }
101 |
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/LoggerMiddleware.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Copyright 2020 BQ
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package mini
20 |
21 | import android.util.Log
22 | import java.util.concurrent.atomic.AtomicInteger
23 |
24 | /** Actions implementing this interface won't log anything, including nested calls */
25 | interface SilentAction
26 |
27 | /**
28 | * Actions implementing this interface will log nested actions visually since they will
29 | * most likely dispatch other actions.
30 | */
31 | interface SuspendingAction
32 |
33 | internal fun extractClassName(clazz: Class<*>): String {
34 | return clazz.name.substringAfterLast(".")
35 | }
36 |
37 | /**
38 | * Action logging for stores.
39 | */
40 | class LoggerMiddleware(stores: Collection>,
41 | private val tag: String = "MiniLog",
42 | private val diffFunction: ((a: Any?, b: Any?) -> String)? = null,
43 | private val logger: (priority: Int, tag: String, msg: String) -> Unit) : Middleware {
44 |
45 | private var actionCounter = AtomicInteger(0)
46 |
47 | private val stores = stores.toList()
48 |
49 | override suspend fun intercept(action: Any, chain: Chain): Any {
50 | if (action is SilentAction) return chain.proceed(action) //Do nothing
51 |
52 | val isSuspending = action is SuspendingAction
53 | val beforeStates: Array = Array(stores.size) { }
54 | val afterStates: Array = Array(stores.size) { }
55 | val actionName = extractClassName(action.javaClass)
56 |
57 | if (!isSuspending) {
58 | stores.forEachIndexed { idx, store -> beforeStates[idx] = store.state }
59 | }
60 |
61 | val (upCorner, downCorner) = if (isSuspending) {
62 | "╔═════ " to "╚════> "
63 | } else {
64 | "┌── " to "└─> "
65 | }
66 |
67 | val prelude = "[${"${actionCounter.getAndIncrement() % 100}".padStart(2, '0')}] "
68 |
69 | logger(Log.DEBUG, tag, "$prelude$upCorner$actionName")
70 | logger(Log.DEBUG, tag, "$prelude$action")
71 |
72 | //Pass it down
73 | val start = System.nanoTime()
74 | val outAction = chain.proceed(action)
75 | val processTime = (System.nanoTime() - start) / 1000000
76 |
77 | if (!isSuspending) {
78 | stores.forEachIndexed { idx, store -> afterStates[idx] = store.state }
79 |
80 | for (i in beforeStates.indices) {
81 | val oldState = beforeStates[i]
82 | val newState = afterStates[i]
83 | if (oldState !== newState) {
84 | val line = "$prelude│ ${stores[i].javaClass.name}"
85 | logger(Log.VERBOSE, tag, "$line: $newState")
86 | diffFunction?.invoke(oldState, newState)?.let { diff ->
87 | logger(Log.DEBUG, tag, "$line: $diff")
88 | }
89 | }
90 | }
91 | }
92 |
93 | logger(Log.DEBUG, tag, "$prelude$downCorner$actionName ${processTime}ms")
94 |
95 | return outAction
96 | }
97 | }
--------------------------------------------------------------------------------
/mini-common/src/test/kotlin/mini/StoreFlowTest.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import kotlinx.coroutines.*
20 | import kotlinx.coroutines.flow.launchIn
21 | import kotlinx.coroutines.flow.onEach
22 | import kotlinx.coroutines.flow.take
23 | import mini.SampleStore.Companion.INITIAL_STATE
24 | import org.amshove.kluent.`should be equal to`
25 | import org.amshove.kluent.`should equal`
26 | import org.junit.Test
27 | import java.util.concurrent.Executors
28 |
29 | class StoreFlowTest {
30 |
31 | private val testScope =
32 | CoroutineScope(Executors.newScheduledThreadPool(1).asCoroutineDispatcher())
33 |
34 | @Test(timeout = 1000)
35 | fun `flow sends initial state on collection`(): Unit = runBlocking {
36 | val store = SampleStore()
37 | var observedState = SampleState(INITIAL_STATE)
38 |
39 | val job = store.flow(hotStart = false)
40 | .onEach { observedState = it }
41 | .take(1)
42 | .launchIn(testScope)
43 |
44 | store.setState(SampleState("abc")) //Set before collect
45 |
46 | job.join()
47 | observedState `should be equal to` SampleState("abc")
48 | Unit
49 | }
50 |
51 | @Test(timeout = 1000)
52 | fun `flow sends updates to all`(): Unit = runBlocking {
53 | val store = SampleStore()
54 | val called = intArrayOf(0, 0)
55 |
56 | val job1 = store.flow()
57 | .onEach { called[0]++ }
58 | .take(2)
59 | .launchIn(testScope)
60 |
61 | val job2 = store.flow()
62 | .onEach { called[1]++ }
63 | .take(2)
64 | .launchIn(testScope)
65 |
66 | store.setState(SampleState("abc"))
67 |
68 | job1.join()
69 | job2.join()
70 |
71 | //Called two times, one for initial state, one for updated stated
72 | called.`should be equal to`(intArrayOf(2, 2))
73 | Unit
74 | }
75 |
76 | @Test(timeout = 1000)
77 | fun `channel sends updates`(): Unit = runBlocking {
78 | val store = SampleStore()
79 | var observedState = SampleState("")
80 | val scope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher())
81 | val job = scope.launch {
82 | observedState = store.channel().receive()
83 | }
84 | store.setState(SampleState("abc"))
85 | job.join()
86 | observedState `should be equal to` SampleState("abc")
87 | Unit
88 | }
89 |
90 | @Test(timeout = 1000)
91 | fun `flow closes`(): Unit = runBlocking {
92 | val store = SampleStore()
93 | var observedState = store.state
94 |
95 | val scope = CoroutineScope(Job())
96 | store.flow()
97 | .onEach {
98 | observedState = it
99 | }
100 | .launchIn(scope)
101 |
102 | scope.cancel() //Cancel the scope
103 | store.setState(SampleState("abc"))
104 |
105 | observedState `should be equal to` SampleState(INITIAL_STATE)
106 | Unit
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/mini/android/sample/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.sample.ui.theme
18 |
19 | import androidx.compose.ui.graphics.Color
20 |
21 | val primaryLight = Color(0xFF3C6939)
22 | val onPrimaryLight = Color(0xFFFFFFFF)
23 | val primaryContainerLight = Color(0xFFBDF0B4)
24 | val onPrimaryContainerLight = Color(0xFF393838)
25 | val secondaryLight = Color(0xFF53634F)
26 | val onSecondaryLight = Color(0xFFFFFFFF)
27 | val secondaryContainerLight = Color(0xFFD6E8CE)
28 | val onSecondaryContainerLight = Color(0xFF393838)
29 | val tertiaryLight = Color(0xFF49672E)
30 | val onTertiaryLight = Color(0xFFFFFFFF)
31 | val tertiaryContainerLight = Color(0xFFCAEEA6)
32 | val onTertiaryContainerLight = Color(0xFF393838)
33 | val errorLight = Color(0xFFBA1A1A)
34 | val onErrorLight = Color(0xFFFFFFFF)
35 | val errorContainerLight = Color(0xFFFFDAD6)
36 | val onErrorContainerLight = Color(0xFF410002)
37 | val backgroundLight = Color(0xFFF7FBF1)
38 | val onBackgroundLight = Color(0xFF191D17)
39 | val surfaceLight = Color(0xFFF7FBF1)
40 | val onSurfaceLight = Color(0xFF191D17)
41 | val surfaceVariantLight = Color(0xFFDEE5D8)
42 | val onSurfaceVariantLight = Color(0xFF424940)
43 | val outlineLight = Color(0xFF72796F)
44 | val outlineVariantLight = Color(0xFFC2C8BD)
45 | val scrimLight = Color(0xFF393838)
46 | val inverseSurfaceLight = Color(0xFF2D322B)
47 | val inverseOnSurfaceLight = Color(0xFFEFF2E9)
48 | val inversePrimaryLight = Color(0xFFA1D399)
49 | val surfaceDimLight = Color(0xFFD8DBD2)
50 | val surfaceBrightLight = Color(0xFFF7FBF1)
51 | val surfaceContainerLowestLight = Color(0xFFFFFFFF)
52 | val surfaceContainerLowLight = Color(0xFFF1F5EB)
53 | val surfaceContainerLight = Color(0xFFECEFE6)
54 | val surfaceContainerHighLight = Color(0xFFE6E9E0)
55 | val surfaceContainerHighestLight = Color(0xFFE0E4DB)
56 |
57 | val primaryDark = Color(0xFFA1D399)
58 | val onPrimaryDark = Color(0xFF0B390F)
59 | val primaryContainerDark = Color(0xFF245023)
60 | val onPrimaryContainerDark = Color(0xFFBDF0B4)
61 | val secondaryDark = Color(0xFFBACCB3)
62 | val onSecondaryDark = Color(0xFF253423)
63 | val secondaryContainerDark = Color(0xFF3B4B38)
64 | val onSecondaryContainerDark = Color(0xFFD6E8CE)
65 | val tertiaryDark = Color(0xFFAED18D)
66 | val onTertiaryDark = Color(0xFF1C3703)
67 | val tertiaryContainerDark = Color(0xFF324E18)
68 | val onTertiaryContainerDark = Color(0xFFCAEEA6)
69 | val errorDark = Color(0xFFFFB4AB)
70 | val onErrorDark = Color(0xFF690005)
71 | val errorContainerDark = Color(0xFF93000A)
72 | val onErrorContainerDark = Color(0xFFFFDAD6)
73 | val backgroundDark = Color(0xFF393838)
74 | val onBackgroundDark = Color(0xFFE0E4DB)
75 | val surfaceDark = Color(0xFF393838)
76 | val onSurfaceDark = Color(0xFFE0E4DB)
77 | val surfaceVariantDark = Color(0xFF424940)
78 | val onSurfaceVariantDark = Color(0xFFC2C8BD)
79 | val outlineDark = Color(0xFF8C9388)
80 | val outlineVariantDark = Color(0xFF424940)
81 | val scrimDark = Color(0xFF393838)
82 | val inverseSurfaceDark = Color(0xFFE0E4DB)
83 | val inverseOnSurfaceDark = Color(0xFF2D322B)
84 | val inversePrimaryDark = Color(0xFF3C6939)
85 | val surfaceDimDark = Color(0xFF10140F)
86 | val surfaceBrightDark = Color(0xFF363A34)
87 | val surfaceContainerLowestDark = Color(0xFF0B0F0A)
88 | val surfaceContainerLowDark = Color(0xFF191D17)
89 | val surfaceContainerDark = Color(0xFF1D211B)
90 | val surfaceContainerHighDark = Color(0xFF272B25)
91 | val surfaceContainerHighestDark = Color(0xFF323630)
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/convention-plugins/src/main/kotlin/mini/android/plugins/extensions/ProjectExtensions.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.plugins.extensions
18 |
19 | import com.android.build.api.dsl.ApplicationExtension
20 | import com.android.build.gradle.LibraryExtension
21 | import com.gladed.androidgitversion.AndroidGitVersionExtension
22 | import org.gradle.api.Project
23 | import org.gradle.api.plugins.JavaPluginExtension
24 | import org.gradle.api.publish.PublishingExtension
25 | import org.gradle.api.publish.maven.MavenPublication
26 | import org.gradle.api.publish.maven.internal.publication.DefaultMavenPom
27 | import org.gradle.kotlin.dsl.configure
28 |
29 | data class Version(val name: String, val code: Int)
30 |
31 | internal fun Project.applyVersioning(): Version {
32 | pluginManager.apply("com.gladed.androidgitversion")
33 | project.extensions.configure {
34 | codeFormat = "MNNPP"
35 | baseCode = 1
36 | }
37 |
38 | return Version(
39 | project.extensions.getByType(AndroidGitVersionExtension::class.java).name(),
40 | project.extensions.getByType(AndroidGitVersionExtension::class.java).code()
41 | )
42 | }
43 |
44 | internal fun Project.applyAndroidAppVersioning(versionName: String, versionCode: Int) {
45 | project.extensions.configure{
46 | defaultConfig {
47 | this.versionName = versionName
48 | this.versionCode = versionCode
49 | }
50 | }
51 | }
52 |
53 | internal fun Project.applyAndroidLibPublishing(versionName: String) {
54 | val publishingName = "release"
55 |
56 | pluginManager.apply("maven-publish")
57 |
58 | project.extensions.configure {
59 | publishing {
60 | singleVariant(publishingName) {
61 | withSourcesJar()
62 | withJavadocJar()
63 | }
64 | }
65 | }
66 |
67 | project.afterEvaluate {
68 | project.extensions.configure {
69 | publications {
70 | create(publishingName, MavenPublication::class.java) {
71 | from(components.getByName(publishingName))
72 |
73 | artifactId = project.name
74 | version = versionName
75 |
76 | (pom as DefaultMavenPom).setMetadata(
77 | project.name,
78 | versionName
79 | )
80 | }
81 | }
82 | }
83 | }
84 | }
85 |
86 | internal fun Project.applyJavaLibPublishing(versionName: String) {
87 | val publishingName = "maven"
88 |
89 | pluginManager.apply("java")
90 | pluginManager.apply("maven-publish")
91 |
92 | project.extensions.configure {
93 | withSourcesJar()
94 | withJavadocJar()
95 | }
96 |
97 | project.extensions.configure {
98 | publications {
99 | create(publishingName, MavenPublication::class.java) {
100 | from(components.getByName("java"))
101 |
102 | artifactId = project.name
103 | version = versionName
104 |
105 | (pom as DefaultMavenPom).setMetadata(
106 | project.name,
107 | versionName
108 | )
109 | }
110 | }
111 | }
112 | }
--------------------------------------------------------------------------------
/app/src/main/kotlin/mini/android/sample/MainActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.sample
18 |
19 | import android.content.Intent
20 | import android.net.Uri
21 | import android.os.Bundle
22 | import android.widget.Button
23 | import androidx.activity.compose.setContent
24 | import androidx.activity.enableEdgeToEdge
25 | import androidx.appcompat.app.AppCompatActivity
26 | import androidx.compose.foundation.background
27 | import androidx.compose.foundation.layout.Arrangement
28 | import androidx.compose.foundation.layout.Box
29 | import androidx.compose.foundation.layout.Column
30 | import androidx.compose.foundation.layout.fillMaxSize
31 | import androidx.compose.foundation.layout.padding
32 | import androidx.compose.material3.Button
33 | import androidx.compose.material3.MaterialTheme
34 | import androidx.compose.material3.Scaffold
35 | import androidx.compose.material3.Text
36 | import androidx.compose.runtime.Composable
37 | import androidx.compose.runtime.remember
38 | import androidx.compose.ui.Alignment
39 | import androidx.compose.ui.Modifier
40 | import androidx.compose.ui.graphics.Color
41 | import androidx.compose.ui.platform.LocalContext
42 | import androidx.compose.ui.res.stringResource
43 | import androidx.compose.ui.tooling.preview.Preview
44 | import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
45 | import mini.android.sample.ui.theme.AppTheme
46 |
47 | class MainActivity : AppCompatActivity() {
48 |
49 | override fun onCreate(savedInstanceState: Bundle?) {
50 | super.onCreate(savedInstanceState)
51 |
52 | installSplashScreen()
53 | enableEdgeToEdge()
54 |
55 | setContent {
56 | AppTheme {
57 | MainScreen(
58 | onGoToStoreSampleClicked = {
59 | Intent(this, StoreSampleActivity::class.java).apply {
60 | startActivity(this)
61 | }
62 | },
63 | onGoToViewModelSampleClicked = {
64 | Intent(this, ViewModelSampleActivity::class.java).apply {
65 | startActivity(this)
66 | }
67 | }
68 | )
69 | }
70 | }
71 | }
72 | }
73 |
74 | @Composable
75 | private fun MainScreen(modifier: Modifier = Modifier,
76 | onGoToStoreSampleClicked: () -> Unit = {},
77 | onGoToViewModelSampleClicked: () -> Unit = {}) {
78 | Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
79 | MainContent(
80 | modifier = modifier
81 | .fillMaxSize()
82 | .padding(innerPadding),
83 | onGoToStoreSampleClicked = onGoToStoreSampleClicked,
84 | onGoToViewModelSampleClicked = onGoToViewModelSampleClicked
85 | )
86 | }
87 | }
88 |
89 | @Composable
90 | private fun MainContent(
91 | modifier: Modifier = Modifier,
92 | onGoToStoreSampleClicked: () -> Unit = {},
93 | onGoToViewModelSampleClicked: () -> Unit = {}
94 | ) {
95 | Column(
96 | modifier = modifier.fillMaxSize(),
97 | horizontalAlignment = Alignment.CenterHorizontally,
98 | verticalArrangement = Arrangement.Center
99 | ) {
100 | Button(onClick = onGoToStoreSampleClicked) {
101 | Text("Go to Store sample")
102 | }
103 | Button(onClick = onGoToViewModelSampleClicked) {
104 | Text("Go to ViewModel sample")
105 | }
106 | }
107 | }
108 |
109 | @Preview(showBackground = true)
110 | @Composable
111 | fun MainScreenPreview() {
112 | AppTheme {
113 | MainContent()
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/kotlin/mini/android/sample/StoreSampleActivity.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.android.sample
18 |
19 | import android.os.Bundle
20 | import android.view.View
21 | import android.widget.Button
22 | import android.widget.ProgressBar
23 | import android.widget.TextView
24 | import android.widget.Toast
25 | import androidx.activity.compose.setContent
26 | import androidx.compose.foundation.layout.fillMaxSize
27 | import androidx.compose.foundation.layout.padding
28 | import androidx.compose.material3.Scaffold
29 | import androidx.compose.runtime.Composable
30 | import androidx.compose.runtime.LaunchedEffect
31 | import androidx.compose.runtime.collectAsState
32 | import androidx.compose.runtime.mutableStateOf
33 | import androidx.compose.runtime.remember
34 | import androidx.compose.ui.Modifier
35 | import kotlinx.coroutines.delay
36 | import kotlinx.coroutines.flow.map
37 | import kotlinx.coroutines.flow.onEach
38 | import kotlinx.coroutines.launch
39 | import mini.*
40 | import mini.android.FluxActivity
41 | import mini.android.sample.ui.theme.AppTheme
42 |
43 | private val dispatcher = Dispatcher()
44 |
45 | class MainStore : Store() {
46 |
47 | init {
48 | Mini.link(dispatcher, this).track()
49 | }
50 |
51 | @Reducer
52 | fun handleLoading(state: MainState, action: SetLoadingAction): MainState {
53 | return state.copy(loading = action.loading)
54 | }
55 |
56 | @Reducer
57 | fun handleSetTextAction(state: MainState, action: SetTextAction): MainState {
58 | return state.copy(text = action.text)
59 | }
60 |
61 | @Reducer
62 | fun handleAnalyticsAction(action: AnalyticsAction) {
63 | //Log to analytics
64 | }
65 |
66 | @Reducer
67 | fun handleAnyAction(action: Any) {
68 | //Log to analytics
69 | }
70 |
71 | @UseCase
72 | suspend fun useCase(action: LongUseCaseAction) {
73 | if (state.loading) return
74 | dispatcher.dispatch(SetLoadingAction(true))
75 | dispatcher.dispatch(SetTextAction("Loading from network..."))
76 | delay(5000)
77 | dispatcher.dispatch(SetTextAction("Hello From UseCase"))
78 | dispatcher.dispatch(SetLoadingAction(false))
79 | }
80 | }
81 |
82 | class StoreSampleActivity : FluxActivity() {
83 | private val mainStore: MainStore by lazy {
84 | MainStore()
85 | }
86 |
87 | override suspend fun whenCreated(savedInstanceState: Bundle?) {
88 | setContent {
89 | AppTheme {
90 | StoreSampleScreen()
91 | }
92 | }
93 | }
94 |
95 | @Composable
96 | private fun StoreSampleScreen() {
97 | val mainState = mainStore.flow().collectAsState(initial = MainState())
98 | val showToastState = mainStore.flow(hotStart = false).map { !it.loading }
99 | .collectAsState(initial = false)
100 |
101 | LaunchedEffect(showToastState.value) {
102 | if (showToastState.value) {
103 | Toast.makeText(
104 | this@StoreSampleActivity,
105 | "Finished loading",
106 | Toast.LENGTH_LONG
107 | ).show()
108 | }
109 | }
110 |
111 | Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
112 | SampleScreen(
113 | modifier = Modifier.padding(innerPadding),
114 | text = mainState.value.toString(),
115 | isLoading = mainState.value.loading,
116 | onStartSampleClicked = {
117 | launch {
118 | dispatcher.dispatch(LongUseCaseAction("HyperDevs"))
119 | }
120 | }
121 | )
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/mini-kodein-android-compose/src/main/java/mini/kodein/android/compose/KodeinAndroidComposeUtils.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini.kodein.android.compose
18 |
19 | import androidx.annotation.MainThread
20 | import androidx.lifecycle.ViewModel
21 | import androidx.lifecycle.ViewModelProvider
22 | import androidx.navigation.NavBackStackEntry
23 | import androidx.navigation.NavController
24 | import mini.kodein.android.TypedViewModel
25 | import org.kodein.di.DI
26 | import org.kodein.di.DIAware
27 | import org.kodein.di.direct
28 | import org.kodein.di.instance
29 |
30 | /**
31 | * Injects a [ViewModel] scoped to the lifecycle of the [NavBackStackEntry].
32 | * As [NavBackStackEntry] is a final class, can not implement [DIAware] so the [DI] is needed as
33 | * a param.
34 | */
35 | @MainThread
36 | inline fun NavBackStackEntry.viewModel(di: DI): Lazy {
37 | return lazy {
38 | ViewModelProvider(this, di.direct.instance()).get(VM::class.java)
39 | }
40 | }
41 |
42 | /**
43 | * Injects a [TypedViewModel] scoped to the lifecycle of the [NavBackStackEntry].
44 | * As [NavBackStackEntry] is a final class, can not implement [DIAware] so the [DI] is needed as
45 | * a param.
46 | *
47 | * Requires previous ViewModelProvider.Factory injection for the ViewModel via bindViewModelFactory
48 | * to work and a TypedViewModel to be used.
49 | */
50 | @MainThread
51 | inline fun > NavBackStackEntry.viewModel(di: DI, params: T): Lazy {
52 | return lazy {
53 | ViewModelProvider(this, di.direct.instance(VM::class.java, params)).get(VM::class.java)
54 | }
55 | }
56 |
57 | /**
58 | * Injects a [ViewModel] scoped to the lifecycle of the [NavBackStackEntry] of the navigation
59 | * route given in [navigationRoute].
60 | * This allows to retrieve the same [ViewModel] from a previous route to which you have navigated in
61 | * the navigation graph instead of having a different [ViewModel] instance of the same [ViewModel]
62 | * for each navigation composable. It isn't needed that is from a nested navigation graph, only
63 | * that you have navigated previously to that route so we can find it in the [backStackEntry].
64 | * As [NavBackStackEntry] is a final class, can not implement [DIAware] so the [DI] is needed as
65 | * a param.
66 | */
67 | @MainThread
68 | inline fun NavController.sharedViewModelFromRoute(di: DI, navigationRoute: String): Lazy {
69 | return lazy {
70 | val parentBackStackEntry = getBackStackEntry(navigationRoute)
71 | ViewModelProvider(parentBackStackEntry, di.direct.instance()).get(VM::class.java)
72 | }
73 | }
74 |
75 | /**
76 | * Injects a [TypedViewModel] scoped to the lifecycle of the [NavBackStackEntry] of the navigation
77 | * route given in [navigationRoute].
78 | * This allows to retrieve the same [ViewModel] from a previous route to which you have navigated in
79 | * the navigation graph instead of having a different [ViewModel] instance of the same [ViewModel]
80 | * for each navigation composable. It isn't needed that is from a nested navigation graph, only
81 | * that you have navigated previously to that route so we can find it in the [backStackEntry].
82 | * As [NavBackStackEntry] is a final class, can not implement [DIAware] so the [DI] is needed as
83 | * a param.
84 | *
85 | * Requires previous ViewModelProvider.Factory injection for the ViewModel via bindViewModelFactory
86 | * to work and a TypedViewModel to be used.
87 | */
88 | @MainThread
89 | inline fun > NavController.sharedViewModelFromRoute(di: DI,
90 | navigationRoute: String,
91 | params: T): Lazy {
92 | return lazy {
93 | val parentBackStackEntry = getBackStackEntry(navigationRoute)
94 | ViewModelProvider(parentBackStackEntry, di.direct.instance(VM::class.java, params)).get(VM::class.java)
95 | }
96 | }
--------------------------------------------------------------------------------
/mini-common/src/main/java/mini/DiffAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 HyperDevs
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package mini
18 |
19 | import kotlin.reflect.KClass
20 |
21 | interface DiffAdapter {
22 |
23 | companion object {
24 | const val GENERATED_CLASS_SUFFIX = "DiffAdapter"
25 | }
26 |
27 | /**
28 | * Produce a json like diff of an object
29 | */
30 | fun diff(a: T, b: T, indent: String = " "): String?
31 | }
32 |
33 | class ListDiffAdapter> : DiffAdapter {
34 | companion object {
35 | val INSTANCE = ListDiffAdapter>()
36 | }
37 |
38 | override fun diff(a: T, b: T, indent: String): String? {
39 | val sb = StringBuilder()
40 | val aSize = a.size
41 | val bSize = b.size
42 | val range = 0..(kotlin.math.max(aSize, bSize))
43 | range.forEach { index ->
44 | val ai = a.getOrNull(index)
45 | val bi = b.getOrNull(index)
46 | val diff = diffObjects(ai, bi, indent)
47 | if (diff != null) {
48 | @Suppress("UNCHECKED_CAST")
49 | sb.append("\n$index: $diff")
50 | }
51 | }
52 | return "[${sb.toString().prependIndent(indent)}\n]"
53 | }
54 | }
55 |
56 | @Suppress("UNCHECKED_CAST")
57 | class MapDiffAdapter> : DiffAdapter {
58 |
59 | companion object {
60 | val INSTANCE = MapDiffAdapter