Skip to content

Commit

Permalink
Merge pull request #80 from Fbada006/caleb/task/detect-pressure
Browse files Browse the repository at this point in the history
Pressure Detection
  • Loading branch information
CalebKL authored Sep 3, 2024
2 parents b355eef + 8515848 commit 6ea71d4
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 35 deletions.
2 changes: 2 additions & 0 deletions artmaker/src/main/java/io/artmaker/ArtMaker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ fun ArtMaker(
backgroundImage = viewModel.backgroundImage.value,
isFullScreenMode = isFullScreenEnabled,
shouldUseStylusOnly = state.shouldUseStylusOnly,
shouldDetectPressure = state.shouldDetectPressure,
canShowEnableStylusDialog = state.canShowEnableStylusDialog,
canShowDisableStylusDialog = state.canShowDisableStylusDialog,
),
Expand All @@ -138,6 +139,7 @@ fun ArtMaker(
modifier = Modifier
.padding(top = dimensionResource(id = R.dimen.Padding8), end = dimensionResource(id = R.dimen.Padding12), start = dimensionResource(id = R.dimen.Padding12)),
shouldUseStylusOnly = state.shouldUseStylusOnly,
shouldDetectPressure = state.shouldDetectPressure,
)
}
AnimatedVisibility(visible = !isFullScreenEnabled) {
Expand Down
1 change: 1 addition & 0 deletions artmaker/src/main/java/io/artmaker/ArtMakerUIState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ data class ArtMakerUIState(
val canUndo: Boolean = false,
val canClear: Boolean = false,
val shouldUseStylusOnly: Boolean = false,
val shouldDetectPressure: Boolean = false,
val canShowEnableStylusDialog: Boolean = true,
val canShowDisableStylusDialog: Boolean = true,
)
49 changes: 33 additions & 16 deletions artmaker/src/main/java/io/artmaker/ArtMakerViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import io.artmaker.actions.DrawEvent
import io.artmaker.actions.ExportType
import io.artmaker.data.ArtMakerSharedPreferences
import io.artmaker.data.PreferenceKeys
import io.artmaker.data.PreferenceKeys.SELECTED_STROKE_WIDTH
import io.artmaker.data.PreferenceKeys.PREF_SELECTED_STROKE_WIDTH
import io.artmaker.export.DrawingManager
import io.artmaker.models.PointsData
import io.artmaker.utils.saveToDisk
Expand All @@ -55,23 +55,27 @@ internal class ArtMakerViewModel(
private var _uiState = MutableStateFlow(
value = ArtMakerUIState(
strokeColour = preferences.get(
key = PreferenceKeys.SELECTED_STROKE_COLOUR,
key = PreferenceKeys.PREF_SELECTED_STROKE_COLOUR,
defaultValue = Color.Red.toArgb(),
),
strokeWidth = preferences.get(
key = SELECTED_STROKE_WIDTH,
key = PREF_SELECTED_STROKE_WIDTH,
defaultValue = 5,
),
shouldUseStylusOnly = preferences.get(
key = PreferenceKeys.USE_STYLUS_ONLY,
key = PreferenceKeys.PREF_USE_STYLUS_ONLY,
false,
),
shouldDetectPressure = preferences.get(
key = PreferenceKeys.PREF_DETECT_PRESSURE,
false,
),
canShowEnableStylusDialog = preferences.get(
key = PreferenceKeys.SHOW_ENABLE_STYLUS_DIALOG,
key = PreferenceKeys.PREF_SHOW_ENABLE_STYLUS_DIALOG,
true,
),
canShowDisableStylusDialog = preferences.get(
key = PreferenceKeys.SHOW_DISABLE_STYLUS_DIALOG,
key = PreferenceKeys.PREF_SHOW_DISABLE_STYLUS_DIALOG,
true,
),
),
Expand Down Expand Up @@ -103,6 +107,7 @@ internal class ArtMakerViewModel(
is ArtMakerAction.SelectStrokeColour -> updateStrokeColor(colour = action.color)
is ArtMakerAction.SetStrokeWidth -> selectStrokeWidth(strokeWidth = action.strokeWidth)
is ArtMakerAction.UpdateSetStylusOnly -> updateStylusSetting(useStylusOnly = action.shouldUseStylusOnly)
is ArtMakerAction.UpdateSetPressureDetection -> updatePressureSetting(detectPressure = action.shouldDetectPressure)
is ArtMakerAction.UpdateEnableStylusDialogShow -> updateEnableStylusDialog(canShow = action.canShowEnableStylusDialog)
is ArtMakerAction.UpdateDisableStylusDialogShow -> updateDisableStylusDialog(canShow = action.canShowDisableStylusDialog)
}
Expand Down Expand Up @@ -141,13 +146,13 @@ internal class ArtMakerViewModel(

private fun updateStrokeColor(colour: Color) {
preferences.set(
key = PreferenceKeys.SELECTED_STROKE_COLOUR,
key = PreferenceKeys.PREF_SELECTED_STROKE_COLOUR,
value = colour.toArgb(),
)
_uiState.update {
it.copy(
strokeColour = preferences.get(
key = PreferenceKeys.SELECTED_STROKE_COLOUR,
key = PreferenceKeys.PREF_SELECTED_STROKE_COLOUR,
defaultValue = 0,
),
)
Expand All @@ -160,49 +165,61 @@ internal class ArtMakerViewModel(

private fun selectStrokeWidth(strokeWidth: Int) {
preferences.set(
key = SELECTED_STROKE_WIDTH,
key = PREF_SELECTED_STROKE_WIDTH,
value = strokeWidth,
)
_uiState.update {
it.copy(
strokeWidth = preferences.get(
SELECTED_STROKE_WIDTH,
PREF_SELECTED_STROKE_WIDTH,
defaultValue = 5,
),
)
}
}

private fun updateStylusSetting(useStylusOnly: Boolean) {
preferences.set(PreferenceKeys.USE_STYLUS_ONLY, useStylusOnly)
preferences.set(PreferenceKeys.PREF_USE_STYLUS_ONLY, useStylusOnly)
_uiState.update {
it.copy(
shouldUseStylusOnly = preferences.get(
key = PreferenceKeys.USE_STYLUS_ONLY,
key = PreferenceKeys.PREF_USE_STYLUS_ONLY,
false,
),
)
}
}

private fun updatePressureSetting(detectPressure: Boolean) {
preferences.set(PreferenceKeys.PREF_DETECT_PRESSURE, detectPressure)
_uiState.update {
it.copy(
shouldDetectPressure = preferences.get(
key = PreferenceKeys.PREF_DETECT_PRESSURE,
false,
),
)
}
}

private fun updateEnableStylusDialog(canShow: Boolean) {
preferences.set(PreferenceKeys.SHOW_ENABLE_STYLUS_DIALOG, canShow)
preferences.set(PreferenceKeys.PREF_SHOW_ENABLE_STYLUS_DIALOG, canShow)
_uiState.update {
it.copy(
canShowEnableStylusDialog = preferences.get(
key = PreferenceKeys.SHOW_ENABLE_STYLUS_DIALOG,
key = PreferenceKeys.PREF_SHOW_ENABLE_STYLUS_DIALOG,
true,
),
)
}
}

private fun updateDisableStylusDialog(canShow: Boolean) {
preferences.set(PreferenceKeys.SHOW_DISABLE_STYLUS_DIALOG, canShow)
preferences.set(PreferenceKeys.PREF_SHOW_DISABLE_STYLUS_DIALOG, canShow)
_uiState.update {
it.copy(
canShowDisableStylusDialog = preferences.get(
key = PreferenceKeys.SHOW_DISABLE_STYLUS_DIALOG,
key = PreferenceKeys.PREF_SHOW_DISABLE_STYLUS_DIALOG,
true,
),
)
Expand Down
1 change: 1 addition & 0 deletions artmaker/src/main/java/io/artmaker/DrawState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal data class DrawState(
val shouldTriggerArtExport: Boolean,
val isFullScreenMode: Boolean,
val shouldUseStylusOnly: Boolean,
val shouldDetectPressure: Boolean,
val canShowEnableStylusDialog: Boolean,
val canShowDisableStylusDialog: Boolean,
)
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ sealed interface ArtMakerAction {
data class SelectStrokeColour(val color: Color) : ArtMakerAction
data class SetStrokeWidth(val strokeWidth: Int) : ArtMakerAction
data class UpdateSetStylusOnly(val shouldUseStylusOnly: Boolean) : ArtMakerAction
data class UpdateSetPressureDetection(val shouldDetectPressure: Boolean) : ArtMakerAction
class UpdateEnableStylusDialogShow(val canShowEnableStylusDialog: Boolean) : ArtMakerAction
class UpdateDisableStylusDialogShow(val canShowDisableStylusDialog: Boolean) : ArtMakerAction
}
Expand Down
4 changes: 2 additions & 2 deletions artmaker/src/main/java/io/artmaker/actions/DrawEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import androidx.compose.ui.geometry.Offset
* Events that happen during drawing
*/
sealed interface DrawEvent {
data class AddNewShape(val offset: Offset) : DrawEvent
data class UpdateCurrentShape(val offset: Offset) : DrawEvent
data class AddNewShape(val offset: Offset, val pressure: Float) : DrawEvent
data class UpdateCurrentShape(val offset: Offset, val pressure: Float) : DrawEvent
data object UndoLastShapePoint : DrawEvent
data object Undo : DrawEvent
data object Redo : DrawEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import io.artmaker.DrawState
import io.artmaker.actions.ArtMakerAction
import io.artmaker.actions.DrawEvent
import io.artmaker.models.ArtMakerConfiguration
import io.artmaker.models.alpha
import io.artmaker.utils.isStylusInput
import io.artmaker.utils.validateEvent
import io.fbada006.artmaker.R
Expand Down Expand Up @@ -159,6 +160,7 @@ internal fun ArtMakerDrawScreen(
}
.pointerInteropFilter { event ->
val offset = Offset(event.x, event.y)
val pressure = event.getPressure(event.actionIndex)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
getDialogType(context, event, state.shouldUseStylusOnly)?.let { type ->
Expand All @@ -167,14 +169,13 @@ internal fun ArtMakerDrawScreen(
}

if (!event.validateEvent(context, state.shouldUseStylusOnly)) return@pointerInteropFilter false

onDrawEvent(DrawEvent.AddNewShape(offset))
onDrawEvent(DrawEvent.AddNewShape(offset, pressure))
}

MotionEvent.ACTION_MOVE -> {
val clampedOffset =
Offset(x = offset.x, y = clamp(offset.y, 0f, maxDrawingHeight))
onDrawEvent(DrawEvent.UpdateCurrentShape(clampedOffset))
onDrawEvent(DrawEvent.UpdateCurrentShape(clampedOffset, pressure))
}

MotionEvent.ACTION_CANCEL -> {
Expand All @@ -193,13 +194,14 @@ internal fun ArtMakerDrawScreen(
size = Size(bitmapWidth.toFloat(), bitmapHeight.toFloat()),
)
}

state.pathList.forEach { data ->
drawPoints(
points = data.points,
pointMode = if (data.points.size == 1) PointMode.Points else PointMode.Polygon, // Draw a point if the shape has only one item otherwise a free flowing shape
color = data.strokeColor,
alpha = data.alpha(state.shouldDetectPressure),
strokeWidth = data.strokeWidth,
alpha = data.alpha,
)
}
}
Expand Down
24 changes: 23 additions & 1 deletion artmaker/src/main/java/io/artmaker/composables/StrokeSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,17 @@ import io.artmaker.utils.isStylusConnected
import io.fbada006.artmaker.R

@Composable
fun StrokeSettings(strokeWidth: Int, shouldUseStylusOnly: Boolean, onAction: (ArtMakerAction) -> Unit, configuration: ArtMakerConfiguration, modifier: Modifier = Modifier) {
fun StrokeSettings(
strokeWidth: Int,
shouldUseStylusOnly: Boolean,
shouldDetectPressure: Boolean,
onAction: (ArtMakerAction) -> Unit,
configuration: ArtMakerConfiguration,
modifier: Modifier = Modifier,
) {
var sliderPosition by remember { mutableIntStateOf(strokeWidth) }
var stylusOnly by remember { mutableStateOf(shouldUseStylusOnly) }
var detectPressure by remember { mutableStateOf(shouldDetectPressure) }

Column(modifier = modifier, verticalArrangement = Arrangement.SpaceEvenly) {
Slider(
Expand Down Expand Up @@ -69,6 +77,20 @@ fun StrokeSettings(strokeWidth: Int, shouldUseStylusOnly: Boolean, onAction: (Ar
},
)
}
HorizontalDivider()
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
Text(
text = stringResource(R.string.enable_pressure_detection),
style = MaterialTheme.typography.bodyLarge,
)
Switch(
checked = detectPressure,
onCheckedChange = {
detectPressure = !detectPressure
onAction(ArtMakerAction.UpdateSetPressureDetection(shouldDetectPressure = detectPressure))
},
)
}
}
}
}
12 changes: 6 additions & 6 deletions artmaker/src/main/java/io/artmaker/data/PreferenceKeys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
package io.artmaker.data

object PreferenceKeys {
const val SELECTED_BACKGROUND_COLOUR = "com.artmaker.sharedpreferences.selectedBackgroundColour"
const val SELECTED_STROKE_COLOUR = "com.artmaker.sharedpreferences.selectedStrokeColour"
const val SELECTED_STROKE_WIDTH = "com.artmaker.sharedpreferences.selectedStrokeWidth"
const val USE_STYLUS_ONLY = "com.artmaker.sharedpreferences.useStylusOnly"
const val SHOW_ENABLE_STYLUS_DIALOG = "com.artmaker.sharedpreferences.showEnableStylusDialog"
const val SHOW_DISABLE_STYLUS_DIALOG = "com.artmaker.sharedpreferences.showDisableStylusDialog"
const val PREF_SELECTED_STROKE_COLOUR = "com.artmaker.sharedpreferences.selectedStrokeColour"
const val PREF_SELECTED_STROKE_WIDTH = "com.artmaker.sharedpreferences.selectedStrokeWidth"
const val PREF_USE_STYLUS_ONLY = "com.artmaker.sharedpreferences.useStylusOnly"
const val PREF_DETECT_PRESSURE = "com.artmaker.sharedpreferences.detectDrawingPressure"
const val PREF_SHOW_ENABLE_STYLUS_DIALOG = "com.artmaker.sharedpreferences.showEnableStylusDialog"
const val PREF_SHOW_DISABLE_STYLUS_DIALOG = "com.artmaker.sharedpreferences.showDisableStylusDialog"
}
13 changes: 8 additions & 5 deletions artmaker/src/main/java/io/artmaker/export/DrawingManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,31 @@ internal class DrawingManager {

fun onDrawEvent(event: DrawEvent, strokeColor: Int, strokeWidth: Int) {
when (event) {
is DrawEvent.AddNewShape -> addNewShape(event.offset, strokeColor, strokeWidth)
is DrawEvent.AddNewShape -> addNewShape(event.offset, strokeColor, strokeWidth, event.pressure)
DrawEvent.UndoLastShapePoint -> undoLastShapePoint()
is DrawEvent.UpdateCurrentShape -> updateCurrentShape(event.offset)
is DrawEvent.UpdateCurrentShape -> updateCurrentShape(event.offset, event.pressure)
DrawEvent.Clear -> clear()
DrawEvent.Redo -> redo()
DrawEvent.Undo -> undo()
}
}

private fun addNewShape(offset: Offset, strokeColor: Int, strokeWidth: Int) {
private fun addNewShape(offset: Offset, strokeColor: Int, strokeWidth: Int, pressure: Float) {
val data = PointsData(
points = mutableStateListOf(offset),
strokeColor = Color(strokeColor),
strokeWidth = strokeWidth.toFloat(),
alphas = mutableStateListOf(pressure),
)

_pathList.add(data)
_undoRedoState.update { computeUndoRedoState() }
}

private fun updateCurrentShape(offset: Offset) {
private fun updateCurrentShape(offset: Offset, pressure: Float) {
val idx = _pathList.lastIndex
_pathList[idx].points.add(offset)
_pathList[idx].alphas.add(pressure)
}

private fun undoLastShapePoint() {
Expand Down Expand Up @@ -91,7 +94,7 @@ internal class DrawingManager {
return UndoRedoState(
canUndo = _pathList.isNotEmpty(),
canRedo = undoStack.isNotEmpty(),
canClear = _pathList.isNotEmpty(),
canClear = _pathList.isNotEmpty() || undoStack.isNotEmpty(),
)
}
}
Expand Down
11 changes: 10 additions & 1 deletion artmaker/src/main/java/io/artmaker/models/PointsData.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,14 @@ internal data class PointsData(
var points: SnapshotStateList<Offset>,
val strokeWidth: Float = 15f,
val strokeColor: Color,
val alpha: Float = 1f,
val alphas: MutableList<Float>,
)

// The alpha will always be 1 during no pressure detection
internal fun PointsData.alpha(detectPressure: Boolean): Float {
return if (detectPressure) {
this.alphas.average().coerceAtLeast(0.0).coerceAtMost(1.0).toFloat()
} else {
1.0f
}
}
1 change: 1 addition & 0 deletions artmaker/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@
<string name="non_stylus_input_detected_message">We have detected that you are not using your stylus to draw but you have enabled the stylus only input setting. Please disable this using the pencil icon at the bottom of the screen if you wish to use other input types to draw.</string>
<string name="do_not_show_again">Do not show again.</string>
<string name="use_stylus_only">Use stylus only</string>
<string name="enable_pressure_detection">Enable pressure detection</string>
<string name="got_it">Got it</string>
</resources>

0 comments on commit 6ea71d4

Please sign in to comment.