Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
bdd1c36
Add converters and repository for brushgraph
maxmmitchell Apr 27, 2026
9ca6e48
Gemini code review comments
maxmmitchell Apr 27, 2026
3669298
Add support for caching last valid brush family + fix warnings on test
maxmmitchell Apr 30, 2026
0e58708
Support duplication respecting original nodes positioning
maxmmitchell Apr 30, 2026
b3049cf
Respond to Chris' comments
maxmmitchell May 5, 2026
9358890
Remove file-wide opt-ins unneded on 1.1.0-alpha02
maxmmitchell May 5, 2026
3e5b8c2
Refactor tests
maxmmitchell May 5, 2026
2f2c2ab
Add ViewModel and TutorialManager for brushgraph
maxmmitchell Apr 27, 2026
577578c
Fix issues from PR 2
maxmmitchell Apr 28, 2026
1265faf
Gemini comments
maxmmitchell Apr 28, 2026
07f56b9
Support returning mapping of old ids to duplicated ids from viewmodel…
maxmmitchell Apr 30, 2026
ee29a64
Get brush family correctly
maxmmitchell May 1, 2026
de408c2
Add tests
maxmmitchell May 5, 2026
ca83e1f
Respond to other comments from Chris
maxmmitchell May 5, 2026
f2b1434
Add Node UI infrastructure and simple fields
maxmmitchell Apr 27, 2026
2582552
Fix breakage from PR 3
maxmmitchell Apr 28, 2026
1dc8bd4
Gemini review comments
maxmmitchell Apr 28, 2026
6a6ca31
Remove NumericLimits.standard
maxmmitchell May 4, 2026
7679153
Add modifier params to composables
maxmmitchell May 6, 2026
681b9b7
Misc. cleanup from Chris' comments
maxmmitchell May 6, 2026
ac4301e
Fix errors/failures caused by version bump
maxmmitchell May 7, 2026
e7af8fd
Use modifier in top-level wrapper composable
maxmmitchell May 8, 2026
b0d4536
Add complex node fields
maxmmitchell Apr 27, 2026
56dc8f0
Revive NodeFields.kt
maxmmitchell Apr 28, 2026
93511e8
Gemini review comments
maxmmitchell Apr 28, 2026
c87ac3f
Fix angle source bug + remove animation fields
maxmmitchell May 1, 2026
1e1b35c
Remove NumericLimits.standard
maxmmitchell May 4, 2026
24892b8
Add modifier params
maxmmitchell May 6, 2026
6bbfbcb
Fix build errors
maxmmitchell May 8, 2026
c0deb97
Pass modifier to top-level wrapper composable
maxmmitchell May 8, 2026
5d7564d
Add Graph Canvas and core UI components
maxmmitchell Apr 27, 2026
b3d9d26
Gemini review comments
maxmmitchell Apr 28, 2026
4b178e1
Support more brush sizes + display error in test canvas
maxmmitchell May 1, 2026
0e5066b
Fix multi node movement
maxmmitchell May 1, 2026
a17e42f
Modifier params
maxmmitchell May 8, 2026
956f7e9
Use WindowSizeClass rather than computing isLandscape
maxmmitchell May 8, 2026
3357505
Add main screens and integrate brushgraph into the app
maxmmitchell Apr 27, 2026
073c2de
Fix from PR 6 + Gemini review comments
maxmmitchell Apr 28, 2026
99122a8
Show top issue on test canvas
maxmmitchell May 1, 2026
e78146c
Fix duplicate node positioning
maxmmitchell May 1, 2026
5645d8d
Make tutorial overlay slightly more opaque and swallow taps
maxmmitchell May 5, 2026
bf39c92
Use WindowSizeClass instead of computing isLandscape
maxmmitchell May 8, 2026
35db0cc
Respond to Chris' comments
maxmmitchell May 8, 2026
dfdb003
Update decode operations to use maxVersion of Version.DEVELOPMENT
maxmmitchell May 11, 2026
98c1038
Fix warning + format
maxmmitchell May 11, 2026
81fbf53
HUGE Reformatting commit
maxmmitchell May 11, 2026
87721b9
Make node widget colors better
maxmmitchell May 11, 2026
3e563a5
Adjust colors for buttons on nodes
maxmmitchell May 12, 2026
288e8e1
Fix tests, remove clientBrushFamilyId, fix error message
maxmmitchell May 12, 2026
bffddb5
Display fixes
maxmmitchell May 12, 2026
d67a07e
Massive texture store fix
maxmmitchell May 13, 2026
823499c
Fix tests + small UI bug
maxmmitchell May 14, 2026
d15c829
Fix developer comment bug on behaviors
maxmmitchell May 14, 2026
4966d4b
Merge pull request #62 from android/brush-graph/3-viewmodel
cka-dev May 14, 2026
426011c
Merge pull request #64 from android/brush-graph/5-complex-fields
cka-dev May 14, 2026
36c3282
Merge pull request #66 from android/brush-graph/7-integration
cka-dev May 14, 2026
25fd0ce
Merge pull request #74 from android/brush-graph/4-nodes (+ brush-grap…
maxmmitchell May 14, 2026
c957dec
Merge pull request #75 from android/brush-graph/6-canvas (+ brush-gra…
maxmmitchell May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ android {
isIncludeAndroidResources = true
}
}

