diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b948907 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,34 @@ +# CHANGELOG + +## 1.4.0 (2022-04-29) +* add `DrawingViewState` containing all the needed information to draw image on canvas +* bump Kotlin to 1.6.21, Gradle plugin to 7.1.3 +* bump buildToolsVersion to 32.0.0, compileSdkVersion and targetSdkVersion to 32 + +## 1.3.0 (2020-08-05) +* bump Gradle plugin to 7.0.0 +* update publish scripts + +## 1.2.0 (2021-03-24) +* add `undoAll()`, `redoAll()`, `clearRedoHistory()`, `isDrawingEmpty()` +* add explicit callbacks parameter names to `listenerEmptyState` and `listenerDrawingInProgress` +* add option to define canvas colour +* remove reference to additional canvas +* better documentation of public methods and parameters + +## 1.1.0 (2021-03-23) +* add `sizeChanged` flag to determine when to create new canvas instead of creating it from + bitmap on every size change +* bump Kotlin to 1.4.31, Gradle plugin to 4.1.3, Build tools to 30.0.3 + +## 1.0.2 (2020-12-05) +* add documentation +* remove deprecated Kotlin extensions from example app +* bump to Kotlin 1.4.20 and Gradle plugin 4.1.1 + +## 1.0.1 (2020-08-24) +* add license +* bump Kotlin to 1.4.0, Gradle plugin to 4.0.1 and build tools to 30.0.2 + +## 1.0.0 (2020-05-26) +* initial public release \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 24287a9..8f6ef76 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,11 +17,6 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - sourceSets { main.java.srcDirs += 'src/main/kotlin' } @@ -40,10 +35,10 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.appcompat:appcompat:1.4.0' implementation project(":drawingview") testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8be1b4c..fba4038 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,19 +3,22 @@ package="com.vojtkovszky.drawingview.example"> - + + - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3150a38..4aad04c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ buildscript { ext { - kotlin_version = "1.5.21" /* https://github.com/JetBrains/kotlin */ - dokka_version = '1.5.0' /* https://github.com/Kotlin/dokka/releases */ + kotlin_version = "1.6.21" /* https://github.com/JetBrains/kotlin */ + dokka_version = '1.6.20' /* https://github.com/Kotlin/dokka/releases */ } repositories { @@ -10,7 +10,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' + classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version" } diff --git a/drawingview/build.gradle b/drawingview/build.gradle index e948cf7..47880ff 100644 --- a/drawingview/build.gradle +++ b/drawingview/build.gradle @@ -12,7 +12,6 @@ android { defaultConfig { minSdkVersion project.properties['minSdkVersion'] as Integer targetSdkVersion project.properties['targetSdkVersion'] as Integer - versionName project.properties['versionName'] as String } sourceSets { diff --git a/drawingview/gradle.properties b/drawingview/gradle.properties index 198b997..b16d25c 100644 --- a/drawingview/gradle.properties +++ b/drawingview/gradle.properties @@ -3,4 +3,4 @@ buildTypes=debug,release groupId=com.github.mvojtkovszky artifactId=DrawingView moduleId=drawingview -versionName=1.3.0 \ No newline at end of file +versionName=1.4.0 \ No newline at end of file diff --git a/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingView.kt b/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingView.kt index fc012ae..614c717 100644 --- a/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingView.kt +++ b/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingView.kt @@ -33,21 +33,19 @@ class DrawingView @JvmOverloads constructor( strokeWidth = 30f } - private val drawPathHistory = mutableListOf() - private val drawPaintHistory = mutableListOf() - private val undonePaths = mutableListOf() - private val undonePaints = mutableListOf() + /** + * State holding all necessary information to populate the view and keeping track of it's + * active history + */ + var state = DrawingViewState() + set(value) { + field = value + invalidate() + } private var xStart: Float = 0f // reference positions for last move (x) private var yStart: Float = 0f // reference positions for last move (y) private var isCurrentlyMoving = false - private var isDrawingEmpty = true // track if anything written - set(value) { - field = value - if (value) { - listenerEmptyState?.let { it(true) } - } - } // endregion // region Public attributes @@ -117,11 +115,13 @@ class DrawingView @JvmOverloads constructor( override fun onDraw(canvas: Canvas) { canvas.drawColor(canvasColor) - for (i in drawPathHistory.indices) { - val path = drawPathHistory[i] - val paint = drawPaintHistory[i] - canvas.drawPath(path, paint) + val numStepsInHistory = state.numHistorySteps() + if (numStepsInHistory > 0) { + for (i in 0 until numStepsInHistory) { + canvas.drawPath(state.getPathFromHistory(i), state.getPaintFromHistory(i)) + } } + canvas.drawPath(drawPath, drawPaint) super.onDraw(canvas) @@ -140,8 +140,7 @@ class DrawingView @JvmOverloads constructor( when (event.action) { MotionEvent.ACTION_DOWN -> { - undonePaths.clear() - undonePaints.clear() + state.clearRedoHistory() drawPath.reset() drawPath.moveTo(touchX, touchY) @@ -149,7 +148,7 @@ class DrawingView @JvmOverloads constructor( xStart = touchX yStart = touchY - listenerDrawingInProgress?.let { it(true) } + listenerDrawingInProgress?.invoke(true) } MotionEvent.ACTION_MOVE -> { @@ -169,16 +168,19 @@ class DrawingView @JvmOverloads constructor( drawPath.addCircle(touchX, touchY, 0.1f, Path.Direction.CW) } - drawPathHistory.add(drawPath) - drawPaintHistory.add(Paint(drawPaint)) + val drawingEmptyBeforeAdding = state.isHistoryEmpty() + + state.addToHistory(drawPath, drawPaint) drawPath = Path() - if (isDrawingEmpty) listenerEmptyState?.let { it(false) } - isDrawingEmpty = false + // moved from empty to no longer empty + if (drawingEmptyBeforeAdding) { + listenerEmptyState?.invoke(false) + } isCurrentlyMoving = false - listenerDrawingInProgress?.let { it(false) } + listenerDrawingInProgress?.invoke(false) } else -> return false @@ -193,28 +195,22 @@ class DrawingView @JvmOverloads constructor( * Clear canvas. Will also clear all history */ fun startNew() { - drawPathHistory.clear() - drawPaintHistory.clear() - undonePaths.clear() - undonePaints.clear() - + state.startNew() invalidate() - - isDrawingEmpty = true + listenerEmptyState?.invoke(true) } /** * Undo */ fun undo() { - if (drawPathHistory.size > 0) { - undonePaths.add(drawPathHistory.removeAt(drawPathHistory.size - 1)) - undonePaints.add(drawPaintHistory.removeAt(drawPaintHistory.size - 1)) + if (!state.isHistoryEmpty()) { + state.undo() invalidate() } - if (drawPathHistory.isEmpty()) { - isDrawingEmpty = true + if (state.isHistoryEmpty()) { + listenerEmptyState?.invoke(true) } } @@ -224,7 +220,7 @@ class DrawingView @JvmOverloads constructor( * [redo] or [redoAll] */ fun undoAll() { - repeat(drawPathHistory.size) { + repeat(state.numHistorySteps()) { undo() } } @@ -233,13 +229,9 @@ class DrawingView @JvmOverloads constructor( * Redo */ fun redo() { - if (undonePaths.size > 0) { - drawPathHistory.add(undonePaths.removeAt(undonePaths.size - 1)) - drawPaintHistory.add(undonePaints.removeAt(undonePaints.size - 1)) - + if (!state.isUndoneEmpty()) { + state.redo() invalidate() - - isDrawingEmpty = false } } @@ -247,7 +239,7 @@ class DrawingView @JvmOverloads constructor( * Redo all known undone steps */ fun redoAll() { - repeat(undonePaths.size) { + repeat(state.numUndoneSteps()) { redo() } } @@ -256,7 +248,7 @@ class DrawingView @JvmOverloads constructor( * Clears undone history, essentially making [redo] do nothing */ fun clearRedoHistory() { - undonePaths.clear() + state.clearRedoHistory() } /** @@ -271,7 +263,7 @@ class DrawingView @JvmOverloads constructor( * Determine if canvas is empty. */ fun isDrawingEmpty(): Boolean { - return isDrawingEmpty + return state.isHistoryEmpty() } // endregion } \ No newline at end of file diff --git a/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingViewState.kt b/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingViewState.kt new file mode 100644 index 0000000..5838ccd --- /dev/null +++ b/drawingview/src/main/kotlin/com/vojtkovszky/drawingview/DrawingViewState.kt @@ -0,0 +1,85 @@ +package com.vojtkovszky.drawingview + +import android.graphics.Paint +import android.graphics.Path +import java.io.Serializable + +/** + * Class holding all the necessary information required to draw paths and paints + * on the canvas. + */ +class DrawingViewState: Serializable { + + private val drawPathHistory = mutableListOf() + private val drawPaintHistory = mutableListOf() + private val undonePaths = mutableListOf() + private val undonePaints = mutableListOf() + + // add path and paint to history + internal fun addToHistory(path: Path, paint: Paint) { + drawPathHistory.add(path) + drawPaintHistory.add(Paint(paint)) + } + + // clears redo history by cleaning undone paths and paints + internal fun clearRedoHistory() { + undonePaths.clear() + undonePaints.clear() + } + + // return single path from history on index + internal fun getPathFromHistory(index: Int): Path { + return drawPathHistory[index] + } + + // return single paint from history on index + internal fun getPaintFromHistory(index: Int): Paint { + return drawPaintHistory[index] + } + + // return number of available undone steps + internal fun numUndoneSteps(): Int { + return undonePaths.size + } + + // return number of available history steps + internal fun numHistorySteps(): Int { + return drawPathHistory.size + } + + // moves latest path and pant from undone list to history list + // by removing from undone list and adding to history list + internal fun redo() { + drawPathHistory.add(undonePaths.removeAt(undonePaths.size - 1)) + drawPaintHistory.add(undonePaints.removeAt(undonePaints.size - 1)) + } + + // reset the history and undone lists + internal fun startNew() { + drawPathHistory.clear() + drawPaintHistory.clear() + undonePaths.clear() + undonePaints.clear() + } + + // moves latest path and pant from history list to undone list + // by removing from history list and adding to undone list + internal fun undo() { + undonePaths.add(drawPathHistory.removeAt(drawPathHistory.size - 1)) + undonePaints.add(drawPaintHistory.removeAt(drawPaintHistory.size - 1)) + } + + /** + * Determine if we have any paths or paints in history + */ + fun isHistoryEmpty(): Boolean { + return drawPathHistory.isEmpty() //&& drawPaintHistory.isEmpty() + } + + /** + * Determine if we have any paths or paints in undo history + */ + fun isUndoneEmpty(): Boolean { + return undonePaths.isEmpty() //&& undonePaints.isEmpty() + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 4ff0831..651e144 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,8 +20,14 @@ android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official +# Software Components will not be created automatically for Maven publishing from +# Android Gradle Plugin 8.0. To opt-in to the future behavior, set the Gradle property +# android.disableAutomaticComponentCreation=true in the `gradle.properties` file or use the new +# publishing DSL. +android.disableAutomaticComponentCreation=true + # base versions -buildToolsVersion=30.0.3 -compileSdkVersion=30 +buildToolsVersion=32.0.0 +compileSdkVersion=32 minSdkVersion=16 -targetSdkVersion=30 +targetSdkVersion=32 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 796df92..8b17419 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Aug 05 09:37:59 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME