├── .gitignore
├── LICENSE
├── README.md
├── canvaseditor
├── .gitignore
├── bintray.gradle
├── build.gradle
├── canvaseditor.iml
├── consumer-rules.pro
├── install.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── outsbook
│ │ └── libs
│ │ └── canvaseditor
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── outsbook
│ │ │ └── libs
│ │ │ └── canvaseditor
│ │ │ ├── CanvasEditorView.kt
│ │ │ ├── constants
│ │ │ ├── ActionMode.kt
│ │ │ ├── ConstantSticker.kt
│ │ │ └── ConstantStickerIcon.kt
│ │ │ ├── enums
│ │ │ └── DrawType.kt
│ │ │ ├── events
│ │ │ ├── DeleteIconEvent.kt
│ │ │ ├── DoneIconEvent.kt
│ │ │ ├── FlipIconEvent.kt
│ │ │ └── ZoomIconEvent.kt
│ │ │ ├── listeners
│ │ │ ├── CanvasEditorListener.kt
│ │ │ ├── PaintViewListener.kt
│ │ │ ├── StickerIconListener.kt
│ │ │ └── StickerViewListener.kt
│ │ │ ├── models
│ │ │ ├── DrawObject.kt
│ │ │ └── PathAndPaint.kt
│ │ │ ├── paints
│ │ │ └── PaintView.kt
│ │ │ └── stickers
│ │ │ ├── BitmapSticker.kt
│ │ │ ├── DrawableSticker.kt
│ │ │ ├── Sticker.kt
│ │ │ ├── StickerIcon.kt
│ │ │ ├── StickerView.kt
│ │ │ └── TextSticker.kt
│ └── res
│ │ └── drawable
│ │ ├── ic_close_white_20dp.xml
│ │ ├── ic_done_white_20dp.xml
│ │ ├── ic_flip_white_20dp.xml
│ │ ├── ic_rotate_scale_white_17dp.xml
│ │ └── shape_transfarent_background.xml
│ └── test
│ └── java
│ └── com
│ └── outsbook
│ └── libs
│ └── canvaseditor
│ └── ExampleUnitTest.kt
├── example-java
├── .gitignore
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── outsbook
│ │ │ └── examplejava
│ │ │ └── ExampleInstrumentedTest.java
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── outsbook
│ │ │ │ └── examplejava
│ │ │ │ └── MainActivity.java
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ ├── app_icon.png
│ │ │ ├── ic_cancel_yellow_24dp.xml
│ │ │ ├── ic_color_black_24dp.xml
│ │ │ ├── ic_color_yellow_24dp.xml
│ │ │ ├── ic_delete_white_24dp.xml
│ │ │ ├── ic_download_gray_24dp.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_minus_black_24dp.xml
│ │ │ ├── ic_minus_yellow_24dp.xml
│ │ │ ├── ic_panorama_240dp.xml
│ │ │ ├── ic_plus_black_24dp.xml
│ │ │ ├── ic_plus_yellow_24dp.xml
│ │ │ ├── ic_redo_white_24dp.xml
│ │ │ ├── ic_sticker_text_white_24dp.xml
│ │ │ ├── ic_sticker_white_24dp.xml
│ │ │ ├── ic_text_white_24dp.xml
│ │ │ ├── ic_undo_white_24dp.xml
│ │ │ ├── shape_delete_bg.xml
│ │ │ ├── shape_redo_bg.xml
│ │ │ └── shape_undo_bg.xml
│ │ │ ├── layout
│ │ │ └── activity_main.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── outsbook
│ │ └── examplejava
│ │ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
├── example-kotlin
├── .gitignore
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── outsbook
│ │ │ └── examplekotlin
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── outsbook
│ │ │ │ └── examplekotlin
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ ├── app_icon.png
│ │ │ ├── ic_cancel_yellow_24dp.xml
│ │ │ ├── ic_color_black_24dp.xml
│ │ │ ├── ic_color_yellow_24dp.xml
│ │ │ ├── ic_delete_white_24dp.xml
│ │ │ ├── ic_download_gray_24dp.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── ic_minus_black_24dp.xml
│ │ │ ├── ic_minus_yellow_24dp.xml
│ │ │ ├── ic_panorama_240dp.xml
│ │ │ ├── ic_plus_black_24dp.xml
│ │ │ ├── ic_plus_yellow_24dp.xml
│ │ │ ├── ic_redo_white_24dp.xml
│ │ │ ├── ic_sticker_text_white_24dp.xml
│ │ │ ├── ic_sticker_white_24dp.xml
│ │ │ ├── ic_text_white_24dp.xml
│ │ │ ├── ic_undo_white_24dp.xml
│ │ │ ├── shape_delete_bg.xml
│ │ │ ├── shape_redo_bg.xml
│ │ │ └── shape_undo_bg.xml
│ │ │ ├── layout
│ │ │ └── activity_main.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_foreground.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── outsbook
│ │ └── examplekotlin
│ │ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
└── screenshot
├── screenshot_1.png
├── screenshot_2.png
├── screenshot_3.png
├── screenshot_4.png
├── screenshot_5.png
├── screenshot_6.png
└── screenshot_7.png
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Gradle
6 | # ------
7 | .gradle
8 | /build
9 | /buildSrc/build
10 | /buildSrc/subprojects/*/build
11 | /subprojects/*/build
12 | /subprojects/docs/src/samples/*/*/build
13 | /subprojects/internal-android-performance-testing/build-android-libs
14 |
15 | # IDEA
16 | # ----
17 | .idea
18 | .shelf
19 | /*.iml
20 | /*.ipr
21 | /*.iws
22 | /buildSrc/.idea
23 | /buildSrc/.shelf
24 | /buildSrc/*.iml
25 | /buildSrc/*.ipr
26 | /buildSrc/*.iws
27 | /buildSrc/out
28 | /buildSrc/subprojects/*/*.iml
29 | /buildSrc/subprojects/*/out
30 | /out
31 | /subprojects/*/*.iml
32 | /subprojects/*/out
33 | /.teamcity/*.iml
34 | /.teamcity/target
35 |
36 | # Eclipse
37 | # -------
38 | *.classpath
39 | *.project
40 | *.settings
41 | /bin
42 | /subprojects/*/bin
43 | atlassian-ide-plugin.xml
44 | .metadata/
45 |
46 | # NetBeans
47 | # --------
48 | .nb-gradle
49 | .nb-gradle-properties
50 |
51 | # Vim
52 | # ---
53 | *.sw[op]
54 |
55 | # Emacs
56 | # -----
57 | *~
58 | \#*\#
59 | .\#*
60 |
61 | # Textmate
62 | # --------
63 | .textmate
64 |
65 | # Sublime Text
66 | # ------------
67 | *.sublime-*
68 |
69 | # jEnv
70 | # ----
71 | .java-version
72 |
73 | # macOS
74 | # ----
75 | .DS_Store
76 |
77 | # HPROF
78 | # -----
79 | *.hprof
80 |
81 | # Work dirs
82 | # ---------
83 | /incoming-distributions
84 | /intTestHomeDir
85 |
86 | # Logs
87 | # ----
88 | /*.log
89 |
90 | # oh-my-zsh gradle plugin
91 | .gradletasknamecache
92 |
93 | # Files for the ART/Dalvik VM
94 | *.dex
95 |
96 | # Java class files
97 | *.class
98 |
99 | # Generated files
100 | bin/
101 | gen/
102 | out/
103 |
104 | # Gradle files
105 | .gradle/
106 | build/
107 |
108 | # Local configuration file (sdk path, etc)
109 | local.properties
110 |
111 | # Proguard folder generated by Eclipse
112 | proguard/
113 |
114 | # Log Files
115 | *.log
116 |
117 | # Android Studio Navigation editor temp files
118 | .navigation/
119 |
120 | # Android Studio captures folder
121 | captures/
122 |
123 | # Intellij
124 | *.iml
125 | .idea/workspace.xml
126 | .idea/tasks.xml
127 | .idea/gradle.xml
128 | .idea/dictionaries
129 | .idea/libraries
130 |
131 | # Keystore files
132 | *.jks
133 |
134 | # External native build folder generated in Android Studio 2.2 and later
135 | .externalNativeBuild
136 |
137 | # Google Services (e.g. APIs or Firebase)
138 | google-services.json
139 |
140 | # Freeline
141 | freeline.py
142 | freeline/
143 | freeline_project_description.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright(c) 2020, Shahin ShamS
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CanvasEditor
2 | A Canvas/Image Editor library with easy support for canvas/image editing using paints, drawable sticker, and text sticker in Android. The lib source code writeen using Kotlin language.
3 |
4 | ## Index
5 | | Title | Description/Methods |
6 | | ------------ | ------------ |
7 | | [**Getting Started**](#getting-started) | Install the canvas editor library to your project |
8 | | [**Setup the Canvas Editor**](#setup-the-canvas-editor) | Setup the canvas editor to your project activity and activity layout |
9 | | [**Drawing**](#drawing) | [1. setPaintColor(color: Int)](#1-setpaintcolorcolor-int)
[2. setStrokeWidth(strokeWidth: Float)](#2-setstrokewidthstrokeWidth-float) |
10 | | [**Drawable/Bitmap Sticker**](#drawablebitmap-sticker) | [1. addDrawableSticker(drawable: Drawable)](#1-adddrawablestickerdrawable-drawable)
[2. addBitmapSticker(bitmap: Bitmap)](#2-addbitmapstickerbitmap-bitmap)|
11 | | [**Text Sticker**](#text-sticker) | [1. addTextSticker(text: String, textColor: Int, typeface: Typeface?)](#1-addtextStickertext-string-textcolor-int-typeface-typeface)
[2. addDrawableTextSticker(drawable: Drawable, text: String, textColor: Int, typeface: Typeface?)](#2-adddrawabletextstickerdrawable-drawable-text-string-textColor-int-typeface-typeface) |
12 | | [**Active Sticker Methods**](#active-sticker-methods) | [1. removeActiveSticker()](#1-removeactivesticker)
[2. doneActiveSticker()](#2-doneactivesticker)
[3. flipActiveSticker()](#3-flipactivesticker)
[4. zoomAndRotateActiveSticker(motionEvent: MotionEvent)](#4-zoomandrotateactivestickermotionevent-motionevent) |
13 | | [**Canvas Editor Methods**](#canvas-editor-methods) | [1. undo()](#1-undo)
[2. redo()](#2-redo)
[3. removeAll()](#3-removeall)
[4. downloadBitmap(): Bitmap](#4-downloadbitmap-bitmap) |
14 | | [**Canvas Editor Callback**](#canvas-editor-callback) | Set the listener for access callback functions |
15 |
16 | ## Getting Started
17 | To include the library in your project just simply add the dependencies. Choose one from Gradle, and Maven
18 | #### Gradle
19 | ```groovy
20 | implementation 'com.outsbook.libs:canvaseditor:1.0.0'
21 | ```
22 | #### Maven
23 | ```xml
24 |
25 | com.outsbook.libs
26 | canvaseditor
27 | 1.0.0
28 | pom
29 |
30 | ```
31 |
32 | ## Setup the Canvas Editor
33 | #### Add the `CanvasEditorView` to your Activity/Fragment layout
34 | ```xml
35 |
39 | ```
40 | #### Get the `CanvasEditor` in your Activity
41 | ##### Kotlin
42 | ```kotlin
43 | import com.outsbook.libs.canvaseditor.CanvasEditorView
44 |
45 | class MainActivity : AppCompatActivity() {
46 | private lateinit var canvasEditor: CanvasEditorView
47 |
48 | override fun onCreate(savedInstanceState: Bundle?) {
49 | super.onCreate(savedInstanceState)
50 | setContentView(R.layout.activity_main)
51 | canvasEditor = findViewById(R.id.canvasEditor)
52 | }
53 | }
54 | ```
55 | ##### Java
56 | ```java
57 | import com.outsbook.libs.canvaseditor.CanvasEditorView;
58 |
59 | public class MainActivity extends AppCompatActivity {
60 | private CanvasEditorView canvasEditor;
61 |
62 | @Override
63 | protected void onCreate(Bundle savedInstanceState) {
64 | super.onCreate(savedInstanceState);
65 | setContentView(R.layout.activity_main);
66 | canvasEditor = findViewById(R.id.canvasEditor);
67 | }
68 | }
69 | ```
70 | ##### Preview
71 | 
72 |
73 | Now you are ready to play with `CanvasEditor`
74 |
75 | ## Drawing
76 | | # | Method | Action |
77 | | ------------ | ------------ | ------------ |
78 | | 1 | [setPaintColor(color: Int)](#1-setpaintcolorcolor-int) | Set the brush color to paint |
79 | | 2 | [setStrokeWidth(strokeWidth: Float)](#2-setstrokewidthstrokeWidth-float) | Set the brush stroke width to paint |
80 | #### 1. setPaintColor(color: Int)
81 | ##### Kotlin
82 | ```kotlin
83 | val color = ContextCompat.getColor(this, R.color.colorBlack)
84 | canvasEditor.setPaintColor(color)
85 | ```
86 | ##### Java
87 | ```java
88 | int color = ContextCompat.getColor(this, R.color.colorBlack);
89 | canvasEditor.setPaintColor(color);
90 | ```
91 | ##### Preview
92 | 
93 |
94 | #### 2. setStrokeWidth(strokeWidth: Float)
95 | ##### Kotlin
96 | ```kotlin
97 | val strokeWidth = 20f
98 | canvasEditor.setStrokeWidth(strokeWidth)
99 | ```
100 | ##### Java
101 | ```java
102 | float strokeWidth = 20f;
103 | canvasEditor.setStrokeWidth(strokeWidth);
104 | ```
105 | ##### Preview
106 | 
107 |
108 | ## Drawable/Bitmap Sticker
109 | | # | Method | Action |
110 | | ------------ | ------------ | ------------ |
111 | | 1 | [addDrawableSticker(drawable: Drawable)](#1-adddrawablestickerdrawable-drawable) | Add drawable sticker to the canvas editor |
112 | | 2 | [addBitmapSticker(bitmap: Bitmap)](#2-addbitmapstickerbitmap-bitmap) | Add bitmap sticker to the canvas editor |
113 | #### 1. addDrawableSticker(drawable: Drawable)
114 | ##### Kotlin
115 | ```kotlin
116 | val drawable = ContextCompat.getDrawable(this, R.drawable.app_icon)
117 | drawable?.let{
118 | canvasEditor.addDrawableSticker(drawable)
119 | }
120 | ```
121 | ##### Java
122 | ```java
123 | Drawable drawable = ContextCompat.getDrawable(this, R.drawable.app_icon);
124 | if(drawable != null){
125 | canvasEditor.addDrawableSticker(drawable);
126 | }
127 | ```
128 | ##### Preview
129 | 
130 |
131 | #### 2. addBitmapSticker(bitmap: Bitmap)
132 |
133 | ##### Kotlin
134 | ```kotlin
135 | val bitmap = //get your bitmap
136 | bitmap?.let{
137 | canvasEditor.addBitmapSticker(bitmap)
138 | }
139 | ```
140 | ##### Java
141 | ```java
142 | Bitmap bitmap = //get your bitmap
143 | if(bitmap != null){
144 | canvasEditor.addBitmapSticker(drawable);
145 | }
146 | ```
147 | ##### Preview
148 | 
149 |
150 | ## Text Sticker
151 | | # | Method | Action |
152 | | ------------ | ------------ | ------------ |
153 | | 1 | [addTextSticker(text: String, textColor: Int, typeface: Typeface?)](#1-addtextStickertext-string-textcolor-int-typeface-typeface) | Add text sticker to the canvas editor |
154 | | 2 | [addDrawableTextSticker(drawable: Drawable, text: String, textColor: Int, typeface: Typeface?)](#2-adddrawabletextstickerdrawable-drawable-text-string-textColor-int-typeface-typeface) | Add text sticker with drawable background to the canvas editor |
155 | #### 1. addTextSticker(text: String, textColor: Int, typeface: Typeface?)
156 | ##### Kotlin
157 | ```kotlin
158 | val text = "Canvas"
159 | val textColor = ContextCompat.getColor(this, R.color.colorPrimary)
160 | canvasEditor.addTextSticker(text, textColor, null)
161 | ```
162 | ##### Java
163 | ```java
164 | String text = "Canvas";
165 | int color = ContextCompat.getColor(this, R.color.colorPrimary);
166 | canvasEditor.addTextSticker(text, color, null);
167 | ```
168 | ##### Preview
169 | 
170 |
171 | #### 2. addDrawableTextSticker(drawable: Drawable, text: String, textColor: Int, typeface: Typeface?)
172 | ##### Kotlin
173 | ```kotlin
174 | val drawable = ContextCompat.getDrawable(this, R.drawable.ic_panorama_240dp)
175 | val text = "Canvas"
176 | val textColor = ContextCompat.getColor(this, R.color.colorYellow)
177 | drawable?.let{
178 | canvasEditor.addDrawableTextSticker(it, text, textColor, null)
179 | }
180 | ```
181 | ##### Java
182 | ```java
183 | Drawable drawable = ContextCompat.getDrawable(this, R.drawable.ic_panorama_240dp);
184 | String text = "Canvas";
185 | int textColor = ContextCompat.getColor(this, R.color.colorYellow);
186 | if(drawable != null){
187 | canvasEditor.addDrawableTextSticker(drawable, text, textColor, null);
188 | }
189 | ```
190 | ##### Preview
191 | 
192 |
193 | ## Active Sticker Methods
194 | | # | Method | Action |
195 | | ------------ | ------------ | ------------ |
196 | | 1 | [removeActiveSticker()](#1-removeactivesticker) | Remove active sticker from canvas editor |
197 | | 2 | [doneActiveSticker()](#2-doneactivesticker) | Editing done of active sticker on canvas editor |
198 | | 3 | [flipActiveSticker()](#3-flipactivesticker) | Flip active sticker on canvas editor |
199 | | 4 | [zoomAndRotateActiveSticker(motionEvent: MotionEvent)](#4-zoomandrotateactivestickermotionevent-motionevent) | Zoom and rotate active sticker with motihn event on canvas editor|
200 | #### 1. removeActiveSticker()
201 | ##### Kotlin
202 | ```kotlin
203 | canvasEditor.removeActiveSticker()
204 | ```
205 | ##### Java
206 | ```java
207 | canvasEditor.removeActiveSticker();
208 | ```
209 | #### 2. doneActiveSticker()
210 | ##### Kotlin
211 | ```kotlin
212 | canvasEditor.doneActiveSticker()
213 | ```
214 | ##### Java
215 | ```java
216 | canvasEditor.doneActiveSticker();
217 | ```
218 | #### 3. flipActiveSticker()
219 | ##### Kotlin
220 | ```kotlin
221 | canvasEditor.flipActiveSticker()
222 | ```
223 | ##### Java
224 | ```java
225 | canvasEditor.flipActiveSticker();
226 | ```
227 | #### 4. zoomAndRotateActiveSticker(motionEvent: MotionEvent)
228 | ##### Kotlin
229 | ```kotlin
230 | val motionEvent = //Set your motion event
231 | canvasEditor.zoomAndRotateActiveSticker(motionEvent)
232 | ```
233 | ##### Java
234 | ```java
235 | MotionEvent motionEvent = //Set your motion event
236 | canvasEditor.zoomAndRotateActiveSticker(motionEvent);
237 | ```
238 | ## Canvas Editor Methods
239 | | # | Method | Action |
240 | | ------------ | ------------ | ------------ |
241 | | 1 | [undo()](#1-undo) | Undo from canvas editor |
242 | | 2 | [redo()](#2-redo) | Redo to canvas editor |
243 | | 3 | [removeAll()](#3-removeall) | Delete everything from canvas editor |
244 | | 4 | [downloadBitmap(): Bitmap](#4-downloadbitmap-bitmap) | Get the canvas as bitmap, you can play with the bitmap :)|
245 | #### 1. undo()
246 | ##### Kotlin
247 | ```kotlin
248 | canvasEditor.undo()
249 | ```
250 | ##### Java
251 | ```java
252 | canvasEditor.undo();
253 | ```
254 | #### 2. redo()
255 | ##### Kotlin
256 | ```kotlin
257 | canvasEditor.redo()
258 | ```
259 | ##### Java
260 | ```java
261 | canvasEditor.redo();
262 | ```
263 | #### 3. removeAll()
264 | ##### Kotlin
265 | ```kotlin
266 | canvasEditor.removeAll()
267 | ```
268 | ##### Java
269 | ```java
270 | canvasEditor.removeAll();
271 | ```
272 | #### 4. downloadBitmap(): Bitmap
273 | ##### Kotlin
274 | ```kotlin
275 | val bitmap = canvasEditor.downloadBitmap()
276 | ```
277 | ##### Java
278 | ```java
279 | Bitmap bitmap = canvasEditor.downloadBitmap();
280 | ```
281 |
282 | ## Canvas Editor Callback
283 | ##### Kotlin
284 | ```kotlin
285 | import com.outsbook.libs.canvaseditor.CanvasEditorView
286 | import com.outsbook.libs.canvaseditor.listeners.CanvasEditorListener
287 |
288 | class MainActivity : AppCompatActivity() {
289 | private lateinit var canvasEditor: CanvasEditorView
290 |
291 | override fun onCreate(savedInstanceState: Bundle?) {
292 | super.onCreate(savedInstanceState)
293 | setContentView(R.layout.activity_main)
294 | canvasEditor = findViewById(R.id.canvasEditor)
295 |
296 | canvasEditor.setListener(object: CanvasEditorListener {
297 | override fun onEnableUndo(isEnable: Boolean) {
298 | // isEnable = true (undo list is not empty)
299 | // isEnable = false (undo list is empty)
300 | buttonUndo.imageAlpha = if(isEnable) 255 else 50
301 | }
302 |
303 | override fun onEnableRedo(isEnable: Boolean) {
304 | // isEnable = true (redo list is not empty)
305 | // isEnable = false (redo list is empty)
306 | buttonRedo.imageAlpha = if(isEnable) 255 else 50
307 | }
308 |
309 | override fun onTouchEvent(event: MotionEvent) {
310 | //When the canvas touch
311 | }
312 |
313 | override fun onStickerActive() {
314 | //When a sticker change to active mode
315 | }
316 |
317 | override fun onStickerRemove() {
318 | //When a sticker remove from canvas
319 | }
320 |
321 | override fun onStickerDone() {
322 | //When the active sticker added to canvas
323 | }
324 |
325 | override fun onStickerZoomAndRotate() {
326 | //When the active sticker zoom or rotate
327 | }
328 |
329 | override fun onStickerFlip() {
330 | //When the active sticker flip
331 | }
332 | })
333 | }
334 | }
335 | ```
336 | ##### Java
337 | ```java
338 | import com.outsbook.libs.canvaseditor.CanvasEditorView;
339 | import com.outsbook.libs.canvaseditor.listeners.CanvasEditorListener;
340 |
341 | public class MainActivity extends AppCompatActivity {
342 | private CanvasEditorView canvasEditor;
343 |
344 | @Override
345 | protected void onCreate(Bundle savedInstanceState) {
346 | super.onCreate(savedInstanceState);
347 | setContentView(R.layout.activity_main);
348 | canvasEditor = findViewById(R.id.canvasEditor);
349 |
350 | canvasEditor.setListener(new CanvasEditorListener() {
351 | @Override
352 | public void onEnableUndo(boolean isEnable) {
353 | // isEnable = true (undo list is not empty)
354 | // isEnable = false (undo list is empty)
355 | }
356 |
357 | @Override
358 | public void onEnableRedo(boolean isEnable) {
359 | // isEnable = true (redo list is not empty)
360 | // isEnable = false (redo list is empty)
361 | }
362 |
363 | @Override
364 | public void onTouchEvent(MotionEvent motionEvent) {
365 | //When the canvas touch
366 | }
367 |
368 | @Override
369 | public void onStickerActive() {
370 | //When a sticker change to active mode
371 | }
372 |
373 | @Override
374 | public void onStickerRemove() {
375 | //When a sticker remove from canvas
376 | }
377 |
378 | @Override
379 | public void onStickerDone() {
380 | //When the active sticker added to canvas
381 | }
382 |
383 | @Override
384 | public void onStickerZoomAndRotate() {
385 | //When the active sticker zoom or rotate
386 | }
387 |
388 | @Override
389 | public void onStickerFlip() {
390 | //When the active sticker flip
391 | }
392 | });
393 | }
394 | }
395 | ```
396 |
397 | ## How to contribute?
398 | - Create an issue first to discuss about the changes you are suggesting.
399 | - Fork the project.
400 | - Create a branch with name CE-[Issue Number] Example: CE-123
401 | - Make required changes and commit to that branch.
402 | - Generate pull request. Mention all the required description regarding changes you made.
403 |
404 | ## Maintained by
405 | Shahin ShamS (GitHub: [outsbook](https://github.com/outsbook/))
406 |
407 | ## Licenses
408 | Copyright(c) 2020, Shahin ShamS
409 |
410 | Licensed under the Apache License, Version 2.0 (the "License");
411 | you may not use this file except in compliance with the License.
412 | You may obtain a copy of the License at
413 |
414 | http://www.apache.org/licenses/LICENSE-2.0
415 |
416 | Unless required by applicable law or agreed to in writing, software
417 | distributed under the License is distributed on an "AS IS" BASIS,
418 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
419 | See the License for the specific language governing permissions and
420 | limitations under the License.
421 |
--------------------------------------------------------------------------------
/canvaseditor/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/canvaseditor/bintray.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.jfrog.bintray'
2 |
3 | version = libraryVersion
4 |
5 | if (project.hasProperty("android")) { // Android libraries
6 | task sourcesJar(type: Jar) {
7 | classifier = 'sources'
8 | from android.sourceSets.main.java.srcDirs
9 | }
10 |
11 | task javadoc(type: Javadoc) {
12 | source = android.sourceSets.main.java.srcDirs
13 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
14 | }
15 | } else { // Java libraries
16 | task sourcesJar(type: Jar, dependsOn: classes) {
17 | classifier = 'sources'
18 | from sourceSets.main.allSource
19 | }
20 | }
21 |
22 | task javadocJar(type: Jar, dependsOn: javadoc) {
23 | classifier = 'javadoc'
24 | from javadoc.destinationDir
25 | }
26 |
27 | artifacts {
28 | archives javadocJar
29 | archives sourcesJar
30 | }
31 |
32 | // Bintray
33 | Properties properties = new Properties()
34 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
35 |
36 | bintray {
37 | user = properties.getProperty("bintray.user")
38 | key = properties.getProperty("bintray.apikey")
39 |
40 | configurations = ['archives']
41 | pkg {
42 | repo = bintrayRepo
43 | name = bintrayName
44 | desc = libraryDescription
45 | websiteUrl = siteUrl
46 | vcsUrl = gitUrl
47 | licenses = allLicenses
48 | publish = true
49 | publicDownloadNumbers = true
50 | version {
51 | desc = libraryDescription
52 | gpg {
53 | sign = true //Determines whether to GPG sign the files. The default is false
54 | passphrase = properties.getProperty("bintray.gpg.password")
55 | //Optional. The passphrase for GPG signing'
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/canvaseditor/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 |
9 | defaultConfig {
10 | minSdkVersion 16
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles 'consumer-rules.pro'
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | tasks.withType(Javadoc).all { enabled = false }
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(dir: 'libs', include: ['*.jar'])
31 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
32 | implementation 'androidx.appcompat:appcompat:1.1.0'
33 | implementation 'androidx.core:core-ktx:1.2.0'
34 | testImplementation 'junit:junit:4.13'
35 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
36 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
37 | }
38 |
39 | ext {
40 | bintrayRepo = 'CanvasEditor' // Repo name in bintray dashboard
41 | bintrayName = 'com.outsbook.libs.canvaseditor' // package name of the bintray repo
42 |
43 | publishedGroupId = 'com.outsbook.libs' // this is the ID we want to see in implementation line
44 | libraryName = 'canvaseditor' // this is the module name of library
45 | artifact = 'canvaseditor' // this is the artifact we want to see in implementation line
46 |
47 | libraryDescription = 'A Canvas/Image Editor library with easy support for canvas/image editing using paints, drawable sticker, and text sticker in Android (Kotlin).' // description of library
48 |
49 | siteUrl = 'https://github.com/outsbook/CanvasEditor' // git repo url
50 | gitUrl = 'https://github.com/outsbook/CanvasEditor.git' // git repo vcs url
51 |
52 | libraryVersion = '1.0.0' // library version
53 |
54 | developerId = 'outsbook' // This is your bintray username
55 | developerName = 'Shahin ShamS' // Developer's name
56 | developerEmail = 'shamscse@gmail.com' // Developer's email
57 |
58 | licenseName = 'The Apache Software License, Version 2.0' // for example, The Apache Software License, Version 2.0
59 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' // for example, http://www.apache.org/licenses/LICENSE-2.0.txt
60 | allLicenses = ["Apache-2.0"] // array of licenses, for example, ["Apache-2.0"]
61 | }
62 |
63 | apply from: 'install.gradle'
64 | apply from: 'bintray.gradle'
65 |
66 | //commands
67 | // ./gradlew install
68 | // ./gradlew bintrayUpload
69 |
--------------------------------------------------------------------------------
/canvaseditor/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/canvaseditor/consumer-rules.pro
--------------------------------------------------------------------------------
/canvaseditor/install.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 |
3 | group = publishedGroupId // Maven Group ID for the artifact
4 |
5 | install {
6 | repositories.mavenInstaller {
7 | // This generates POM.xml with proper parameters
8 | pom {
9 | project {
10 | packaging 'aar'
11 | groupId publishedGroupId
12 | artifactId artifact
13 |
14 | // Add your description here
15 | name libraryName
16 | description libraryDescription
17 | url siteUrl
18 |
19 | // Set your license
20 | licenses {
21 | license {
22 | name licenseName
23 | url licenseUrl
24 | }
25 | }
26 | developers {
27 | developer {
28 | id developerId
29 | name developerName
30 | email developerEmail
31 | }
32 | }
33 | scm {
34 | connection gitUrl
35 | developerConnection gitUrl
36 | url siteUrl
37 |
38 | }
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/canvaseditor/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/canvaseditor/src/androidTest/java/com/outsbook/libs/canvaseditor/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.outsbook.libs.canvaseditor.test", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/canvaseditor/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/CanvasEditorView.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor
2 |
3 | import android.annotation.TargetApi
4 | import android.content.Context
5 | import android.graphics.Bitmap
6 | import android.graphics.Typeface
7 | import android.graphics.drawable.Drawable
8 | import android.os.Build
9 | import android.util.AttributeSet
10 | import android.util.Log
11 | import android.view.GestureDetector
12 | import android.view.MotionEvent
13 | import android.view.View
14 | import android.view.ViewGroup
15 | import android.widget.RelativeLayout
16 | import androidx.core.content.ContextCompat
17 | import com.outsbook.libs.canvaseditor.enums.DrawType
18 | import com.outsbook.libs.canvaseditor.listeners.PaintViewListener
19 | import com.outsbook.libs.canvaseditor.listeners.CanvasEditorListener
20 | import com.outsbook.libs.canvaseditor.listeners.StickerViewListener
21 | import com.outsbook.libs.canvaseditor.models.DrawObject
22 | import com.outsbook.libs.canvaseditor.paints.PaintView
23 | import com.outsbook.libs.canvaseditor.stickers.BitmapSticker
24 | import com.outsbook.libs.canvaseditor.stickers.DrawableSticker
25 | import com.outsbook.libs.canvaseditor.stickers.StickerView
26 | import com.outsbook.libs.canvaseditor.stickers.TextSticker
27 |
28 | class CanvasEditorView : RelativeLayout{
29 | @JvmOverloads
30 | constructor(
31 | context: Context,
32 | attrs: AttributeSet? = null,
33 | defStyleAttr: Int = 0
34 | ) : super(context, attrs, defStyleAttr)
35 |
36 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
37 | constructor(
38 | context: Context,
39 | attrs: AttributeSet?,
40 | defStyleAttr: Int,
41 | defStyleRes: Int
42 | ) : super(context, attrs, defStyleAttr, defStyleRes)
43 |
44 | private val mUndoList = mutableListOf()
45 | private val mRedoList = mutableListOf()
46 |
47 | private val paintViewListener = object : PaintViewListener {
48 | override fun onTouchUp(obj: DrawObject) {
49 | mUndoList.add(obj)
50 | mRedoList.clear()
51 | mListener?.onEnableUndo(true)
52 | mListener?.onEnableRedo(false)
53 | }
54 |
55 | override fun onClick(x: Float, y: Float) {
56 | val pos = findTapedSticker(x, y)
57 | if(pos > -1)
58 | enableEditModeSticker(pos)
59 | }
60 |
61 | override fun onTouchEvent(event: MotionEvent) {
62 | mListener?.onTouchEvent(event)
63 | }
64 | }
65 |
66 | private val stickerViewListener = object : StickerViewListener {
67 | override fun onRemove() {
68 | mListener?.onStickerRemove()
69 | mListener?.onEnableUndo(mUndoList.isNotEmpty())
70 | }
71 | override fun onDone(obj: DrawObject) {
72 | addStickerToPaint(obj)
73 | mListener?.onStickerDone()
74 | }
75 | override fun onZoomAndRotate() {
76 | mListener?.onStickerZoomAndRotate()
77 | }
78 | override fun onFlip() {
79 | mListener?.onStickerFlip()
80 | }
81 |
82 | override fun onClickStickerOutside(x: Float, y: Float) {
83 | val pos = findTapedSticker(x, y)
84 | if(pos > -1){
85 | enableEditModeSticker(pos)
86 | }
87 | }
88 |
89 | override fun onTouchEvent(event: MotionEvent) {
90 | mListener?.onTouchEvent(event)
91 | }
92 | }
93 |
94 | private val mPaintView: PaintView = PaintView(context, paintViewListener)
95 | private val mStickerView: StickerView = StickerView(context, stickerViewListener)
96 | private var mListener: CanvasEditorListener? = null
97 |
98 | init {
99 | val params = LayoutParams(
100 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
101 | )
102 | mPaintView.layoutParams = params
103 | mPaintView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.white))
104 | addView(mPaintView)
105 |
106 | mStickerView.layoutParams = params
107 | mStickerView.setBackgroundColor(
108 | ContextCompat.getColor(
109 | context,
110 | android.R.color.transparent
111 | )
112 | )
113 | addView(mStickerView)
114 | mStickerView.visibility = View.GONE
115 | }
116 |
117 | fun setListener(listener: CanvasEditorListener) {
118 | mListener = listener
119 | }
120 |
121 | fun setPaintColor(color: Int) {
122 | doneStickerEdit()
123 | mPaintView.paint.color = color
124 | }
125 |
126 | fun setStrokeWidth(strokeWidth: Float) {
127 | doneStickerEdit()
128 | mPaintView.paint.strokeWidth = strokeWidth
129 | }
130 |
131 | fun setStrokeCap(strokeCap: Paint.Cap) {
132 | doneStickerEdit()
133 | mPaintView.paint.strokeCap = strokeCap
134 | }
135 |
136 | //region add sticker
137 | fun addDrawableSticker(drawable: Drawable) {
138 | doneStickerEdit()
139 | mStickerView.visibility = View.VISIBLE
140 | val sticker = DrawableSticker(drawable)
141 | mStickerView.addSticker(sticker)
142 | mListener?.onEnableUndo(true)
143 | mListener?.onEnableRedo(false)
144 | mListener?.onStickerActive()
145 | }
146 |
147 | fun addBitmapSticker(bitmap: Bitmap) {
148 | doneStickerEdit()
149 | mStickerView.visibility = View.VISIBLE
150 | val sticker = BitmapSticker(context, bitmap)
151 | mStickerView.addSticker(sticker)
152 | mListener?.onEnableUndo(true)
153 | mListener?.onEnableRedo(false)
154 | mListener?.onStickerActive()
155 | }
156 |
157 | fun addTextSticker(text: String, textColor: Int, typeface: Typeface?) {
158 | doneStickerEdit()
159 | mStickerView.visibility = View.VISIBLE
160 | val sticker = TextSticker(context, null)
161 | sticker.setText(text)
162 | sticker.setTextColor(textColor)
163 | typeface?.let {
164 | sticker.setTypeface(it)
165 | }
166 | sticker.setAlpha(255)
167 | sticker.resizeText()
168 | mStickerView.addSticker(sticker)
169 | mListener?.onEnableUndo(true)
170 | mListener?.onEnableRedo(false)
171 | mListener?.onStickerActive()
172 | }
173 |
174 | fun addDrawableTextSticker(
175 | drawable: Drawable,
176 | text: String,
177 | textColor: Int,
178 | typeface: Typeface?
179 | ) {
180 | doneStickerEdit()
181 | mStickerView.visibility = View.VISIBLE
182 | val sticker = TextSticker(context, drawable)
183 | sticker.setText(text)
184 | sticker.setTextColor(textColor)
185 | typeface?.let {
186 | sticker.setTypeface(it)
187 | }
188 | sticker.resizeText()
189 | mStickerView.addSticker(sticker)
190 | mListener?.onEnableUndo(true)
191 | mListener?.onEnableRedo(false)
192 | mListener?.onStickerActive()
193 | }
194 |
195 | fun doneActiveSticker(){
196 | if (mStickerView.visibility == View.VISIBLE) {
197 | mStickerView.done()
198 | }
199 | }
200 |
201 | fun removeActiveSticker(){
202 | if (mStickerView.visibility == View.VISIBLE) {
203 | mStickerView.remove()
204 | }
205 | }
206 |
207 | fun zoomAndRotateActiveSticker(motionEvent: MotionEvent){
208 | if (mStickerView.visibility == View.VISIBLE) {
209 | mStickerView.zoomAndRotate(motionEvent)
210 | }
211 | }
212 |
213 | fun flipActiveSticker(){
214 | if (mStickerView.visibility == View.VISIBLE) {
215 | mStickerView.flip()
216 | }
217 | }
218 | //endregion
219 |
220 | fun undo() {
221 | if (mStickerView.visibility == View.VISIBLE) {
222 | mStickerView.remove()
223 | return
224 | }
225 | if (mUndoList.isNotEmpty()) {
226 | mRedoList.add(mUndoList.last())
227 | mUndoList.removeAt(mUndoList.lastIndex)
228 | mPaintView.initCanvas()
229 | mUndoList.forEach {
230 | drawObject(it)
231 | }
232 | mListener?.onEnableUndo(mUndoList.isNotEmpty())
233 | mListener?.onEnableRedo(mRedoList.isNotEmpty())
234 | }
235 | }
236 |
237 | fun redo() {
238 | if (mRedoList.isNotEmpty()) {
239 | val obj = mRedoList.last()
240 | mUndoList.add(obj)
241 | mRedoList.removeAt(mRedoList.lastIndex)
242 | drawObject(obj)
243 | mListener?.onEnableUndo(mUndoList.isNotEmpty())
244 | mListener?.onEnableRedo(mRedoList.isNotEmpty())
245 | }
246 | }
247 |
248 | fun removeAll(){
249 | mUndoList.clear()
250 | mRedoList.clear()
251 | mStickerView.remove()
252 | mPaintView.initCanvas()
253 | mListener?.onEnableUndo(false)
254 | mListener?.onEnableRedo(false)
255 | }
256 |
257 | fun downloadBitmap(): Bitmap{
258 | doneStickerEdit()
259 | return mPaintView.extraBitmap
260 | }
261 |
262 | private fun drawObject(obj: DrawObject) {
263 | when (obj.drawType) {
264 | DrawType.PATH -> {
265 | mPaintView.drawPath(obj.pathAndPaint!!)
266 | }
267 | DrawType.STICKER -> {
268 | mPaintView.drawSticker(obj.sticker!!)
269 | }
270 | }
271 | }
272 |
273 | //region find double tap inside sticker
274 | private fun findTapedSticker(x: Float, y: Float): Int {
275 | for (i in mUndoList.size - 1 downTo 0) {
276 | val obj = mUndoList[i]
277 | if (obj.drawType == DrawType.STICKER) {
278 | val sticker = obj.sticker!!
279 | if (sticker.contains(x, y)) {
280 | return i
281 | }
282 | }
283 | }
284 | return -1
285 | }
286 |
287 | private fun enableEditModeSticker(pos: Int) {
288 | val obj = mUndoList[pos]
289 | val sticker = obj.sticker!!
290 | mStickerView.visibility = View.VISIBLE
291 | mStickerView.currentSticker = sticker
292 | mUndoList.removeAt(pos)
293 | mPaintView.initCanvas()
294 | mUndoList.forEach {
295 | drawObject(it)
296 | }
297 | mRedoList.clear()
298 | mListener?.onEnableUndo(true)
299 | mListener?.onEnableRedo(mRedoList.isNotEmpty())
300 | mListener?.onStickerActive()
301 | }
302 |
303 | private fun addStickerToPaint(obj: DrawObject) {
304 | mPaintView.drawSticker(obj.sticker!!)
305 | mUndoList.add(obj)
306 | mRedoList.clear()
307 | mListener?.onEnableUndo(true)
308 | mListener?.onEnableRedo(false)
309 | }
310 |
311 | private fun doneStickerEdit() {
312 | if (mStickerView.visibility == View.VISIBLE) {
313 | mStickerView.done()
314 | }
315 | }
316 | }
317 | //endregion
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/constants/ActionMode.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.constants
2 |
3 | internal class ActionMode {
4 | companion object {
5 | var NONE = 0
6 | var DRAG = 1
7 | var ZOOM_WITH_TWO_FINGER = 2
8 | var ICON = 3
9 | var CLICK = 4
10 | }
11 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/constants/ConstantSticker.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.constants
2 |
3 | internal class ConstantSticker {
4 | companion object{
5 | var CENTER = 1
6 | var TOP = 1 shl 1
7 | var LEFT = 1 shl 2
8 | var RIGHT = 1 shl 3
9 | var BOTTOM = 1 shl 4
10 | }
11 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/constants/ConstantStickerIcon.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.constants
2 |
3 | import android.content.res.Resources
4 |
5 | internal class ConstantStickerIcon {
6 | companion object{
7 | val DEFAULT_ICON_RADIUS = 14f * Resources.getSystem().displayMetrics.density
8 |
9 | const val LEFT_TOP = 0
10 | const val RIGHT_TOP = 1
11 | const val LEFT_BOTTOM = 2
12 | const val RIGHT_BOTTOM = 3
13 | }
14 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/enums/DrawType.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.enums
2 |
3 | internal enum class DrawType {
4 | PATH,
5 | STICKER
6 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/events/DeleteIconEvent.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.events
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.listeners.StickerIconListener
5 | import com.outsbook.libs.canvaseditor.stickers.StickerView
6 |
7 | internal class DeleteIconEvent: StickerIconListener {
8 | override fun onActionDown(stickerView: StickerView?, event: MotionEvent?) {}
9 | override fun onActionMove(stickerView: StickerView, event: MotionEvent) {}
10 | override fun onActionUp(stickerView: StickerView, event: MotionEvent?) {
11 | stickerView.remove()
12 | }
13 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/events/DoneIconEvent.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.events
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.listeners.StickerIconListener
5 | import com.outsbook.libs.canvaseditor.stickers.StickerView
6 |
7 | internal class DoneIconEvent: StickerIconListener {
8 | override fun onActionDown(stickerView: StickerView?, event: MotionEvent?) {}
9 | override fun onActionMove(stickerView: StickerView, event: MotionEvent) {}
10 | override fun onActionUp(stickerView: StickerView, event: MotionEvent?) {
11 | stickerView.done()
12 | }
13 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/events/FlipIconEvent.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.events
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.listeners.StickerIconListener
5 | import com.outsbook.libs.canvaseditor.stickers.StickerView
6 |
7 | internal class FlipIconEvent: StickerIconListener {
8 | override fun onActionDown(stickerView: StickerView?, event: MotionEvent?) {}
9 | override fun onActionMove(stickerView: StickerView, event: MotionEvent) {}
10 | override fun onActionUp(stickerView: StickerView, event: MotionEvent?) {
11 | stickerView.flip()
12 | }
13 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/events/ZoomIconEvent.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.events
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.listeners.StickerIconListener
5 | import com.outsbook.libs.canvaseditor.stickers.StickerView
6 |
7 | internal class ZoomIconEvent: StickerIconListener {
8 | override fun onActionDown(stickerView: StickerView?, event: MotionEvent?) {}
9 | override fun onActionMove(stickerView: StickerView, event: MotionEvent) {
10 | stickerView.zoomAndRotate(event)
11 | }
12 | override fun onActionUp(stickerView: StickerView, event: MotionEvent?) {}
13 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/listeners/CanvasEditorListener.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.listeners
2 |
3 | import android.view.MotionEvent
4 |
5 | interface CanvasEditorListener {
6 | fun onEnableUndo(isEnable: Boolean)
7 | fun onEnableRedo(isEnable: Boolean)
8 | fun onTouchEvent(event: MotionEvent) {}
9 |
10 | fun onStickerActive() {}
11 | fun onStickerRemove() {}
12 | fun onStickerDone() {}
13 | fun onStickerZoomAndRotate() {}
14 | fun onStickerFlip() {}
15 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/listeners/PaintViewListener.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.listeners
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.models.DrawObject
5 |
6 | internal interface PaintViewListener {
7 | fun onTouchUp(obj: DrawObject)
8 | fun onClick(x: Float, y: Float)
9 | fun onTouchEvent(event: MotionEvent)
10 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/listeners/StickerIconListener.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.listeners
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.stickers.StickerView
5 |
6 | internal interface StickerIconListener {
7 | fun onActionDown(stickerView: StickerView?, event: MotionEvent?)
8 | fun onActionMove(stickerView: StickerView, event: MotionEvent)
9 | fun onActionUp(stickerView: StickerView, event: MotionEvent?)
10 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/listeners/StickerViewListener.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.listeners
2 |
3 | import android.view.MotionEvent
4 | import com.outsbook.libs.canvaseditor.models.DrawObject
5 |
6 | internal interface StickerViewListener {
7 | fun onRemove()
8 | fun onDone(obj: DrawObject)
9 | fun onZoomAndRotate()
10 | fun onFlip()
11 | fun onClickStickerOutside(x: Float, y: Float)
12 | fun onTouchEvent(event: MotionEvent)
13 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/models/DrawObject.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.models
2 |
3 | import com.outsbook.libs.canvaseditor.enums.DrawType
4 | import com.outsbook.libs.canvaseditor.models.PathAndPaint
5 | import com.outsbook.libs.canvaseditor.stickers.Sticker
6 |
7 | internal data class DrawObject(
8 | val pathAndPaint: PathAndPaint?,
9 | val sticker: Sticker?,
10 | val drawType: DrawType
11 | )
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/models/PathAndPaint.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.models
2 |
3 | import android.graphics.Paint
4 | import android.graphics.Path
5 |
6 | internal data class PathAndPaint(
7 | val path: Path,
8 | val paint: Paint
9 | )
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/paints/PaintView.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.paints
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.Bitmap
6 | import android.graphics.Canvas
7 | import android.graphics.Paint
8 | import android.graphics.Path
9 | import android.util.Log
10 | import android.view.GestureDetector
11 | import android.view.MotionEvent
12 | import android.view.ViewConfiguration
13 | import android.widget.FrameLayout
14 | import androidx.core.content.ContextCompat
15 | import androidx.core.content.res.ResourcesCompat
16 | import com.outsbook.libs.canvaseditor.enums.DrawType
17 | import com.outsbook.libs.canvaseditor.listeners.PaintViewListener
18 | import com.outsbook.libs.canvaseditor.models.DrawObject
19 | import com.outsbook.libs.canvaseditor.models.PathAndPaint
20 | import com.outsbook.libs.canvaseditor.stickers.Sticker
21 | import kotlin.math.abs
22 |
23 | internal class PaintView (context: Context, private val paintViewListener: PaintViewListener) :
24 | FrameLayout(context) {
25 |
26 | private val drawColor = ResourcesCompat.getColor(resources, android.R.color.black, null)
27 | private var path = Path()
28 | private var motionTouchEventX = 0f
29 | private var motionTouchEventY = 0f
30 | private var currentX = 0f
31 | private var currentY = 0f
32 | private var isDrawPath = false
33 | private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop
34 |
35 | private lateinit var extraCanvas: Canvas
36 | lateinit var extraBitmap: Bitmap
37 |
38 | val paint = Paint().apply {
39 | color = drawColor
40 | isAntiAlias = true
41 | isDither = true
42 | style = Paint.Style.STROKE
43 | strokeJoin = Paint.Join.ROUND
44 | strokeCap = Paint.Cap.ROUND
45 | strokeWidth = 10f
46 | }
47 |
48 | fun initCanvas() {
49 | extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
50 | extraCanvas = Canvas(extraBitmap)
51 | extraCanvas.drawColor(ContextCompat.getColor(context, android.R.color.white))
52 | invalidate()
53 | }
54 |
55 | override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
56 | super.onSizeChanged(width, height, oldWidth, oldHeight)
57 | initCanvas()
58 | }
59 |
60 | override fun onDraw(canvas: Canvas) {
61 | super.onDraw(canvas)
62 | canvas.drawBitmap(extraBitmap, 0f, 0f, null)
63 | }
64 |
65 | fun drawPath(pathAndPaint: PathAndPaint) {
66 | extraCanvas.drawPath(pathAndPaint.path, pathAndPaint.paint)
67 | invalidate()
68 | }
69 |
70 | fun drawSticker(sticker: Sticker) {
71 | sticker.draw(extraCanvas)
72 | invalidate()
73 | }
74 |
75 | //region touch events
76 | private val gestureDetector =
77 | GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
78 | override fun onSingleTapConfirmed(event: MotionEvent?): Boolean {
79 | event?.let {
80 | paintViewListener.onClick(it.x, it.y)
81 | }
82 | return super.onSingleTapConfirmed(event)
83 | }
84 | })
85 |
86 | @SuppressLint("ClickableViewAccessibility")
87 | override fun onTouchEvent(event: MotionEvent): Boolean {
88 | paintViewListener.onTouchEvent(event)
89 | motionTouchEventX = event.x
90 | motionTouchEventY = event.y
91 | when (event.action) {
92 | MotionEvent.ACTION_DOWN -> touchStart()
93 | MotionEvent.ACTION_MOVE -> touchMove()
94 | MotionEvent.ACTION_UP -> touchUp()
95 | }
96 | gestureDetector.onTouchEvent(event)
97 | return true
98 | }
99 |
100 | private fun touchStart() {
101 | path.reset()
102 | path.moveTo(motionTouchEventX, motionTouchEventY)
103 | currentX = motionTouchEventX
104 | currentY = motionTouchEventY
105 | }
106 |
107 | private fun touchMove() {
108 | val dx = abs(motionTouchEventX - currentX)
109 | val dy = abs(motionTouchEventY - currentY)
110 | if (dx >= touchTolerance || dy >= touchTolerance) {
111 | path.quadTo(
112 | currentX,
113 | currentY,
114 | (motionTouchEventX + currentX) / 2,
115 | (motionTouchEventY + currentY) / 2
116 | )
117 | currentX = motionTouchEventX
118 | currentY = motionTouchEventY
119 | extraCanvas.drawPath(path, paint)
120 | isDrawPath = true
121 | }
122 | invalidate()
123 | }
124 |
125 | private fun touchUp() {
126 | if (isDrawPath) {
127 | val obj = DrawObject(PathAndPaint(Path(path), Paint(paint)), null, DrawType.PATH)
128 | paintViewListener.onTouchUp(obj)
129 | }
130 | invalidate()
131 | path.reset()
132 | isDrawPath = false
133 | }
134 | //endregion
135 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/stickers/BitmapSticker.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.stickers
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.graphics.Canvas
6 | import android.graphics.Rect
7 | import android.graphics.drawable.BitmapDrawable
8 | import android.graphics.drawable.Drawable
9 |
10 | internal class BitmapSticker(context: Context, bitmap: Bitmap): Sticker() {
11 | private val realBounds: Rect
12 |
13 | override var drawable: Drawable = BitmapDrawable(context.resources, bitmap)
14 |
15 | final override val width: Int
16 | get() = drawable.intrinsicWidth
17 |
18 | final override val height: Int
19 | get() = drawable.intrinsicHeight
20 |
21 | init {
22 | realBounds = Rect(0, 0, width, height)
23 | }
24 |
25 | override fun setDrawable(drawable: Drawable): BitmapSticker {
26 | this.drawable = drawable
27 | return this
28 | }
29 |
30 | override fun draw(canvas: Canvas) {
31 | canvas.save()
32 | canvas.concat(matrix)
33 | drawable.bounds = realBounds
34 | drawable.draw(canvas)
35 | canvas.restore()
36 | }
37 |
38 | override fun setAlpha(alpha: Int): BitmapSticker {
39 | drawable.alpha = alpha
40 | return this
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/stickers/DrawableSticker.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.stickers
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Rect
5 | import android.graphics.drawable.Drawable
6 |
7 | internal open class DrawableSticker(override var drawable: Drawable): Sticker() {
8 | private val realBounds: Rect
9 |
10 | final override val width: Int
11 | get() = drawable.intrinsicWidth
12 |
13 | final override val height: Int
14 | get() = drawable.intrinsicHeight
15 |
16 | init {
17 | realBounds = Rect(0, 0, width, height)
18 | }
19 |
20 | override fun setDrawable(drawable: Drawable): DrawableSticker {
21 | this.drawable = drawable
22 | return this
23 | }
24 |
25 | override fun draw(canvas: Canvas) {
26 | canvas.save()
27 | canvas.concat(matrix)
28 | drawable.bounds = realBounds
29 | drawable.draw(canvas)
30 | canvas.restore()
31 | }
32 |
33 | override fun setAlpha(alpha: Int): DrawableSticker {
34 | drawable.alpha = alpha
35 | return this
36 | }
37 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/stickers/Sticker.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.stickers
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Matrix
5 | import android.graphics.PointF
6 | import android.graphics.RectF
7 | import android.graphics.drawable.Drawable
8 | import kotlin.math.atan2
9 | import kotlin.math.pow
10 | import kotlin.math.roundToInt
11 | import kotlin.math.sqrt
12 |
13 | internal abstract class Sticker {
14 | val matrix = Matrix()
15 | var isFlippedHorizontally = false
16 | var isFlippedVertically = false
17 |
18 | private val matrixValues = FloatArray(9)
19 | private val unrotatedWrapperCorner = FloatArray(8)
20 | private val unrotatedPoint = FloatArray(2)
21 | private val boundPoints = FloatArray(8)
22 | private val mappedBounds = FloatArray(8)
23 | private val trappedRect = RectF()
24 |
25 | abstract val width: Int
26 | abstract val height: Int
27 | abstract val drawable: Drawable
28 |
29 | val mappedBoundPoints: FloatArray
30 | get() {
31 | val dst = FloatArray(8)
32 | getMappedPoints(dst, getBoundPoints())
33 | return dst
34 | }
35 | private val bound: RectF
36 | get() {
37 | val bound = RectF()
38 | getBound(bound)
39 | return bound
40 | }
41 | val mappedBound: RectF
42 | get() {
43 | val dst = RectF()
44 | getMappedBound(dst, bound)
45 | return dst
46 | }
47 | private val centerPoint: PointF
48 | get() {
49 | val center = PointF()
50 | getCenterPoint(center)
51 | return center
52 | }
53 | val mappedCenterPoint: PointF
54 | get() {
55 | val pointF = centerPoint
56 | getMappedCenterPoint(pointF, FloatArray(2), FloatArray(2))
57 | return pointF
58 | }
59 | val currentScale: Float
60 | get() = getMatrixScale(matrix)
61 | val currentHeight: Float
62 | get() = getMatrixScale(matrix) * height
63 | val currentWidth: Float
64 | get() = getMatrixScale(matrix) * width
65 | private val currentAngle: Float
66 | get() = getMatrixAngle(matrix)
67 |
68 | companion object {
69 | fun getMatrix(sticker: Sticker): Matrix {
70 | return sticker.matrix
71 | }
72 | }
73 |
74 | abstract fun draw(canvas: Canvas)
75 | abstract fun setDrawable(drawable: Drawable): Sticker
76 | abstract fun setAlpha(alpha: Int): Sticker
77 |
78 | fun setMatrix(matrix: Matrix?): Sticker {
79 | this.matrix.set(matrix)
80 | return this
81 | }
82 |
83 | fun setFlippedHorizontally(flippedHorizontally: Boolean): Sticker {
84 | isFlippedHorizontally = flippedHorizontally
85 | return this
86 | }
87 |
88 | fun setFlippedVertically(flippedVertically: Boolean): Sticker {
89 | isFlippedVertically = flippedVertically
90 | return this
91 | }
92 |
93 | private fun getBoundPoints(): FloatArray {
94 | val points = FloatArray(8)
95 | getBoundPoints(points)
96 | return points
97 | }
98 |
99 | fun getBoundPoints(points: FloatArray) {
100 | if (!isFlippedHorizontally) {
101 | if (!isFlippedVertically) {
102 | points[0] = 0f
103 | points[1] = 0f
104 | points[2] = width.toFloat()
105 | points[3] = 0f
106 | points[4] = 0f
107 | points[5] = height.toFloat()
108 | points[6] = width.toFloat()
109 | points[7] = height.toFloat()
110 | } else {
111 | points[0] = 0f
112 | points[1] = height.toFloat()
113 | points[2] = width.toFloat()
114 | points[3] = height.toFloat()
115 | points[4] = 0f
116 | points[5] = 0f
117 | points[6] = width.toFloat()
118 | points[7] = 0f
119 | }
120 | } else {
121 | if (!isFlippedVertically) {
122 | points[0] = width.toFloat()
123 | points[1] = 0f
124 | points[2] = 0f
125 | points[3] = 0f
126 | points[4] = width.toFloat()
127 | points[5] = height.toFloat()
128 | points[6] = 0f
129 | points[7] = height.toFloat()
130 | } else {
131 | points[0] = width.toFloat()
132 | points[1] = height.toFloat()
133 | points[2] = 0f
134 | points[3] = height.toFloat()
135 | points[4] = width.toFloat()
136 | points[5] = 0f
137 | points[6] = 0f
138 | points[7] = 0f
139 | }
140 | }
141 | }
142 |
143 | fun getMappedPoints(src: FloatArray): FloatArray {
144 | val dst = FloatArray(src.size)
145 | matrix.mapPoints(dst, src)
146 | return dst
147 | }
148 |
149 | fun getMappedPoints(dst: FloatArray, src: FloatArray) {
150 | matrix.mapPoints(dst, src)
151 | }
152 |
153 | private fun getBound(dst: RectF) {
154 | dst[0f, 0f, width.toFloat()] = height.toFloat()
155 | }
156 |
157 | private fun getMappedBound(dst: RectF, bound: RectF) {
158 | matrix.mapRect(dst, bound)
159 | }
160 |
161 | fun getCenterPoint(dst: PointF) {
162 | dst[width * 1f / 2] = height * 1f / 2
163 | }
164 |
165 | fun getMappedCenterPoint(dst: PointF, mappedPoints: FloatArray,
166 | src: FloatArray) {
167 | getCenterPoint(dst)
168 | src[0] = dst.x
169 | src[1] = dst.y
170 | getMappedPoints(mappedPoints, src)
171 | dst[mappedPoints[0]] = mappedPoints[1]
172 | }
173 |
174 | private fun getMatrixScale(matrix: Matrix): Float {
175 | return sqrt(
176 | getMatrixValue(matrix, Matrix.MSCALE_X).toDouble().pow(2.0) + getMatrixValue(
177 | matrix,
178 | Matrix.MSKEW_Y
179 | ).toDouble().pow(2.0)
180 | ).toFloat()
181 | }
182 |
183 | private fun getMatrixAngle(matrix: Matrix): Float {
184 | return Math.toDegrees(-atan2(getMatrixValue(matrix, Matrix.MSKEW_X).toDouble(),
185 | getMatrixValue(matrix, Matrix.MSCALE_X).toDouble())
186 | ).toFloat()
187 | }
188 |
189 | private fun getMatrixValue(matrix: Matrix, valueIndex: Int): Float {
190 | matrix.getValues(matrixValues)
191 | return matrixValues[valueIndex]
192 | }
193 |
194 | fun contains(x: Float, y: Float): Boolean {
195 | return contains(floatArrayOf(x, y))
196 | }
197 |
198 | fun contains(point: FloatArray): Boolean {
199 | val tempMatrix = Matrix()
200 | tempMatrix.setRotate(-currentAngle)
201 | getBoundPoints(boundPoints)
202 | getMappedPoints(mappedBounds, boundPoints)
203 | tempMatrix.mapPoints(unrotatedWrapperCorner, mappedBounds)
204 | tempMatrix.mapPoints(unrotatedPoint, point)
205 | trapToRect(trappedRect, unrotatedWrapperCorner)
206 | return trappedRect.contains(unrotatedPoint[0], unrotatedPoint[1])
207 | }
208 |
209 | private fun trapToRect(r: RectF, array: FloatArray) {
210 | r[Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY] = Float.NEGATIVE_INFINITY
211 | var i = 1
212 | while (i < array.size) {
213 | val x = (array[i - 1] * 10).roundToInt() / 10f
214 | val y = (array[i] * 10).roundToInt() / 10f
215 | r.left = if (x < r.left) x else r.left
216 | r.top = if (y < r.top) y else r.top
217 | r.right = if (x > r.right) x else r.right
218 | r.bottom = if (y > r.bottom) y else r.bottom
219 | i += 2
220 | }
221 | r.sort()
222 | }
223 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/stickers/StickerIcon.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.stickers
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Paint
5 | import android.graphics.drawable.Drawable
6 | import android.view.MotionEvent
7 | import com.outsbook.libs.canvaseditor.constants.ConstantStickerIcon
8 | import com.outsbook.libs.canvaseditor.listeners.StickerIconListener
9 |
10 | internal class StickerIcon(drawable: Drawable?, gravity: Int): DrawableSticker(drawable!!), StickerIconListener {
11 | var iconRadius = ConstantStickerIcon.DEFAULT_ICON_RADIUS
12 | var x = 0f
13 | var y = 0f
14 |
15 | var position = ConstantStickerIcon.LEFT_TOP
16 | var iconListener: StickerIconListener? = null
17 |
18 | init {
19 | position = gravity
20 | }
21 |
22 | fun draw(canvas: Canvas, paint: Paint?) {
23 | canvas.drawCircle(x, y, iconRadius, paint!!)
24 | super.draw(canvas)
25 | }
26 |
27 | override fun onActionDown(stickerView: StickerView?, event: MotionEvent?) {
28 | iconListener?.onActionDown(stickerView, event)
29 | }
30 |
31 | override fun onActionMove(stickerView: StickerView, event: MotionEvent) {
32 | iconListener?.onActionMove(stickerView, event)
33 | }
34 |
35 | override fun onActionUp(stickerView: StickerView, event: MotionEvent?) {
36 | iconListener?.onActionUp(stickerView, event)
37 | }
38 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/stickers/StickerView.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.stickers
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.graphics.*
6 | import android.view.MotionEvent
7 | import android.view.View
8 | import android.view.ViewConfiguration
9 | import android.widget.FrameLayout
10 | import androidx.core.content.ContextCompat
11 | import androidx.core.view.ViewCompat
12 | import com.outsbook.libs.canvaseditor.R
13 | import com.outsbook.libs.canvaseditor.constants.ActionMode
14 | import com.outsbook.libs.canvaseditor.constants.ConstantSticker
15 | import com.outsbook.libs.canvaseditor.constants.ConstantStickerIcon
16 | import com.outsbook.libs.canvaseditor.enums.DrawType
17 | import com.outsbook.libs.canvaseditor.events.DeleteIconEvent
18 | import com.outsbook.libs.canvaseditor.events.DoneIconEvent
19 | import com.outsbook.libs.canvaseditor.events.FlipIconEvent
20 | import com.outsbook.libs.canvaseditor.events.ZoomIconEvent
21 | import com.outsbook.libs.canvaseditor.listeners.StickerViewListener
22 | import com.outsbook.libs.canvaseditor.models.DrawObject
23 | import java.util.*
24 | import kotlin.math.abs
25 | import kotlin.math.atan2
26 | import kotlin.math.pow
27 | import kotlin.math.sqrt
28 |
29 | internal class StickerView(context: Context, private val stickerViewListener: StickerViewListener) :
30 | FrameLayout(context) {
31 | var currentSticker: Sticker? = null
32 |
33 | private var currentMode = ActionMode.NONE
34 |
35 | private var isTouchInsideSticker = false
36 |
37 | private val stickerRect = RectF()
38 | private val icons: MutableList = ArrayList(4)
39 | private val bitmapPoints = FloatArray(8)
40 | private val bounds = FloatArray(8)
41 | private val point = FloatArray(2)
42 | private val currentCenterPoint = PointF()
43 | private val tmp = FloatArray(2)
44 | private var midPoint = PointF()
45 |
46 | private val sizeMatrix = Matrix()
47 | private val downMatrix = Matrix()
48 | private val moveMatrix = Matrix()
49 |
50 | private var downX = 0f
51 | private var downY = 0f
52 | private var oldDistance = 0f
53 | private var oldRotation = 0f
54 |
55 | private val borderPaint = Paint().apply {
56 | isAntiAlias = true
57 | color = Color.BLACK
58 | alpha = 50
59 | }
60 | private val iconPaint = Paint().apply {
61 | isAntiAlias = true
62 | color = Color.BLACK
63 | alpha = 128
64 | }
65 |
66 | private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
67 | private var currentIcon: StickerIcon? = null
68 |
69 | init {
70 | configDefaultIcons()
71 | }
72 |
73 | private fun configDefaultIcons() {
74 | val deleteIcon = StickerIcon(
75 | ContextCompat.getDrawable(context, R.drawable.ic_close_white_20dp),
76 | ConstantStickerIcon.LEFT_TOP
77 | )
78 | deleteIcon.iconListener = DeleteIconEvent()
79 | val doneIcon = StickerIcon(
80 | ContextCompat.getDrawable(context, R.drawable.ic_done_white_20dp),
81 | ConstantStickerIcon.RIGHT_TOP
82 | )
83 | doneIcon.iconListener = DoneIconEvent()
84 | val zoomIcon = StickerIcon(
85 | ContextCompat.getDrawable(context, R.drawable.ic_rotate_scale_white_17dp),
86 | ConstantStickerIcon.RIGHT_BOTTOM
87 | )
88 | zoomIcon.iconListener = ZoomIconEvent()
89 | val flipIcon = StickerIcon(
90 | ContextCompat.getDrawable(context, R.drawable.ic_flip_white_20dp),
91 | ConstantStickerIcon.LEFT_BOTTOM
92 | )
93 | flipIcon.iconListener = FlipIconEvent()
94 | icons.clear()
95 | icons.add(deleteIcon)
96 | icons.add(doneIcon)
97 | icons.add(zoomIcon)
98 | icons.add(flipIcon)
99 | }
100 |
101 | override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
102 | super.onSizeChanged(w, h, oldw, oldh)
103 | currentSticker?.let {
104 | transformSticker(it)
105 | }
106 | }
107 |
108 | override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
109 | super.onLayout(changed, left, top, right, bottom)
110 | if (changed) {
111 | stickerRect.left = left.toFloat()
112 | stickerRect.top = top.toFloat()
113 | stickerRect.right = right.toFloat()
114 | stickerRect.bottom = bottom.toFloat()
115 | }
116 | }
117 |
118 | override fun dispatchDraw(canvas: Canvas) {
119 | super.dispatchDraw(canvas)
120 | drawStickers(canvas)
121 | }
122 |
123 | override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
124 | when (ev.action) {
125 | MotionEvent.ACTION_DOWN -> {
126 | downX = ev.x
127 | downY = ev.y
128 | return findCurrentIconTouched() != null || currentSticker != null
129 | }
130 | }
131 | return super.onInterceptTouchEvent(ev)
132 | }
133 |
134 | @SuppressLint("ClickableViewAccessibility")
135 | override fun onTouchEvent(event: MotionEvent): Boolean {
136 | stickerViewListener.onTouchEvent(event)
137 | when (event.actionMasked) {
138 | MotionEvent.ACTION_DOWN -> if (!onTouchDown(event)) {
139 | return false
140 | }
141 | MotionEvent.ACTION_POINTER_DOWN -> {
142 | oldDistance = calculateDistance(event)
143 | oldRotation = calculateRotation(event)
144 | midPoint = calculateMidPoint(event)
145 | if (currentSticker != null && isInStickerArea(
146 | currentSticker!!, event.getX(1),
147 | event.getY(1)
148 | ) && findCurrentIconTouched() == null
149 | ) {
150 | currentMode = ActionMode.ZOOM_WITH_TWO_FINGER
151 | }
152 | }
153 | MotionEvent.ACTION_MOVE -> {
154 | handleCurrentMode(event)
155 | invalidate()
156 | }
157 | MotionEvent.ACTION_UP -> onTouchUp(event)
158 | MotionEvent.ACTION_POINTER_UP -> {
159 | currentMode = ActionMode.NONE
160 | }
161 | }
162 | return true
163 | }
164 |
165 | private fun drawStickers(canvas: Canvas) {
166 | currentSticker?.draw(canvas)
167 | if (currentSticker != null) {
168 | getStickerPoints(currentSticker, bitmapPoints)
169 | val x1 = bitmapPoints[0]
170 | val y1 = bitmapPoints[1]
171 | val x2 = bitmapPoints[2]
172 | val y2 = bitmapPoints[3]
173 | val x3 = bitmapPoints[4]
174 | val y3 = bitmapPoints[5]
175 | val x4 = bitmapPoints[6]
176 | val y4 = bitmapPoints[7]
177 |
178 | //draw border
179 | canvas.drawLine(x1, y1, x2, y2, borderPaint)
180 | canvas.drawLine(x1, y1, x3, y3, borderPaint)
181 | canvas.drawLine(x2, y2, x4, y4, borderPaint)
182 | canvas.drawLine(x4, y4, x3, y3, borderPaint)
183 |
184 | //draw icons
185 | val rotation = calculateRotation(x4, y4, x3, y3)
186 | for (i in icons.indices) {
187 | val icon = icons[i]
188 | when (icon.position) {
189 | ConstantStickerIcon.LEFT_TOP -> configIconMatrix(icon, x1, y1, rotation)
190 | ConstantStickerIcon.RIGHT_TOP -> configIconMatrix(icon, x2, y2, rotation)
191 | ConstantStickerIcon.LEFT_BOTTOM -> configIconMatrix(icon, x3, y3, rotation)
192 | ConstantStickerIcon.RIGHT_BOTTOM -> configIconMatrix(icon, x4, y4, rotation)
193 | }
194 | icon.draw(canvas, iconPaint)
195 | }
196 | }
197 | }
198 |
199 | private fun getStickerPoints(sticker: Sticker?, dst: FloatArray) {
200 | if (sticker == null) {
201 | Arrays.fill(dst, 0f)
202 | return
203 | }
204 | sticker.getBoundPoints(bounds)
205 | sticker.getMappedPoints(dst, bounds)
206 | }
207 |
208 | private fun calculateDistance(event: MotionEvent?): Float {
209 | return if (event == null || event.pointerCount < 2) {
210 | 0f
211 | } else calculateDistance(event.getX(0), event.getY(0), event.getX(1), event.getY(1))
212 | }
213 |
214 | private fun calculateDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
215 | val x = x1 - x2.toDouble()
216 | val y = y1 - y2.toDouble()
217 | return sqrt(x * x + y * y).toFloat()
218 | }
219 |
220 | private fun calculateRotation(event: MotionEvent?): Float {
221 | return if (event == null || event.pointerCount < 2) {
222 | 0f
223 | } else calculateRotation(event.getX(0), event.getY(0), event.getX(1), event.getY(1))
224 | }
225 |
226 | private fun calculateRotation(x1: Float, y1: Float, x2: Float, y2: Float): Float {
227 | val x = x1 - x2.toDouble()
228 | val y = y1 - y2.toDouble()
229 | val radians = atan2(y, x)
230 | return Math.toDegrees(radians).toFloat()
231 | }
232 |
233 | private fun configIconMatrix(icon: StickerIcon, x: Float, y: Float, rotation: Float) {
234 | icon.x = x
235 | icon.y = y
236 | icon.matrix.reset()
237 | icon.matrix.postRotate(rotation, icon.width / 2.toFloat(), icon.height / 2.toFloat())
238 | icon.matrix.postTranslate(x - icon.width / 2, y - icon.height / 2)
239 | }
240 |
241 | private fun transformSticker(sticker: Sticker) {
242 | sizeMatrix.reset()
243 | val width = width.toFloat()
244 | val height = height.toFloat()
245 | val stickerWidth = sticker.width.toFloat()
246 | val stickerHeight = sticker.height.toFloat()
247 | //step 1
248 | val offsetX = (width - stickerWidth) / 2
249 | val offsetY = (height - stickerHeight) / 2
250 | sizeMatrix.postTranslate(offsetX, offsetY)
251 |
252 | //step 2
253 | val scaleFactor: Float
254 | scaleFactor = if (width < height) {
255 | width / stickerWidth
256 | } else {
257 | height / stickerHeight
258 | }
259 | sizeMatrix.postScale(scaleFactor / 2f, scaleFactor / 2f, width / 2f, height / 2f)
260 | sticker.matrix.reset()
261 | sticker.setMatrix(sizeMatrix)
262 | invalidate()
263 | }
264 |
265 | private fun findCurrentIconTouched(): StickerIcon? {
266 | for (icon in icons) {
267 | val x = icon.x - downX
268 | val y = icon.y - downY
269 | val distancePow2 = x * x + y * y
270 | if (distancePow2 <= (icon.iconRadius + icon.iconRadius.toDouble()).pow(2.0)) {
271 | return icon
272 | }
273 | }
274 | return null
275 | }
276 |
277 | private fun onTouchDown(event: MotionEvent): Boolean {
278 | currentMode = ActionMode.DRAG
279 | downX = event.x
280 | downY = event.y
281 | midPoint = calculateMidPoint()
282 | oldDistance = calculateDistance(midPoint.x, midPoint.y, downX, downY)
283 | oldRotation = calculateRotation(midPoint.x, midPoint.y, downX, downY)
284 | currentIcon = findCurrentIconTouched()
285 |
286 | if (currentIcon != null) {
287 | currentMode = ActionMode.ICON
288 | currentIcon!!.onActionDown(this, event)
289 | }
290 |
291 | if (currentSticker != null) {
292 | isTouchInsideSticker = currentSticker!!.contains(downX, downY)
293 | downMatrix.set(Sticker.getMatrix(currentSticker!!))
294 | }
295 |
296 | if (currentIcon == null && !isTouchInsideSticker) {
297 | doneSticker(currentSticker)
298 | return false
299 | }
300 |
301 | invalidate()
302 | return true
303 | }
304 |
305 | private fun handleCurrentMode(event: MotionEvent) {
306 | when (currentMode) {
307 | ActionMode.NONE, ActionMode.CLICK -> {
308 | }
309 | ActionMode.DRAG -> if (currentSticker != null && isTouchInsideSticker) {
310 | moveMatrix.set(downMatrix)
311 | moveMatrix.postTranslate(event.x - downX, event.y - downY)
312 | currentSticker!!.setMatrix(moveMatrix)
313 | }
314 |
315 | ActionMode.ZOOM_WITH_TWO_FINGER -> if (currentSticker != null && isTouchInsideSticker) {
316 | val newDistance = calculateDistance(event)
317 | val newRotation = calculateRotation(event)
318 | moveMatrix.set(downMatrix)
319 | moveMatrix.postScale(
320 | newDistance / oldDistance, newDistance / oldDistance, midPoint.x,
321 | midPoint.y
322 | )
323 | moveMatrix.postRotate(newRotation - oldRotation, midPoint.x, midPoint.y)
324 | currentSticker!!.setMatrix(moveMatrix)
325 | }
326 | ActionMode.ICON -> if (currentSticker != null && currentIcon != null) {
327 | currentIcon!!.onActionMove(this, event)
328 | }
329 | }
330 | }
331 |
332 | private fun onTouchUp(event: MotionEvent) {
333 | if (currentMode == ActionMode.ICON && currentIcon != null && currentSticker != null) {
334 | currentIcon!!.onActionUp(this, event)
335 | }
336 | if (currentMode == ActionMode.DRAG && abs(event.x - downX) < touchSlop && abs(event.y - downY) < touchSlop && currentSticker != null) {
337 | if (!isTouchInsideSticker)
338 | stickerViewListener.onClickStickerOutside(event.x, event.y)
339 | currentMode = ActionMode.CLICK
340 | }
341 | currentMode = ActionMode.NONE
342 | }
343 |
344 | private fun calculateMidPoint(event: MotionEvent?): PointF {
345 | if (event == null || event.pointerCount < 2) {
346 | midPoint[0f] = 0f
347 | return midPoint
348 | }
349 | val x = (event.getX(0) + event.getX(1)) / 2
350 | val y = (event.getY(0) + event.getY(1)) / 2
351 | midPoint[x] = y
352 | return midPoint
353 | }
354 |
355 | private fun calculateMidPoint(): PointF {
356 | if (currentSticker == null) {
357 | midPoint[0f] = 0f
358 | return midPoint
359 | }
360 | currentSticker?.getMappedCenterPoint(midPoint, point, tmp)
361 | return midPoint
362 | }
363 |
364 | private fun isInStickerArea(sticker: Sticker, downX: Float, downY: Float): Boolean {
365 | tmp[0] = downX
366 | tmp[1] = downY
367 | return sticker.contains(tmp)
368 | }
369 |
370 | fun addSticker(sticker: Sticker): StickerView {
371 | return addSticker(sticker, ConstantSticker.CENTER)
372 | }
373 |
374 | private fun addSticker(sticker: Sticker, position: Int): StickerView {
375 | if (ViewCompat.isLaidOut(this)) {
376 | addStickerImmediately(sticker, position)
377 | } else {
378 | post { addStickerImmediately(sticker, position) }
379 | }
380 | return this
381 | }
382 |
383 | private fun addStickerImmediately(sticker: Sticker, position: Int) {
384 | setStickerPosition(sticker, position)
385 | val scaleFactor: Float
386 | val widthScaleFactor: Float = width.toFloat() / sticker.drawable.intrinsicWidth
387 | val heightScaleFactor: Float = height.toFloat() / sticker.drawable.intrinsicHeight
388 | scaleFactor =
389 | if (widthScaleFactor > heightScaleFactor) heightScaleFactor else widthScaleFactor
390 | sticker.matrix.postScale(
391 | scaleFactor / 2,
392 | scaleFactor / 2,
393 | width / 2.toFloat(),
394 | height / 2.toFloat()
395 | )
396 | currentSticker = sticker
397 | //stickers.add(sticker)
398 | invalidate()
399 | }
400 |
401 | private fun setStickerPosition(sticker: Sticker, position: Int) {
402 | val width = width.toFloat()
403 | val height = height.toFloat()
404 | var offsetX = width - sticker.width
405 | var offsetY = height - sticker.height
406 | when {
407 | position and ConstantSticker.TOP > 0 -> offsetY /= 4f
408 | position and ConstantSticker.BOTTOM > 0 -> offsetY *= 3f / 4f
409 | else -> offsetY /= 2f
410 | }
411 | when {
412 | position and ConstantSticker.LEFT > 0 -> offsetX /= 4f
413 | position and ConstantSticker.RIGHT > 0 -> offsetX *= 3f / 4f
414 | else -> offsetX /= 2f
415 | }
416 | sticker.matrix.postTranslate(offsetX, offsetY)
417 | }
418 |
419 | private fun removeSticker(sticker: Sticker?) {
420 | if (sticker == null)
421 | return
422 | currentSticker = null
423 | this.visibility = View.GONE
424 | stickerViewListener.onRemove()
425 | }
426 |
427 | private fun doneSticker(sticker: Sticker?) {
428 | if (sticker == null)
429 | return
430 | currentSticker = null
431 | this.visibility = View.GONE
432 | val obj = DrawObject(null, sticker, DrawType.STICKER)
433 | stickerViewListener.onDone(obj)
434 | }
435 |
436 | private fun zoomAndRotateSticker(sticker: Sticker?, event: MotionEvent) {
437 | if (sticker == null)
438 | return
439 | val newDistance = calculateDistance(midPoint.x, midPoint.y, event.x, event.y)
440 | val newRotation = calculateRotation(midPoint.x, midPoint.y, event.x, event.y)
441 | moveMatrix.set(downMatrix)
442 | moveMatrix.postScale(
443 | newDistance / oldDistance, newDistance / oldDistance, midPoint.x,
444 | midPoint.y
445 | )
446 | moveMatrix.postRotate(newRotation - oldRotation, midPoint.x, midPoint.y)
447 | currentSticker!!.setMatrix(moveMatrix)
448 | stickerViewListener.onZoomAndRotate()
449 | }
450 |
451 | private fun flipSticker(sticker: Sticker?) {
452 | if (sticker == null)
453 | return
454 | sticker.getCenterPoint(midPoint)
455 | sticker.matrix.preScale(-1f, 1f, midPoint.x, midPoint.y)
456 | sticker.isFlippedHorizontally = !sticker.isFlippedHorizontally
457 | invalidate()
458 | stickerViewListener.onFlip()
459 | }
460 |
461 | fun remove() {
462 | removeSticker(currentSticker)
463 | }
464 |
465 | fun done() {
466 | doneSticker(currentSticker)
467 | }
468 |
469 | fun zoomAndRotate(event: MotionEvent) {
470 | zoomAndRotateSticker(currentSticker, event)
471 | }
472 |
473 | fun flip() {
474 | flipSticker(currentSticker)
475 | }
476 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/java/com/outsbook/libs/canvaseditor/stickers/TextSticker.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor.stickers
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Rect
6 | import android.graphics.Typeface
7 | import android.graphics.drawable.Drawable
8 | import android.os.Build
9 | import android.text.Layout
10 | import android.text.StaticLayout
11 | import android.text.TextPaint
12 | import androidx.core.content.ContextCompat
13 | import com.outsbook.libs.canvaseditor.R
14 |
15 | internal class TextSticker(private val context: Context, drawable: Drawable?): Sticker() {
16 | override lateinit var drawable: Drawable
17 |
18 | private var text: String? = null
19 | private val mEllipsis = "\u2026"
20 |
21 | private val realBounds: Rect
22 | private val textRect: Rect
23 | private val textPaint: TextPaint
24 | private var staticLayout: StaticLayout? = null
25 | private var alignment: Layout.Alignment
26 |
27 | private var maxTextSizePixels: Float
28 | private var minTextSizePixels: Float
29 | private var lineSpacingMultiplier = 1.0f
30 | private var lineSpacingExtra = 0.0f
31 |
32 | init {
33 | if (drawable == null) {
34 | this.drawable = ContextCompat.getDrawable(context, R.drawable.shape_transfarent_background)!!
35 | }else{
36 | this.drawable = drawable
37 | }
38 | textPaint = TextPaint(TextPaint.ANTI_ALIAS_FLAG)
39 | realBounds = Rect(0, 0, width, height)
40 | textRect = Rect(0, 0, width, height)
41 | minTextSizePixels = convertSpToPx(6f)
42 | maxTextSizePixels = convertSpToPx(32f)
43 | alignment = Layout.Alignment.ALIGN_CENTER
44 | textPaint.textSize = maxTextSizePixels
45 | }
46 |
47 | override val width: Int
48 | get() = drawable.intrinsicWidth
49 |
50 | override val height: Int
51 | get() = drawable.intrinsicHeight
52 |
53 | override fun draw(canvas: Canvas) {
54 | val matrix = matrix
55 | canvas.save()
56 | canvas.concat(matrix)
57 | drawable.bounds = realBounds
58 | drawable.draw(canvas)
59 |
60 | canvas.restore()
61 | canvas.save()
62 | canvas.concat(matrix)
63 | if (textRect.width() == width) {
64 | val dy = height / 2 - staticLayout!!.height / 2
65 | canvas.translate(0f, dy.toFloat())
66 | } else {
67 | val dx = textRect.left
68 | val dy = textRect.top + textRect.height() / 2 - staticLayout!!.height / 2
69 | canvas.translate(dx.toFloat(), dy.toFloat())
70 | }
71 | staticLayout!!.draw(canvas)
72 | canvas.restore()
73 | }
74 |
75 | override fun setAlpha(alpha: Int): TextSticker {
76 | textPaint.alpha = alpha
77 | return this
78 | }
79 |
80 | override fun setDrawable(drawable: Drawable): TextSticker {
81 | this.drawable = drawable
82 | realBounds[0, 0, width] = height
83 | textRect[0, 0, width] = height
84 | return this
85 | }
86 |
87 | fun setDrawable(drawable: Drawable, region: Rect?): TextSticker {
88 | this.drawable = drawable
89 | realBounds[0, 0, width] = height
90 | if (region == null) {
91 | textRect[0, 0, width] = height
92 | } else {
93 | textRect[region.left, region.top, region.right] = region.bottom
94 | }
95 | return this
96 | }
97 |
98 | fun setText(text: String): TextSticker{
99 | this.text = text
100 | return this
101 | }
102 |
103 | fun setTypeface(typeface: Typeface?): TextSticker {
104 | textPaint.typeface = typeface
105 | return this
106 | }
107 |
108 | fun setTextColor(color: Int): TextSticker {
109 | textPaint.color = color
110 | return this
111 | }
112 |
113 | fun setTextAlign(alignment: Layout.Alignment): TextSticker {
114 | this.alignment = alignment
115 | return this
116 | }
117 |
118 | fun setMaxTextSize(size: Float): TextSticker {
119 | textPaint.textSize = convertSpToPx(size)
120 | maxTextSizePixels = textPaint.textSize
121 | return this
122 | }
123 |
124 | fun setMinTextSize(minTextSizeScaledPixels: Float): TextSticker {
125 | minTextSizePixels = convertSpToPx(minTextSizeScaledPixels)
126 | return this
127 | }
128 |
129 | fun setLineSpacing(add: Float, multiplier: Float): TextSticker {
130 | lineSpacingMultiplier = multiplier
131 | lineSpacingExtra = add
132 | return this
133 | }
134 |
135 | fun resizeText(): TextSticker {
136 | val availableHeightPixels = textRect.height()
137 | val availableWidthPixels = textRect.width()
138 | val text: CharSequence? = text
139 | if (text == null || text.isEmpty() || availableHeightPixels <= 0 || availableWidthPixels <= 0 || maxTextSizePixels <= 0) {
140 | return this
141 | }
142 | var targetTextSizePixels = maxTextSizePixels
143 | var targetTextHeightPixels = getTextHeightPixels(text, availableWidthPixels, targetTextSizePixels)
144 |
145 | while (targetTextHeightPixels > availableHeightPixels
146 | && targetTextSizePixels > minTextSizePixels) {
147 | targetTextSizePixels = Math.max(targetTextSizePixels - 2, minTextSizePixels)
148 | targetTextHeightPixels = getTextHeightPixels(text, availableWidthPixels, targetTextSizePixels)
149 | }
150 |
151 | if (targetTextSizePixels == minTextSizePixels
152 | && targetTextHeightPixels > availableHeightPixels) {
153 | val textPaintCopy = TextPaint(textPaint)
154 | textPaintCopy.textSize = targetTextSizePixels
155 |
156 | val staticLayout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
157 | StaticLayout.Builder
158 | .obtain(text,0, text.length, textPaintCopy, availableWidthPixels)
159 | .setAlignment(Layout.Alignment.ALIGN_NORMAL)
160 | .setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
161 | .setIncludePad(false)
162 | .build()
163 | } else {
164 | @Suppress("DEPRECATION")
165 | (StaticLayout(
166 | text, textPaintCopy, availableWidthPixels, Layout.Alignment.ALIGN_NORMAL,
167 | lineSpacingMultiplier, lineSpacingExtra, false
168 | ))
169 | }
170 |
171 | if (staticLayout.lineCount > 0) {
172 | val lastLine = staticLayout.getLineForVertical(availableHeightPixels) - 1
173 | if (lastLine >= 0) {
174 | val startOffset = staticLayout.getLineStart(lastLine)
175 | var endOffset = staticLayout.getLineEnd(lastLine)
176 | var lineWidthPixels = staticLayout.getLineWidth(lastLine)
177 | val ellipseWidth = textPaintCopy.measureText(mEllipsis)
178 |
179 | while (availableWidthPixels < lineWidthPixels + ellipseWidth) {
180 | endOffset--
181 | lineWidthPixels = textPaintCopy.measureText(text.subSequence(startOffset, endOffset + 1).toString())
182 | }
183 | setText(text.subSequence(0, endOffset).toString() + mEllipsis)
184 | }
185 | }
186 | }
187 | textPaint.textSize = targetTextSizePixels
188 | staticLayout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
189 | StaticLayout.Builder
190 | .obtain(this.text!!,0, this.text!!.length, textPaint, textRect.width())
191 | .setAlignment(alignment)
192 | .setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
193 | .setIncludePad(true)
194 | .build()
195 | } else {
196 | @Suppress("DEPRECATION")
197 | (StaticLayout(
198 | this.text!!, textPaint, availableWidthPixels, alignment,
199 | lineSpacingMultiplier, lineSpacingExtra, true
200 | ))
201 | }
202 | return this
203 | }
204 |
205 | private fun getTextHeightPixels(source: CharSequence, availableWidthPixels: Int, textSizePixels: Float): Int {
206 | textPaint.textSize = textSizePixels
207 | val staticLayout = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
208 | StaticLayout.Builder
209 | .obtain(source,0, 0, textPaint, availableWidthPixels)
210 | .setAlignment(Layout.Alignment.ALIGN_NORMAL)
211 | .setLineSpacing(lineSpacingExtra, lineSpacingMultiplier)
212 | .setIncludePad(true)
213 | .build()
214 | } else {
215 | @Suppress("DEPRECATION")
216 | (StaticLayout(
217 | source, textPaint, availableWidthPixels, Layout.Alignment.ALIGN_NORMAL,
218 | lineSpacingMultiplier, lineSpacingExtra, true
219 | ))
220 | }
221 |
222 | return staticLayout.height
223 | }
224 |
225 | private fun convertSpToPx(scaledPixels: Float): Float {
226 | return scaledPixels * context.resources.displayMetrics.scaledDensity
227 | }
228 | }
--------------------------------------------------------------------------------
/canvaseditor/src/main/res/drawable/ic_close_white_20dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/canvaseditor/src/main/res/drawable/ic_done_white_20dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/canvaseditor/src/main/res/drawable/ic_flip_white_20dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/canvaseditor/src/main/res/drawable/ic_rotate_scale_white_17dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/canvaseditor/src/main/res/drawable/shape_transfarent_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/canvaseditor/src/test/java/com/outsbook/libs/canvaseditor/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.libs.canvaseditor
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example-java/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/example-java/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/example-java/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion "29.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.outsbook.examplejava"
9 | minSdkVersion 23
10 | targetSdkVersion 29
11 | versionCode 1
12 | versionName "1.0"
13 |
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 |
29 | }
30 |
31 | dependencies {
32 | implementation fileTree(dir: 'libs', include: ['*.jar'])
33 |
34 | implementation 'androidx.appcompat:appcompat:1.1.0'
35 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
36 | testImplementation 'junit:junit:4.13'
37 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
39 |
40 | implementation 'com.outsbook.libs:canvaseditor:1.0.0'
41 | }
42 |
--------------------------------------------------------------------------------
/example-java/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/example-java/app/src/androidTest/java/com/outsbook/examplejava/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.outsbook.examplejava;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("com.outsbook.examplejava", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example-java/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example-java/app/src/main/java/com/outsbook/examplejava/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.outsbook.examplejava;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import androidx.core.content.ContextCompat;
5 | import android.graphics.Bitmap;
6 | import android.graphics.drawable.Drawable;
7 | import android.os.Bundle;
8 | import android.view.MotionEvent;
9 | import android.view.View;
10 | import android.widget.ImageButton;
11 | import android.widget.ImageView;
12 |
13 | import com.outsbook.libs.canvaseditor.CanvasEditorView;
14 | import com.outsbook.libs.canvaseditor.listeners.CanvasEditorListener;
15 |
16 | public class MainActivity extends AppCompatActivity {
17 |
18 | private CanvasEditorView canvasEditor;
19 |
20 | private ImageButton buttonSticker;
21 | private ImageButton buttonText;
22 | private ImageButton buttonStickerText;
23 | private ImageButton buttonBlack;
24 | private ImageButton buttonYellow;
25 | private ImageButton buttonPlus;
26 | private ImageButton buttonMinus;
27 |
28 | private ImageButton buttonSave;
29 | private ImageButton buttonUndo;
30 | private ImageButton buttonRedo;
31 | private ImageButton buttonDelete;
32 |
33 | private View viewImagePreview;
34 | private ImageButton buttonClose;
35 | private ImageView imageView;
36 |
37 | private Float strokeWidth = 20f;
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_main);
43 | initView();
44 | initValue();
45 | initClickListener();
46 | initCanvasEditorListener();
47 | }
48 |
49 | private void initView(){
50 | canvasEditor = findViewById(R.id.canvasEditor);
51 |
52 | buttonSticker = findViewById(R.id.buttonSticker);
53 | buttonText = findViewById(R.id.buttonText);
54 | buttonStickerText = findViewById(R.id.buttonStickerText);
55 | buttonBlack = findViewById(R.id.buttonBlack);
56 | buttonYellow = findViewById(R.id.buttonYellow);
57 | buttonPlus = findViewById(R.id.buttonPlus);
58 | buttonMinus = findViewById(R.id.buttonMinus);
59 |
60 | buttonSave = findViewById(R.id.buttonSave);
61 | buttonUndo = findViewById(R.id.buttonUndo);
62 | buttonRedo = findViewById(R.id.buttonRedo);
63 | buttonDelete = findViewById(R.id.buttonDelete);
64 |
65 | viewImagePreview = findViewById(R.id.viewImagePreview);
66 | buttonClose = findViewById(R.id.buttonClose);
67 | imageView = findViewById(R.id.imageView);
68 | }
69 |
70 | private void initValue(){
71 | buttonUndo.setImageAlpha(50);
72 | buttonRedo.setImageAlpha(50);
73 | //set stroke width
74 | canvasEditor.setStrokeWidth(strokeWidth);
75 | //set paint color
76 | canvasEditor.setPaintColor(ContextCompat.getColor(this, R.color.colorBlack));
77 | }
78 |
79 | private void initClickListener(){
80 | buttonSticker.setOnClickListener(v -> {
81 | Drawable drawable = ContextCompat.getDrawable(this, R.drawable.app_icon);
82 | if(drawable != null)
83 | canvasEditor.addDrawableSticker(drawable);
84 | });
85 | buttonText.setOnClickListener(v -> {
86 | String text = "Canvas";
87 | int color = ContextCompat.getColor(this, R.color.colorPrimary);
88 | canvasEditor.addTextSticker(text, color, null);
89 | });
90 | buttonStickerText.setOnClickListener(v -> {
91 | Drawable drawable = ContextCompat.getDrawable(this, R.drawable.ic_panorama_240dp);
92 | String text = "Canvas";
93 | int textColor = ContextCompat.getColor(this, R.color.colorAccent);
94 | if(drawable != null)
95 | canvasEditor.addDrawableTextSticker(drawable, text, textColor, null);
96 | });
97 | buttonBlack.setOnClickListener(v -> {
98 | buttonPlus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_plus_black_24dp));
99 | buttonMinus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_minus_black_24dp));
100 | int color = ContextCompat.getColor(this, R.color.colorBlack);
101 | canvasEditor.setPaintColor(color);
102 | });
103 | buttonYellow.setOnClickListener(v -> {
104 | buttonPlus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_plus_yellow_24dp));
105 | buttonMinus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_minus_yellow_24dp));
106 | int color = ContextCompat.getColor(this, R.color.colorYellow);
107 | canvasEditor.setPaintColor(color);
108 | });
109 | buttonPlus.setOnClickListener(v -> {
110 | strokeWidth += 10f;
111 | canvasEditor.setStrokeWidth(strokeWidth);
112 | });
113 | buttonMinus.setOnClickListener(v -> {
114 | strokeWidth -= 10f;
115 | canvasEditor.setStrokeWidth(strokeWidth);
116 | });
117 |
118 | buttonSave.setOnClickListener(v -> {
119 | Bitmap bitmap = canvasEditor.downloadBitmap();
120 | imageView.setImageBitmap(bitmap);
121 | viewImagePreview.setVisibility(View.VISIBLE);
122 | });
123 | buttonUndo.setOnClickListener(v -> {
124 | canvasEditor.undo();
125 | });
126 | buttonRedo.setOnClickListener(v -> {
127 | canvasEditor.redo();
128 | });
129 | buttonDelete.setOnClickListener(v -> {
130 | canvasEditor.removeAll();
131 | });
132 |
133 | buttonClose.setOnClickListener(v -> {
134 | viewImagePreview.setVisibility(View.GONE);
135 | });
136 | }
137 |
138 | private void initCanvasEditorListener(){
139 | canvasEditor.setListener(new CanvasEditorListener() {
140 | @Override
141 | public void onEnableUndo(boolean isEnable) {
142 | // isEnable = true (undo list is not empty)
143 | // isEnable = false (undo list is empty)
144 | buttonUndo.setImageAlpha(isEnable? 255 : 50);
145 | }
146 |
147 | @Override
148 | public void onEnableRedo(boolean isEnable) {
149 | // isEnable = true (redo list is not empty)
150 | // isEnable = false (redo list is empty)
151 | buttonRedo.setImageAlpha(isEnable? 255 : 50);
152 | }
153 |
154 | @Override
155 | public void onTouchEvent(MotionEvent motionEvent) {
156 | //When the canvas touch
157 | }
158 |
159 | @Override
160 | public void onStickerActive() {
161 | //When a sticker change to active mode
162 | }
163 |
164 | @Override
165 | public void onStickerRemove() {
166 | //When a sticker remove from canvas
167 | }
168 |
169 | @Override
170 | public void onStickerDone() {
171 | //When the active sticker added to canvas
172 | }
173 |
174 | @Override
175 | public void onStickerZoomAndRotate() {
176 | //When the active sticker zoom or rotate
177 | }
178 |
179 | @Override
180 | public void onStickerFlip() {
181 | //When the active sticker flip
182 | }
183 | });
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/drawable/app_icon.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_cancel_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_color_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_color_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_delete_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_download_gray_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_minus_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_minus_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_panorama_240dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_plus_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_plus_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_redo_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_sticker_text_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_sticker_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_text_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/ic_undo_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/shape_delete_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/shape_redo_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/drawable/shape_undo_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
25 |
26 |
36 |
37 |
47 |
48 |
56 |
65 |
74 |
83 |
92 |
101 |
110 |
119 |
128 |
129 |
130 |
136 |
143 |
144 |
148 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-java/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #DB2659
4 | #DB2659
5 | #FEC63B
6 |
7 | #000000
8 | #FEC63B
9 |
10 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F7F7F7
4 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Canvas Editor
3 |
4 |
--------------------------------------------------------------------------------
/example-java/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example-java/app/src/test/java/com/outsbook/examplejava/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.outsbook.examplejava;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/example-java/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.6.3'
12 |
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 |
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/example-java/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/example-java/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-java/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example-java/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 01 00:22:55 BDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/example-java/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/example-java/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/example-java/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='example-java'
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/example-kotlin/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/example-kotlin/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/example-kotlin/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 29
7 | buildToolsVersion "29.0.2"
8 |
9 | defaultConfig {
10 | applicationId "com.outsbook.examplekotlin"
11 | minSdkVersion 23
12 | targetSdkVersion 29
13 | versionCode 1
14 | versionName "1.0.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
31 | implementation 'androidx.appcompat:appcompat:1.1.0'
32 | implementation 'androidx.core:core-ktx:1.2.0'
33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
34 | testImplementation 'junit:junit:4.13'
35 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
36 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
37 |
38 | implementation 'com.outsbook.libs:canvaseditor:1.0.0'
39 | }
40 |
--------------------------------------------------------------------------------
/example-kotlin/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/androidTest/java/com/outsbook/examplekotlin/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.examplekotlin
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.outsbook.examplekotlin", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/java/com/outsbook/examplekotlin/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.examplekotlin
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 | import android.view.MotionEvent
6 | import android.view.View
7 | import androidx.core.content.ContextCompat
8 | import com.outsbook.libs.canvaseditor.listeners.CanvasEditorListener
9 | import kotlinx.android.synthetic.main.activity_main.*
10 |
11 | class MainActivity : AppCompatActivity() {
12 |
13 | private var strokeWidth: Float = 20f
14 |
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | setContentView(R.layout.activity_main)
18 | initValue()
19 | initClickListener()
20 | initCanvasEditorListener()
21 | }
22 |
23 | private fun initValue(){
24 | buttonUndo.imageAlpha = 50
25 | buttonRedo.imageAlpha = 50
26 | //set stroke width
27 | canvasEditor.setStrokeWidth(strokeWidth)
28 | //set paint color
29 | canvasEditor.setPaintColor(ContextCompat.getColor(this, R.color.colorBlack))
30 | }
31 |
32 | private fun initClickListener(){
33 | buttonSticker.setOnClickListener{
34 | //Add drawable sticker
35 | val drawable = ContextCompat.getDrawable(this, R.drawable.app_icon)
36 | drawable?.let {
37 | canvasEditor.addDrawableSticker(it)
38 | }
39 | }
40 |
41 | buttonText.setOnClickListener{
42 | //Add text sticker
43 | val text = "Canvas"
44 | val textColor = ContextCompat.getColor(this, R.color.colorPrimary)
45 | canvasEditor.addTextSticker(text, textColor, null)
46 | }
47 |
48 | buttonStickerText.setOnClickListener{
49 | //Add text with drawable sticker
50 | val drawable = ContextCompat.getDrawable(this, R.drawable.ic_panorama_240dp)
51 | val text = "Canvas"
52 | val textColor = ContextCompat.getColor(this, R.color.colorAccent)
53 | drawable?.let{
54 | canvasEditor.addDrawableTextSticker(it, text, textColor, null)
55 | }
56 | }
57 |
58 | buttonBlack.setOnClickListener {
59 | buttonPlus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_plus_black_24dp))
60 | buttonMinus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_minus_black_24dp))
61 | val color = ContextCompat.getColor(this, R.color.colorBlack)
62 | canvasEditor.setPaintColor(color)
63 | }
64 |
65 | buttonYellow.setOnClickListener {
66 | buttonPlus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_plus_yellow_24dp))
67 | buttonMinus.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_minus_yellow_24dp))
68 | val color = ContextCompat.getColor(this, R.color.colorYellow)
69 | canvasEditor.setPaintColor(color)
70 | }
71 |
72 | buttonPlus.setOnClickListener {
73 | strokeWidth += 10f
74 | canvasEditor.setStrokeWidth(strokeWidth)
75 | }
76 |
77 | buttonMinus.setOnClickListener {
78 | strokeWidth -= 10f
79 | canvasEditor.setStrokeWidth(strokeWidth)
80 | }
81 |
82 | buttonSave.setOnClickListener {
83 | val bitmap = canvasEditor.downloadBitmap()
84 | imageView.setImageBitmap(bitmap)
85 | viewImagePreview.visibility = View.VISIBLE
86 | }
87 |
88 | buttonUndo.setOnClickListener {
89 | canvasEditor.undo()
90 | }
91 |
92 | buttonDelete.setOnClickListener {
93 | canvasEditor.removeAll()
94 | }
95 |
96 | buttonRedo.setOnClickListener {
97 | canvasEditor.redo()
98 | }
99 |
100 | buttonClose.setOnClickListener {
101 | viewImagePreview.visibility = View.GONE
102 | }
103 | }
104 |
105 | private fun initCanvasEditorListener(){
106 | canvasEditor.setListener(object: CanvasEditorListener {
107 | override fun onEnableUndo(isEnable: Boolean) {
108 | // isEnable = true (undo list is not empty)
109 | // isEnable = false (undo list is empty)
110 | buttonUndo.imageAlpha = if(isEnable) 255 else 50
111 | }
112 |
113 | override fun onEnableRedo(isEnable: Boolean) {
114 | // isEnable = true (redo list is not empty)
115 | // isEnable = false (redo list is empty)
116 | buttonRedo.imageAlpha = if(isEnable) 255 else 50
117 | }
118 |
119 | override fun onTouchEvent(event: MotionEvent) {
120 | //When the canvas touch
121 | }
122 |
123 | override fun onStickerActive() {
124 | //When a sticker change to active mode
125 | }
126 |
127 | override fun onStickerRemove() {
128 | //When a sticker remove from canvas
129 | }
130 |
131 | override fun onStickerDone() {
132 | //When the active sticker added to canvas
133 | }
134 |
135 | override fun onStickerZoomAndRotate() {
136 | //When the active sticker zoom or rotate
137 | }
138 |
139 | override fun onStickerFlip() {
140 | //When the active sticker flip
141 | }
142 | })
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/drawable/app_icon.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_cancel_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_color_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_color_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_delete_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_download_gray_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_minus_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_minus_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_panorama_240dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_plus_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_plus_yellow_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_redo_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_sticker_text_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_sticker_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_text_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/ic_undo_white_24dp.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/shape_delete_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/shape_redo_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/drawable/shape_undo_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
25 |
26 |
36 |
37 |
47 |
48 |
56 |
65 |
74 |
83 |
92 |
101 |
110 |
119 |
128 |
129 |
130 |
136 |
143 |
144 |
148 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #DB2659
4 | #DB2659
5 | #FEC63B
6 |
7 | #000000
8 | #FEC63B
9 |
10 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #F7F7F7
4 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Canvas Editor
3 |
4 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example-kotlin/app/src/test/java/com/outsbook/examplekotlin/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.outsbook.examplekotlin
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example-kotlin/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.72'
5 | repositories {
6 | google()
7 | jcenter()
8 |
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.6.3'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 | }
17 | }
18 |
19 | allprojects {
20 | repositories {
21 | google()
22 | jcenter()
23 |
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/example-kotlin/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/example-kotlin/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/example-kotlin/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example-kotlin/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Apr 30 23:07:38 BDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/example-kotlin/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/example-kotlin/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/example-kotlin/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='ExampleKotlin'
2 | include ':app'
3 |
--------------------------------------------------------------------------------
/screenshot/screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_1.png
--------------------------------------------------------------------------------
/screenshot/screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_2.png
--------------------------------------------------------------------------------
/screenshot/screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_3.png
--------------------------------------------------------------------------------
/screenshot/screenshot_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_4.png
--------------------------------------------------------------------------------
/screenshot/screenshot_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_5.png
--------------------------------------------------------------------------------
/screenshot/screenshot_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_6.png
--------------------------------------------------------------------------------
/screenshot/screenshot_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/outsbook/CanvasEditor/db102fb1ee36ca4e23fb657354f502033cccde02/screenshot/screenshot_7.png
--------------------------------------------------------------------------------