From 12adce6d849b188b5f3b871fa89e91c3c3f578ac Mon Sep 17 00:00:00 2001 From: Maxwell Metzger Mitchell <60010512+maxmmitchell@users.noreply.github.com> Date: Tue, 26 May 2026 15:11:29 +0000 Subject: [PATCH 1/2] Upgrade Ink to version 1.1.0-alpha03 --- .../core/ui/CahierTextureBitmapStore.kt | 2 -- .../brushdesigner/ui/BrushDesignerScreen.kt | 4 +-- .../developer/brushdesigner/ui/PreviewPane.kt | 2 -- .../developer/brushdesigner/ui/TipShapeTab.kt | 2 -- .../viewmodel/BrushDesignerViewModel.kt | 3 +- .../developer/brushgraph/ui/GraphCanvas.kt | 1 - .../cahier/features/drawing/CustomBrushes.kt | 28 ++++++++----------- .../viewmodel/DrawingCanvasViewModel.kt | 24 ++++++++-------- gradle/libs.versions.toml | 2 +- ink-proto/src/main/proto/brush_family.proto | 16 ++++++++++- 10 files changed, 42 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/example/cahier/core/ui/CahierTextureBitmapStore.kt b/app/src/main/java/com/example/cahier/core/ui/CahierTextureBitmapStore.kt index 0e9f1664..dcd899e7 100644 --- a/app/src/main/java/com/example/cahier/core/ui/CahierTextureBitmapStore.kt +++ b/app/src/main/java/com/example/cahier/core/ui/CahierTextureBitmapStore.kt @@ -21,7 +21,6 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import androidx.annotation.DrawableRes -import androidx.ink.brush.ExperimentalInkCustomBrushApi import androidx.ink.brush.TextureBitmapStore import com.example.cahier.R import dagger.hilt.android.qualifiers.ApplicationContext @@ -31,7 +30,6 @@ import kotlinx.coroutines.flow.update import javax.inject.Inject import javax.inject.Singleton -@OptIn(ExperimentalInkCustomBrushApi::class) @Singleton class CahierTextureBitmapStore @Inject constructor(@ApplicationContext context: Context) : TextureBitmapStore { diff --git a/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/BrushDesignerScreen.kt b/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/BrushDesignerScreen.kt index 7fcca08a..b6e0e96f 100644 --- a/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/BrushDesignerScreen.kt +++ b/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/BrushDesignerScreen.kt @@ -79,7 +79,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.ink.brush.Brush -import androidx.ink.brush.ExperimentalInkCustomBrushApi import androidx.ink.brush.StockBrushes import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.example.cahier.R @@ -95,7 +94,6 @@ import ink.proto.BrushTip as ProtoBrushTip ExperimentalMaterial3Api::class, ExperimentalMaterial3WindowSizeClassApi::class, ExperimentalMaterial3AdaptiveApi::class, - ExperimentalInkCustomBrushApi::class ) @Composable fun BrushDesignerScreen( @@ -267,7 +265,7 @@ fun BrushDesignerScreen( * Note: Still accepts ViewModel for delegation to tab content and input model * sections. All state (activeProto, selectedCoatIndex) is hoisted from the screen. */ -@OptIn(ExperimentalInkCustomBrushApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ControlsPane( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/PreviewPane.kt b/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/PreviewPane.kt index 44884bd2..fc4d2ef5 100644 --- a/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/PreviewPane.kt +++ b/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/PreviewPane.kt @@ -53,7 +53,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.ink.brush.Brush -import androidx.ink.brush.ExperimentalInkCustomBrushApi import androidx.ink.rendering.android.canvas.CanvasStrokeRenderer import androidx.ink.strokes.Stroke import com.example.cahier.R @@ -72,7 +71,6 @@ import ink.proto.BrushFamily as ProtoBrushFamily * * Stateless: receives all state and callbacks, does not access ViewModel. */ -@OptIn(ExperimentalInkCustomBrushApi::class) @Composable internal fun PreviewPane( modifier: Modifier = Modifier, diff --git a/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/TipShapeTab.kt b/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/TipShapeTab.kt index 6a74ac2c..903912b1 100644 --- a/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/TipShapeTab.kt +++ b/app/src/main/java/com/example/cahier/developer/brushdesigner/ui/TipShapeTab.kt @@ -33,7 +33,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.ink.brush.Brush -import androidx.ink.brush.ExperimentalInkCustomBrushApi import com.example.cahier.R import ink.proto.BrushTip as ProtoBrushTip @@ -46,7 +45,6 @@ import ink.proto.BrushTip as ProtoBrushTip * * Stateless: receives data and callbacks, does not access ViewModel. */ -@OptIn(ExperimentalInkCustomBrushApi::class) @Composable internal fun TipShapeTabContent( currentTip: ProtoBrushTip, diff --git a/app/src/main/java/com/example/cahier/developer/brushdesigner/viewmodel/BrushDesignerViewModel.kt b/app/src/main/java/com/example/cahier/developer/brushdesigner/viewmodel/BrushDesignerViewModel.kt index a37e19ae..cf2c9719 100644 --- a/app/src/main/java/com/example/cahier/developer/brushdesigner/viewmodel/BrushDesignerViewModel.kt +++ b/app/src/main/java/com/example/cahier/developer/brushdesigner/viewmodel/BrushDesignerViewModel.kt @@ -23,7 +23,6 @@ import android.net.Uri import androidx.compose.ui.graphics.Color import androidx.ink.brush.Brush import androidx.ink.brush.BrushFamily -import androidx.ink.brush.ExperimentalInkCustomBrushApi import androidx.ink.brush.Version import androidx.ink.brush.compose.createWithComposeColor import androidx.ink.storage.decode @@ -65,7 +64,7 @@ import ink.proto.BrushFamily as ProtoBrushFamily import ink.proto.BrushPaint as ProtoBrushPaint import ink.proto.BrushTip as ProtoBrushTip -@OptIn(ExperimentalInkCustomBrushApi::class, FlowPreview::class) +@OptIn(FlowPreview::class) @HiltViewModel class BrushDesignerViewModel @Inject constructor( @param:ApplicationContext private val context: Context, diff --git a/app/src/main/java/com/example/cahier/developer/brushgraph/ui/GraphCanvas.kt b/app/src/main/java/com/example/cahier/developer/brushgraph/ui/GraphCanvas.kt index a02294ad..86b5d10f 100644 --- a/app/src/main/java/com/example/cahier/developer/brushgraph/ui/GraphCanvas.kt +++ b/app/src/main/java/com/example/cahier/developer/brushgraph/ui/GraphCanvas.kt @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@file:OptIn(androidx.ink.brush.ExperimentalInkCustomBrushApi::class) package com.example.cahier.developer.brushgraph.ui diff --git a/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt b/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt index 200d82a5..76a4b844 100644 --- a/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt +++ b/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt @@ -1,28 +1,23 @@ /* + * Copyright 2026 Google LLC. All rights reserved. * - * * - * * * Copyright 2025 Google LLC. All rights reserved. - * * * - * * * Licensed under the Apache License, Version 2.0 (the "License"); - * * * you may not use this file except in compliance with the License. - * * * You may obtain a copy of the License at - * * * - * * * http://www.apache.org/licenses/LICENSE-2.0 - * * * - * * * Unless required by applicable law or agreed to in writing, software - * * * distributed under the License is distributed on an "AS IS" BASIS, - * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * * See the License for the specific language governing permissions and - * * * limitations under the License. - * * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.example.cahier.features.drawing import android.content.Context import android.util.Log -import androidx.ink.brush.ExperimentalInkCustomBrushApi import androidx.ink.brush.Version import androidx.ink.storage.AndroidBrushFamilySerialization import com.example.cahier.R @@ -40,7 +35,6 @@ object CustomBrushes { } } - @OptIn(ExperimentalInkCustomBrushApi::class) private fun loadCustomBrushes( context: Context, textureStore: CahierTextureBitmapStore, diff --git a/app/src/main/java/com/example/cahier/features/drawing/viewmodel/DrawingCanvasViewModel.kt b/app/src/main/java/com/example/cahier/features/drawing/viewmodel/DrawingCanvasViewModel.kt index 06046791..3671bbe8 100644 --- a/app/src/main/java/com/example/cahier/features/drawing/viewmodel/DrawingCanvasViewModel.kt +++ b/app/src/main/java/com/example/cahier/features/drawing/viewmodel/DrawingCanvasViewModel.kt @@ -29,7 +29,6 @@ import androidx.core.graphics.createBitmap import androidx.core.net.toUri import androidx.ink.brush.Brush import androidx.ink.brush.BrushFamily -import androidx.ink.brush.ExperimentalInkCustomBrushApi import androidx.ink.brush.StockBrushes import androidx.ink.brush.Version import androidx.ink.brush.compose.composeColor @@ -92,9 +91,12 @@ class DrawingCanvasViewModel @Inject constructor( private val currentNightMode = AppCompatDelegate.getDefaultNightMode() - private val heartHighlighter = StockBrushes.emojiHighlighter("emoji-heart", showMiniEmojiTrail = true) - private val poopHighlighter = StockBrushes.emojiHighlighter("emoji-poop", showMiniEmojiTrail = true) - private val starHighlighter = StockBrushes.emojiHighlighter("emoji-star", showMiniEmojiTrail = true) + private val heartHighlighter = + StockBrushes.emojiHighlighter("emoji-heart", showMiniEmojiTrail = true) + private val poopHighlighter = + StockBrushes.emojiHighlighter("emoji-poop", showMiniEmojiTrail = true) + private val starHighlighter = + StockBrushes.emojiHighlighter("emoji-star", showMiniEmojiTrail = true) private val highlighter = StockBrushes.highlighter() private val _defaultBrush = MutableStateFlow( @@ -382,7 +384,7 @@ class DrawingCanvasViewModel @Inject constructor( isBrushSelectedInSession = true _selectedBrush.update { currentBrush -> val newBrush = currentBrush.copy(family = brushFamily) - val colorToApply = when(newBrush.family) { + val colorToApply = when (newBrush.family) { highlighter -> newBrush.composeColor.copy(alpha = HIGHLIGHTER_ALPHA) heartHighlighter -> Color(0xFFFF45CA).copy(alpha = HIGHLIGHTER_ALPHA) poopHighlighter -> Color(0xFF783013).copy(alpha = HIGHLIGHTER_ALPHA) @@ -397,7 +399,7 @@ class DrawingCanvasViewModel @Inject constructor( isBrushSelectedInSession = true _selectedBrush.update { currentBrush -> val newBrush = currentBrush.copy(family = brushFamily, size = size) - val colorToApply = when(newBrush.family) { + val colorToApply = when (newBrush.family) { highlighter -> newBrush.composeColor.copy(alpha = HIGHLIGHTER_ALPHA) heartHighlighter -> Color(0xFFFF45CA).copy(alpha = HIGHLIGHTER_ALPHA) poopHighlighter -> Color(0xFF783013).copy(alpha = HIGHLIGHTER_ALPHA) @@ -411,12 +413,14 @@ class DrawingCanvasViewModel @Inject constructor( fun changeBrushColor(color: Color) { isBrushSelectedInSession = true _selectedBrush.update { currentBrush -> - val colorToApply = when(currentBrush.family) { + val colorToApply = when (currentBrush.family) { heartHighlighter, poopHighlighter, starHighlighter, - highlighter -> color.copy(alpha = HIGHLIGHTER_ALPHA) - else -> color.copy(alpha = 1f) + highlighter, + -> color.copy(alpha = HIGHLIGHTER_ALPHA) + + else -> color.copy(alpha = 1f) } currentBrush.copyWithComposeColor(color = colorToApply) } @@ -467,7 +471,6 @@ class DrawingCanvasViewModel @Inject constructor( return _selectedBrush.value } - @OptIn(ExperimentalInkCustomBrushApi::class) suspend fun saveCurrentBrushToAutosave() { withContext(Dispatchers.IO) { try { @@ -491,7 +494,6 @@ class DrawingCanvasViewModel @Inject constructor( } } - @OptIn(ExperimentalInkCustomBrushApi::class) private fun loadCustomBrushes() { viewModelScope.launch(Dispatchers.IO) { val builtInBrushes = CustomBrushes.getBrushes(context, textureStore) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6c223bc0..67cbeb35 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ coilCompose = "3.4.0" foundation = "1.10.5" hiltAndroid = "2.59.2" hiltNavigationCompose = "1.3.0" -ink = "1.1.0-alpha02" +ink = "1.1.0-alpha03" kotlin = "2.3.20" coreKtx = "1.18.0" junit = "4.13.2" diff --git a/ink-proto/src/main/proto/brush_family.proto b/ink-proto/src/main/proto/brush_family.proto index 65f6d26b..5dbb150b 100644 --- a/ink-proto/src/main/proto/brush_family.proto +++ b/ink-proto/src/main/proto/brush_family.proto @@ -474,7 +474,7 @@ message BrushPaint { // (self overlap is discarded). More complex examples may involve color or // opacity variations (e.g. with // `BrushBehavior::Target::HUE_OFFSET_IN_RADIANS`), or complex textures (e.g. - // with `TextureMapping::kStamping`). + // with `BrushPaint::StampingTexture`). enum SelfOverlap { SELF_OVERLAP_UNSPECIFIED = 0 [(ink.proto.enum_value_min_version) = 0]; SELF_OVERLAP_ANY = 1 [(ink.proto.enum_value_min_version) = 0]; @@ -1349,8 +1349,22 @@ message ColorFunction { option (ink.proto.message_min_version) = 0; oneof function { + // Applies a multiplier to the color's opacity. The multiplier must be + // finite and nonnegative. Typical multiplier values range between 0 and 1. float opacity_multiplier = 1 [(ink.proto.field_min_version) = 0]; + // Ignores the original color and replaces it with a fixed color. Color replace_color = 2 [(ink.proto.field_min_version) = 0]; + // Applies an additive offset to the color's hue angle. The offset angle + // must be finite, but can be positive or negative (or zero). + float hue_offset_radians = 3 [(ink.proto.field_min_version) = 2147483647]; + // Applies a multiplier to the color's saturation. The multiplier must be + // finite and nonnegative. Typical multiplier values range between 0 and 1. + float saturation_multiplier = 4 + [(ink.proto.field_min_version) = 2147483647]; + // Applies an additive offset to the color's luminosity. The offset must be + // finite, but can be positive or negative (or zero). Typical offset values + // range between -1 and 1. + float luminosity_offset = 5 [(ink.proto.field_min_version) = 2147483647]; } } From f7eb0c3f5e6b1b42cbebc1b884a7fd7395dc924a Mon Sep 17 00:00:00 2001 From: Maxwell Metzger Mitchell <60010512+maxmmitchell@users.noreply.github.com> Date: Tue, 26 May 2026 15:57:48 +0000 Subject: [PATCH 2/2] Fix copyright year --- .../java/com/example/cahier/features/drawing/CustomBrushes.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt b/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt index 76a4b844..b53db861 100644 --- a/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt +++ b/app/src/main/java/com/example/cahier/features/drawing/CustomBrushes.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 Google LLC. All rights reserved. + * Copyright 2025 Google LLC. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.