Skip to content

Commit

Permalink
Add UDF sample via molecule
Browse files Browse the repository at this point in the history
  • Loading branch information
arunkumar9t2 committed Dec 29, 2023
1 parent 40bdbf9 commit 60a7ab0
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 16 deletions.
1 change: 1 addition & 0 deletions android-app/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ build/
.idea/assetWizardSettings.xml
.idea/kotlinScripting.xml
.idea/git_toolbox_prj.xml
/.idea/inspectionProfiles/Project_Default.xml
# .idea/workspace.xml

# Local configuration file (sdk path, etc)
Expand Down
2 changes: 2 additions & 0 deletions android-app/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ activityCompose = "1.8.2"
composeBom = "2023.10.01"
whetstone = "0.6.0"
dagger = "2.50"
molecule = "1.3.1"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
Expand All @@ -28,6 +29,7 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
dagger = { group = "com.google.dagger", name = "dagger" }
dagger-compiler = { group = "com.google.dagger", name = "dagger" }
molecule-runtime = { module = "app.cash.molecule:molecule-runtime", version.ref = "molecule" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
Expand Down
8 changes: 5 additions & 3 deletions android-app/lynket-playground/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
buildFeatures {
compose = true
Expand Down Expand Up @@ -69,6 +69,8 @@ dependencies {
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)

implementation(libs.molecule.runtime)

// Dagger
implementation(libs.dagger)
kapt(libs.dagger.compiler)
Expand Down
2 changes: 1 addition & 1 deletion android-app/lynket-playground/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.Lynket">
<activity
android:name=".MainActivity"
android:name=".home.HomeActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Lynket">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
*
* Lynket
*
* Copyright (C) 2023 Arunkumar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package arun.com.chromer.halo

import androidx.compose.runtime.Composable
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.cash.molecule.AndroidUiDispatcher
import app.cash.molecule.RecompositionMode
import app.cash.molecule.launchMolecule
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow

/**
* UDF based ViewModel via Molecule
*/
abstract class HaloViewModel<Action, State> : ViewModel() {
private val scope = CoroutineScope(viewModelScope.coroutineContext + AndroidUiDispatcher.Main)

private val actions = MutableSharedFlow<Action>()

val state: StateFlow<State> by lazy(LazyThreadSafetyMode.NONE) {
scope.launchMolecule(mode = RecompositionMode.ContextClock) {
process(actions.asSharedFlow())
}
}

@Composable
protected abstract fun process(actions: Flow<Action>): State
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package arun.com.chromer
package arun.com.chromer.home

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import arun.com.chromer.di.HasInjector
import arun.com.chromer.theme.LynketTheme
import com.deliveryhero.whetstone.activity.ContributesActivityInjector
import com.deliveryhero.whetstone.compose.injectedViewModel

@ContributesActivityInjector
class MainActivity : ComponentActivity(), HasInjector {
class HomeActivity : ComponentActivity(), HasInjector {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -45,22 +51,30 @@ class MainActivity : ComponentActivity(), HasInjector {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
ListItems()
}
}
}
}
}

@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
fun ListItems(
viewModel: HomeViewModel = injectedViewModel()
) {
val state by viewModel.state.collectAsState()
when (state) {
is HomeState.Loading -> {
Text(text = "Loading")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
LynketTheme {
Greeting("Android")
is HomeState.Items -> {
val items = (state as HomeState.Items).items
LazyColumn(contentPadding = PaddingValues(all = 16.dp)) {
items(items, key = { it }) { item ->
Text(text = item)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
*
* Lynket
*
* Copyright (C) 2023 Arunkumar
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package arun.com.chromer.home

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import arun.com.chromer.halo.HaloViewModel
import com.deliveryhero.whetstone.viewmodel.ContributesViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.withContext
import javax.inject.Inject

sealed class HomeAction {
data object Load : HomeAction()
}

sealed class HomeState {
data object Loading : HomeState()
data class Items(val items: List<String>) : HomeState()
}

@ContributesViewModel
class HomeViewModel
@Inject
constructor() : HaloViewModel<HomeAction, HomeState>() {

private val dummyItems by lazy {
flow {
delay(2000)
repeat(100) {
emit(it)
}
}
}

@Composable
override fun process(actions: Flow<HomeAction>): HomeState {
var items by remember { mutableStateOf(emptyList<String>()) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
dummyItems.collect {
items = items + it.toString()
}
}
}
return if (items.isEmpty()) {
HomeState.Loading
} else {
HomeState.Items(items)
}
}
}

0 comments on commit 60a7ab0

Please sign in to comment.