sourceSets {
getByName("test").kotlin.srcDir("src/testShared/java")
getByName("androidTest").kotlin.srcDir("src/testShared/java")
}
}

roborazzi {
Expand All @@ -82,6 +87,8 @@ dependencies {
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.compose.material.icons.core)
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.navigation.runtime.ktx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.cahier.core.data.FakeNotesRepository
import com.example.cahier.features.home.HomePane
import com.example.cahier.features.home.viewmodel.HomeScreenViewModel
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@HiltAndroidTest
class CahierListDetailTest {
@get:Rule
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)

@get:Rule(order = 1)
val composeTestRule = createComposeRule()

private val fakeViewModel = HomeScreenViewModel(FakeNotesRepository())
Expand Down Expand Up @@ -83,6 +89,7 @@ class CahierListDetailTest {
HomePane(
navigateToCanvas = { _ -> },
navigateToDrawingCanvas = { _ -> },
navigateToBrushGraph = {},
navigateUp = {},
forceCompact = forceCompact,
homeScreenViewModel = fakeViewModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.ink.strokes.ImmutableStrokeInputBatch
import androidx.ink.strokes.Stroke
import androidx.test.core.app.ApplicationProvider
import com.example.cahier.core.ui.Converters
import com.example.cahier.developer.brushdesigner.data.CustomBrushDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
Expand Down Expand Up @@ -36,12 +37,14 @@ class OfflineNotesRepositoryTest {
private val testDispatcher = UnconfinedTestDispatcher()
private lateinit var repository: OfflineNotesRepository
private val noteDao: NoteDao = mock()
private val customBrushDao: CustomBrushDao = mock()

@Before
fun setup() {
fun setup() = runTest {
Dispatchers.setMain(testDispatcher)
whenever(customBrushDao.getAllCustomBrushesSync(any())).thenReturn(emptyList())
val context = ApplicationProvider.getApplicationContext<Context>()
repository = OfflineNotesRepository(noteDao, context)
repository = OfflineNotesRepository(noteDao, context, customBrushDao, mock())
}

@After
Expand Down Expand Up @@ -87,6 +90,7 @@ class OfflineNotesRepositoryTest {
verify(noteDao).updateNote(note.copy(isFavorite = true))
}


@Test
fun getAllNotesStream_returns_flow_from_DAO() = runTest {
val notes = listOf(Note(id = 1), Note(id = 2))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,22 @@ import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@HiltAndroidTest
class PointerInputUtilTest {

@get:Rule
@get:Rule(order = 0)
val hiltRule = HiltAndroidRule(this)

@get:Rule(order = 1)
val composeTestRule = createComposeRule()

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.example.cahier.developer.brushdesigner.viewmodel

import android.content.Context
import android.graphics.Bitmap
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.cahier.core.ui.CahierTextureBitmapStore
import com.example.cahier.developer.brushdesigner.data.BrushDesignerRepository
import com.example.cahier.developer.brushdesigner.data.CustomBrushDao
import dagger.hilt.android.testing.HiltAndroidRule
Expand All @@ -16,14 +17,15 @@ import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import javax.inject.Inject

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
@HiltAndroidTest
class BrushDesignerViewModelTest {
Expand Down Expand Up @@ -51,7 +53,10 @@ class BrushDesignerViewModelTest {
repository.updateActiveBrushProto(defaultProto())

val context = ApplicationProvider.getApplicationContext<Context>()
viewModel = BrushDesignerViewModel(context, repository, customBrushDao)
viewModel = BrushDesignerViewModel(
context, repository,
CahierTextureBitmapStore(context), customBrushDao
)
}

/** Produces a clean default [ProtoBrushFamily] matching the repository's initial state. */
Expand Down Expand Up @@ -141,7 +146,6 @@ class BrushDesignerViewModelTest {
@Test
fun saveToPalette_persists_to_dao() = runTest {
val brushName = "Test Persistence Brush"
viewModel.updateClientBrushFamilyId("test-id")

viewModel.saveToPalette(brushName).join()

Expand All @@ -153,36 +157,14 @@ class BrushDesignerViewModelTest {
fun previewBrushFamily_is_null_on_invalid_proto() = runTest {
val invalidRepo = BrushDesignerRepository()
invalidRepo.updateActiveBrushProto(ink.proto.BrushFamily.newBuilder().build())

val context = ApplicationProvider.getApplicationContext<Context>()
val vm = BrushDesignerViewModel(
ApplicationProvider.getApplicationContext(),
context,
invalidRepo,
CahierTextureBitmapStore(context),
customBrushDao
)

assertNull(vm.previewBrushFamily.value)
}

@Test
fun setTextureStore_immediately_syncs_proto_textures() = runTest {
val context = ApplicationProvider.getApplicationContext<Context>()
val testTextureId = "test-texture"
val testBitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888)

val baos = java.io.ByteArrayOutputStream()
testBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos)
val textureBytes = com.google.protobuf.ByteString.copyFrom(baos.toByteArray())

val protoWithTexture = viewModel.activeBrushProto.value.toBuilder()
.putTextureIdToBitmap(testTextureId, textureBytes)
.build()
repository.updateActiveBrushProto(protoWithTexture)

val store = com.example.cahier.core.ui.CahierTextureBitmapStore(context)
assertNull(store[testTextureId])

viewModel.setTextureStore(store)

assertNotNull(store[testTextureId])
}
}
Loading
Loading