├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── preloaded_fonts.xml
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── font_certs.xml
│ │ │ ├── values-land
│ │ │ │ └── dimens.xml
│ │ │ ├── values-w1240dp
│ │ │ │ └── dimens.xml
│ │ │ ├── values-w600dp
│ │ │ │ └── dimens.xml
│ │ │ ├── font
│ │ │ │ ├── aladin.ttf
│ │ │ │ ├── alata.ttf
│ │ │ │ └── baloo_da.ttf
│ │ │ ├── drawable
│ │ │ │ ├── btn_bg.png
│ │ │ │ ├── board_bg.png
│ │ │ │ ├── hand_400x.png
│ │ │ │ ├── drawing_app_bg.jpg
│ │ │ │ ├── drawing_app_home_image.png
│ │ │ │ ├── rounded_white_bg.xml
│ │ │ │ ├── rounded_grey_400_bg.xml
│ │ │ │ ├── redo_selector.xml
│ │ │ │ ├── undo_selector.xml
│ │ │ │ ├── rounded_stroke_white_bg.xml
│ │ │ │ ├── circle_selector_bg.xml
│ │ │ │ ├── btn_default_white_bg.xml
│ │ │ │ ├── btn_selected_white_bg.xml
│ │ │ │ ├── circle_stroke_bg.xml
│ │ │ │ ├── white_with_stroke_bg.xml
│ │ │ │ ├── btn_hovered_white_bg.xml
│ │ │ │ ├── circle_selected_bg.xml
│ │ │ │ ├── drawing_selector.xml
│ │ │ │ ├── btn_selector.xml
│ │ │ │ ├── ic_radio_unchecked.xml
│ │ │ │ ├── ic_paint.xml
│ │ │ │ ├── arrow_down.xml
│ │ │ │ ├── ic_outline_paint.xml
│ │ │ │ ├── empty_board.xml
│ │ │ │ ├── ic_radio_checked.xml
│ │ │ │ ├── ic_brush.xml
│ │ │ │ ├── ic_edit.xml
│ │ │ │ ├── ic_delete.xml
│ │ │ │ ├── ic_undo.xml
│ │ │ │ ├── ic_redo.xml
│ │ │ │ ├── ic_undo_disabled.xml
│ │ │ │ ├── ic_redo_disabled.xml
│ │ │ │ ├── ic_close.xml
│ │ │ │ ├── ic_search.xml
│ │ │ │ ├── ic_outline_brush.xml
│ │ │ │ ├── gallery.xml
│ │ │ │ ├── eraser.xml
│ │ │ │ ├── number1.xml
│ │ │ │ ├── paint.xml
│ │ │ │ ├── number4.xml
│ │ │ │ ├── brush.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── letterd.xml
│ │ │ │ ├── b_bold_inside.xml
│ │ │ │ ├── lettera.xml
│ │ │ │ ├── letterc.xml
│ │ │ │ ├── number2.xml
│ │ │ │ ├── letterb.xml
│ │ │ │ ├── number3.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── button_bg.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── anim
│ │ │ │ ├── slide_in_left.xml
│ │ │ │ ├── slide_in_right.xml
│ │ │ │ ├── slide_out_left.xml
│ │ │ │ └── slide_out_right.xml
│ │ │ ├── menu
│ │ │ │ └── menu_main.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── content_main.xml
│ │ │ │ ├── drawing_item.xml
│ │ │ │ ├── fragment_choose_drawing.xml
│ │ │ │ ├── fragment_home.xml
│ │ │ │ ├── fragment_saved_drawings.xml
│ │ │ │ └── fragment_drawing.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ ├── values-night
│ │ │ │ └── themes.xml
│ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ └── navigation
│ │ │ │ └── nav_graph.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── mohamedbenrejeb
│ │ │ │ └── drawapplication
│ │ │ │ ├── utils
│ │ │ │ ├── Utils.kt
│ │ │ │ └── Constants.kt
│ │ │ │ ├── models
│ │ │ │ ├── GoToType.kt
│ │ │ │ ├── Tool.kt
│ │ │ │ ├── DrawingType.kt
│ │ │ │ ├── TutorialPoint.kt
│ │ │ │ ├── Drawing.kt
│ │ │ │ └── SavedDrawing.kt
│ │ │ │ ├── adapters
│ │ │ │ ├── DrawingAdapter.kt
│ │ │ │ └── SavedDrawingAdapter.kt
│ │ │ │ ├── ui
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── ChooseDrawingFragment.kt
│ │ │ │ ├── SavedDrawingsFragment.kt
│ │ │ │ └── DrawingFragment.kt
│ │ │ │ └── data
│ │ │ │ └── DrawingData.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── mohamedbenrejeb
│ │ │ └── drawapplication
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── mohamedbenrejeb
│ │ └── drawapplication
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle
├── .idea
├── .name
├── .gitignore
├── compiler.xml
├── vcs.xml
├── deploymentTargetDropDown.xml
├── gradle.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── README.md
├── .gitignore
├── settings.gradle
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Draw Application
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w1240dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.utils
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/font/aladin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/font/aladin.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/alata.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/font/alata.ttf
--------------------------------------------------------------------------------
/app/src/main/res/font/baloo_da.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/font/baloo_da.ttf
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/drawable/btn_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/board_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/drawable/board_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/hand_400x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/drawable/hand_400x.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Drawing Android App
2 | Native android drawing app using Kotlin and XML
3 | - Canvas
4 | - Save files
5 | - Load files
6 | - Animations
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drawing_app_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/drawable/drawing_app_bg.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drawing_app_home_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/drawable/drawing_app_home_image.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MohamedRejeb/drawing-android-app/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/models/GoToType.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.models
2 |
3 | enum class GoToType {
4 | AnimationTo, JumpTo
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/models/Tool.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.models
2 |
3 | enum class Tool {
4 | Brush, Pail, Eraser, Colors
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/models/DrawingType.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.models
2 |
3 | enum class DrawingType {
4 | Letter, Number, Animal
5 | }
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/models/TutorialPoint.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.models
2 |
3 | data class TutorialPoint(
4 | val xPercent: Float,
5 | val yPercent: Float,
6 | val goToType: GoToType
7 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_white_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_grey_400_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/utils/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.utils
2 |
3 | import android.os.Environment
4 |
5 | val drawingsDir = "${Environment.getExternalStorageDirectory()}/${Environment.DIRECTORY_DCIM}/Drawings"
--------------------------------------------------------------------------------
/app/src/main/res/values/preloaded_fonts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @font/aladin
5 | - @font/baloo_da
6 |
7 |
8 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jul 19 14:59:37 CET 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/.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 | local.properties
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/redo_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/undo_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_stroke_white_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_selector_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/models/Drawing.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.models
2 |
3 | data class Drawing(
4 | val id: Int,
5 | val text: String,
6 | val imageResource: Int,
7 | val type: DrawingType,
8 | val tutorialPoints: List = emptyList()
9 | )
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_default_white_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_selected_white_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_stroke_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/white_with_stroke_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_hovered_white_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/circle_selected_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/drawing_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/models/SavedDrawing.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.models
2 |
3 | import java.io.File
4 | import java.util.*
5 |
6 | data class SavedDrawing(
7 | val id: String = UUID.randomUUID().toString(),
8 | val file: File,
9 | val isSelected: Boolean = false,
10 | val isSelectionActive: Boolean = false
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "Draw Application"
16 | include ':app'
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_radio_unchecked.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_paint.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/test/java/com/mohamedbenrejeb/drawapplication/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication
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 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/arrow_down.xml:
--------------------------------------------------------------------------------
1 |
6 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_paint.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/empty_board.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_radio_checked.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_brush.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_undo.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_redo.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_undo_disabled.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_redo_disabled.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_search.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_outline_brush.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Draw Application
3 | Settings
4 |
5 | First Fragment
6 | Second Fragment
7 | Next
8 | Previous
9 |
10 | Hello first fragment
11 | Hello second fragment. Arg: %1$s
12 |
13 | Hello blank fragment
14 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/mohamedbenrejeb/drawapplication/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication
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.mohamedbenrejeb.drawapplication", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/gallery.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/eraser.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 |
9 | #FFf5f5f5
10 | #FFe1e7ec
11 | #FFd7dadd
12 | #FF888888
13 |
14 | #FFe6e5d1
15 | #FF7777BB
16 |
17 | #FF96440c
18 | #FF562500
19 |
20 | #FF000000
21 | #FFFFFFFF
22 | #FFAA0000
23 | #FF00AA00
24 | #FF0000AA
25 | #FFFFAA00
26 |
27 | #FFDC143C
28 |
29 | #00000000
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/number1.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/paint.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/drawing_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
20 |
21 |
33 |
--------------------------------------------------------------------------------
/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=-Xmx2048m -Dfile.encoding=UTF-8
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 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
20 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/number4.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/adapters/DrawingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.adapters
2 |
3 | import android.util.Log
4 | import android.view.LayoutInflater
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.mohamedbenrejeb.drawapplication.databinding.DrawingItemBinding
8 | import com.mohamedbenrejeb.drawapplication.models.Drawing
9 |
10 | class DrawingAdapter(
11 | private val drawingList: List,
12 | private val navigateToDrawing: (drawing: Drawing) -> Unit
13 | ): RecyclerView.Adapter() {
14 |
15 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DrawingVH {
16 | val layoutInflater = LayoutInflater.from(parent.context)
17 | val binding = DrawingItemBinding.inflate(layoutInflater, parent, false)
18 | return DrawingVH(binding)
19 | }
20 |
21 | override fun onBindViewHolder(holder: DrawingVH, position: Int) {
22 | val drawing = drawingList[position]
23 | holder.bind(drawing)
24 | }
25 |
26 | override fun getItemCount(): Int {
27 | return drawingList.size
28 | }
29 |
30 | inner class DrawingVH(
31 | private val binding: DrawingItemBinding
32 | ): RecyclerView.ViewHolder(binding.root) {
33 | fun bind(drawing: Drawing) {
34 | binding.cardView.setOnClickListener {
35 | navigateToDrawing(drawing)
36 | }
37 | binding.drawingIv.setImageResource(drawing.imageResource)
38 | }
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/brush.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'androidx.navigation.safeargs.kotlin'
5 | }
6 |
7 | android {
8 | compileSdk 32
9 |
10 | defaultConfig {
11 | applicationId "com.mohamedbenrejeb.drawapplication"
12 | minSdk 21
13 | targetSdk 32
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = '1.8'
32 | }
33 | buildFeatures {
34 | viewBinding true
35 | }
36 | }
37 |
38 | dependencies {
39 |
40 | implementation 'androidx.core:core-ktx:1.8.0'
41 | implementation 'androidx.appcompat:appcompat:1.4.2'
42 | implementation 'com.google.android.material:material:1.6.1'
43 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
44 | implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0'
45 | implementation 'androidx.navigation:navigation-ui-ktx:2.5.0'
46 | testImplementation 'junit:junit:4.13.2'
47 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
49 |
50 | // Navigation Component
51 | implementation "androidx.navigation:navigation-fragment-ktx:2.5.0"
52 | implementation "androidx.navigation:navigation-ui-ktx:2.5.0"
53 | api "androidx.navigation:navigation-dynamic-features-fragment:2.5.0"
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/ui/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.ui
2 |
3 | import android.os.Bundle
4 | import androidx.fragment.app.Fragment
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.navigation.fragment.findNavController
9 | import com.mohamedbenrejeb.drawapplication.databinding.FragmentHomeBinding
10 | import com.mohamedbenrejeb.drawapplication.models.DrawingType
11 |
12 | class HomeFragment : Fragment() {
13 | private var _binding: FragmentHomeBinding? = null
14 | private val binding get() = _binding!!
15 |
16 | override fun onCreateView(
17 | inflater: LayoutInflater, container: ViewGroup?,
18 | savedInstanceState: Bundle?
19 | ): View {
20 | _binding = FragmentHomeBinding.inflate(inflater, container, false)
21 | return binding.root
22 | }
23 |
24 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
25 | super.onViewCreated(view, savedInstanceState)
26 |
27 | binding.newDrawingBtn.setOnClickListener {
28 | findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToFirstFragment())
29 | }
30 | binding.colorNumbersBtn.setOnClickListener {
31 | findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToChooseDrawingFragment(
32 | DrawingType.Number
33 | ))
34 | }
35 | binding.colorLettersBtn.setOnClickListener {
36 | findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToChooseDrawingFragment(
37 | DrawingType.Letter
38 | )) }
39 | binding.colorAnimalsBtn.setOnClickListener {
40 | findNavController().navigate(HomeFragmentDirections.actionHomeFragmentToChooseDrawingFragment(
41 | DrawingType.Animal
42 | ))
43 | }
44 | }
45 |
46 | override fun onDestroyView() {
47 | super.onDestroyView()
48 | _binding = null
49 | }
50 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/ui/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.ui
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.navigation.findNavController
6 | import androidx.navigation.ui.AppBarConfiguration
7 | import androidx.navigation.ui.navigateUp
8 | import android.view.Menu
9 | import android.view.MenuItem
10 | import com.mohamedbenrejeb.drawapplication.R
11 | import com.mohamedbenrejeb.drawapplication.databinding.ActivityMainBinding
12 |
13 | class MainActivity : AppCompatActivity() {
14 |
15 | private lateinit var appBarConfiguration: AppBarConfiguration
16 | private lateinit var binding: ActivityMainBinding
17 |
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 |
21 | binding = ActivityMainBinding.inflate(layoutInflater)
22 | setContentView(binding.root)
23 |
24 | supportActionBar?.hide()
25 |
26 | val navController = findNavController(R.id.nav_host_fragment_content_main)
27 | appBarConfiguration = AppBarConfiguration(navController.graph)
28 | }
29 |
30 | override fun onCreateOptionsMenu(menu: Menu): Boolean {
31 | // Inflate the menu; this adds items to the action bar if it is present.
32 | menuInflater.inflate(R.menu.menu_main, menu)
33 | return true
34 | }
35 |
36 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
37 | // Handle action bar item clicks here. The action bar will
38 | // automatically handle clicks on the Home/Up button, so long
39 | // as you specify a parent activity in AndroidManifest.xml.
40 | return when (item.itemId) {
41 | R.id.action_settings -> true
42 | else -> super.onOptionsItemSelected(item)
43 | }
44 | }
45 |
46 | override fun onSupportNavigateUp(): Boolean {
47 | val navController = findNavController(R.id.nav_host_fragment_content_main)
48 | return navController.navigateUp(appBarConfiguration)
49 | || super.onSupportNavigateUp()
50 | }
51 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/colors.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/letterd.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/b_bold_inside.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/lettera.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/letterc.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/ui/ChooseDrawingFragment.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.ui
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import androidx.fragment.app.Fragment
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import androidx.navigation.fragment.findNavController
10 | import androidx.navigation.fragment.navArgs
11 | import com.mohamedbenrejeb.drawapplication.R
12 | import com.mohamedbenrejeb.drawapplication.adapters.DrawingAdapter
13 | import com.mohamedbenrejeb.drawapplication.data.DrawingData
14 | import com.mohamedbenrejeb.drawapplication.databinding.FragmentChooseDrawingBinding
15 | import com.mohamedbenrejeb.drawapplication.models.DrawingType
16 |
17 | class ChooseDrawingFragment : Fragment() {
18 | private var _binding: FragmentChooseDrawingBinding? = null
19 | private val binding get() = _binding!!
20 |
21 | private val args by navArgs()
22 |
23 | override fun onCreateView(
24 | inflater: LayoutInflater, container: ViewGroup?,
25 | savedInstanceState: Bundle?
26 | ): View? {
27 | _binding = FragmentChooseDrawingBinding.inflate(inflater, container, false)
28 | return binding.root
29 | }
30 |
31 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
32 | super.onViewCreated(view, savedInstanceState)
33 |
34 | binding.backBtn.setOnClickListener {
35 | findNavController().popBackStack()
36 | }
37 |
38 | binding.savedDrawingsFab.setOnClickListener {
39 | findNavController().navigate(
40 | ChooseDrawingFragmentDirections.actionChooseDrawingFragmentToSavedDrawingsFragment()
41 | )
42 | }
43 |
44 | binding.appBarTitle.text =
45 | when (args.drawingType) {
46 | DrawingType.Number -> "Choose Number"
47 | DrawingType.Letter -> "Choose Letter"
48 | DrawingType.Animal -> "Choose Animal"
49 | }
50 |
51 | val drawingList = DrawingData.getDrawingListByType(args.drawingType)
52 | val adapter = DrawingAdapter(drawingList) { drawing ->
53 | findNavController().navigate(
54 | ChooseDrawingFragmentDirections.actionChooseDrawingFragmentToDrawingFragment(
55 | drawingId = drawing.id
56 | )
57 | )
58 | }
59 | binding.drawingsRv.let {
60 | it.setHasFixedSize(true)
61 | it.adapter = adapter
62 | }
63 | }
64 |
65 | override fun onDestroyView() {
66 | super.onDestroyView()
67 | _binding = null
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/number2.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/letterb.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/number3.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/data/DrawingData.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.data
2 |
3 | import com.mohamedbenrejeb.drawapplication.R
4 | import com.mohamedbenrejeb.drawapplication.models.Drawing
5 | import com.mohamedbenrejeb.drawapplication.models.DrawingType
6 | import com.mohamedbenrejeb.drawapplication.models.GoToType
7 | import com.mohamedbenrejeb.drawapplication.models.TutorialPoint
8 |
9 | object DrawingData {
10 | private val drawingList = listOf(
11 | Drawing(
12 | id = 1,
13 | text = "Number One",
14 | imageResource = R.drawable.number1,
15 | type = DrawingType.Number,
16 | tutorialPoints = listOf(
17 | TutorialPoint(41.55f, 33f, GoToType.AnimationTo),
18 | TutorialPoint(48f, 28.65f, GoToType.AnimationTo),
19 | TutorialPoint(48f, 67.93f, GoToType.AnimationTo),
20 | TutorialPoint(40.31f, 67.52f, GoToType.AnimationTo),
21 | TutorialPoint(53.31f, 67.52f, GoToType.AnimationTo)
22 | )
23 | ),
24 | Drawing(
25 | id = 2,
26 | text = "Number Two",
27 | imageResource = R.drawable.number2,
28 | type = DrawingType.Number
29 | ),
30 | Drawing(
31 | id = 3,
32 | text = "Number Three",
33 | imageResource = R.drawable.number3,
34 | type = DrawingType.Number
35 | ),
36 | Drawing(
37 | id = 4,
38 | text = "Number Four",
39 | imageResource = R.drawable.number4,
40 | type = DrawingType.Number
41 | ),
42 |
43 | Drawing(
44 | id = 5,
45 | text = "Letter A",
46 | imageResource = R.drawable.lettera,
47 | type = DrawingType.Letter
48 | ),
49 | Drawing(
50 | id = 6,
51 | text = "Letter B",
52 | imageResource = R.drawable.letterb,
53 | type = DrawingType.Letter,
54 | emptyList()
55 | ),
56 | Drawing(
57 | id = 7,
58 | text = "Letter C",
59 | imageResource = R.drawable.letterc,
60 | type = DrawingType.Letter,
61 | emptyList()
62 | ),
63 | Drawing(
64 | id = 8,
65 | text = "Letter D",
66 | imageResource = R.drawable.letterd,
67 | type = DrawingType.Letter,
68 | emptyList()
69 | ),
70 |
71 | Drawing(
72 | id = 9,
73 | text = "Leopard",
74 | imageResource = R.drawable.leopard,
75 | type = DrawingType.Animal
76 | ),
77 | Drawing(
78 | id = 10,
79 | text = "Dear",
80 | imageResource = R.drawable.deer,
81 | type = DrawingType.Animal
82 | )
83 | )
84 |
85 | fun getDrawingById(id: Int): Drawing? {
86 | return drawingList.firstOrNull { it.id == id }
87 | }
88 |
89 | fun getDrawingListByType(drawingType: DrawingType): List {
90 | return drawingList.filter { it.type == drawingType }
91 | }
92 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/font_certs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - @array/com_google_android_gms_fonts_certs_dev
5 | - @array/com_google_android_gms_fonts_certs_prod
6 |
7 |
8 | -
9 | MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
10 |
11 |
12 |
13 | -
14 | MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/adapters/SavedDrawingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.adapters
2 |
3 | import android.graphics.BitmapFactory
4 | import android.util.Log
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.recyclerview.widget.DiffUtil
9 | import androidx.recyclerview.widget.ListAdapter
10 | import androidx.recyclerview.widget.RecyclerView
11 | import com.mohamedbenrejeb.drawapplication.R
12 | import com.mohamedbenrejeb.drawapplication.databinding.DrawingItemBinding
13 | import com.mohamedbenrejeb.drawapplication.models.SavedDrawing
14 |
15 | class SavedDrawingAdapter(
16 | private val onDrawingSingleClick: (savedDrawing: SavedDrawing) -> Unit,
17 | private val onDrawingLongClick: (savedDrawing: SavedDrawing) -> Unit
18 | ): ListAdapter(DiffCallback()) {
19 |
20 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SavedDrawingVH {
21 | val layoutInflater = LayoutInflater.from(parent.context)
22 | val binding = DrawingItemBinding.inflate(layoutInflater, parent, false)
23 | return SavedDrawingVH(binding)
24 | }
25 |
26 | override fun onBindViewHolder(holder: SavedDrawingVH, position: Int) {
27 | holder.bind(position)
28 | }
29 |
30 | override fun onBindViewHolder(
31 | holder: SavedDrawingVH,
32 | position: Int,
33 | payloads: MutableList
34 | ) {
35 | if (payloads.isEmpty()) {
36 | super.onBindViewHolder(holder, position, payloads)
37 | } else {
38 | when(payloads[0]) {
39 | UPDATE_SELECTION -> holder.update(position)
40 | }
41 | }
42 | }
43 |
44 | private class DiffCallback: DiffUtil.ItemCallback() {
45 | override fun areItemsTheSame(oldItem: SavedDrawing, newItem: SavedDrawing): Boolean {
46 | return oldItem.id == newItem.id
47 | }
48 |
49 | override fun areContentsTheSame(oldItem: SavedDrawing, newItem: SavedDrawing): Boolean {
50 | return oldItem == newItem
51 | }
52 |
53 | override fun getChangePayload(oldItem: SavedDrawing, newItem: SavedDrawing): Any? {
54 | if (oldItem.isSelected != newItem.isSelected || oldItem.isSelectionActive != newItem.isSelectionActive)
55 | return UPDATE_SELECTION
56 |
57 | return super.getChangePayload(oldItem, newItem)
58 | }
59 | }
60 |
61 | inner class SavedDrawingVH(
62 | private val binding: DrawingItemBinding
63 | ): RecyclerView.ViewHolder(binding.root) {
64 | fun bind(position: Int) {
65 | val savedDrawing = getItem(position)
66 |
67 | binding.cardView.setOnClickListener {
68 | onDrawingSingleClick(savedDrawing)
69 | }
70 |
71 | binding.cardView.setOnLongClickListener {
72 | onDrawingLongClick(savedDrawing)
73 | true
74 | }
75 |
76 | val bitmap = BitmapFactory.decodeFile(savedDrawing.file.absolutePath)
77 | binding.drawingIv.setImageBitmap(bitmap)
78 | }
79 |
80 | fun update(position: Int) {
81 | val savedDrawing = getItem(position)
82 |
83 | Log.d("adapter", "update $position")
84 |
85 | binding.radioBtn.setImageResource(
86 | if (savedDrawing.isSelected) R.drawable.ic_radio_checked
87 | else R.drawable.ic_radio_unchecked
88 | )
89 |
90 | binding.radioBtn.visibility =
91 | if (savedDrawing.isSelectionActive)
92 | View.VISIBLE
93 | else
94 | View.GONE
95 | }
96 | }
97 |
98 | companion object {
99 | private const val UPDATE_SELECTION = 1
100 | }
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
17 |
22 |
30 |
31 |
36 |
44 |
52 |
53 |
58 |
66 |
69 |
77 |
78 |
83 |
91 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_choose_drawing.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
22 |
23 |
33 |
34 |
46 |
47 |
59 |
60 |
69 |
70 |
76 |
77 |
83 |
84 |
85 |
86 |
87 |
88 |
104 |
105 |
116 |
117 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
21 |
33 |
34 |
46 |
47 |
60 |
61 |
74 |
75 |
88 |
89 |
102 |
103 |
116 |
117 |
130 |
131 |
144 |
145 |
158 |
159 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_saved_drawings.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
22 |
23 |
33 |
34 |
46 |
47 |
60 |
61 |
73 |
74 |
84 |
85 |
96 |
97 |
103 |
104 |
110 |
111 |
112 |
113 |
114 |
115 |
131 |
132 |
147 |
148 |
157 |
158 |
173 |
180 |
181 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/ui/SavedDrawingsFragment.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.ui
2 |
3 | import android.graphics.BitmapFactory
4 | import android.os.Build
5 | import android.os.Bundle
6 | import android.os.Handler
7 | import android.os.Looper
8 | import android.util.Log
9 | import androidx.fragment.app.Fragment
10 | import android.view.LayoutInflater
11 | import android.view.View
12 | import android.view.ViewGroup
13 | import androidx.activity.OnBackPressedCallback
14 | import androidx.navigation.fragment.findNavController
15 | import com.mohamedbenrejeb.drawapplication.R
16 | import com.mohamedbenrejeb.drawapplication.adapters.SavedDrawingAdapter
17 | import com.mohamedbenrejeb.drawapplication.databinding.FragmentSavedDrawingsBinding
18 | import com.mohamedbenrejeb.drawapplication.models.SavedDrawing
19 | import com.mohamedbenrejeb.drawapplication.utils.drawingsDir
20 | import java.io.File
21 |
22 | class SavedDrawingsFragment : Fragment() {
23 | private var _binding: FragmentSavedDrawingsBinding? = null
24 | private val binding get() = _binding!!
25 |
26 | private var openedDrawingFileAbsolutePath: String? = null
27 | private var isDeletionActionBarActive: Boolean = false
28 |
29 | private val selectedSavedDrawingsToDelete = ArrayList()
30 |
31 | override fun onCreateView(
32 | inflater: LayoutInflater, container: ViewGroup?,
33 | savedInstanceState: Bundle?
34 | ): View {
35 | _binding = FragmentSavedDrawingsBinding.inflate(inflater, container, false)
36 | return binding.root
37 | }
38 |
39 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
40 | super.onViewCreated(view, savedInstanceState)
41 |
42 | val savedDrawingsDir = File(drawingsDir)
43 |
44 | val savedDrawings = savedDrawingsDir.listFiles()?.reversed()?.map { file ->
45 | SavedDrawing(file = file)
46 | }
47 | val savedDrawingAdapter = SavedDrawingAdapter(
48 | { savedDrawing ->
49 | if (isDeletionActionBarActive) {
50 | selectSavedDrawingToDelete(savedDrawing)
51 | } else {
52 | openedDrawingFileAbsolutePath = savedDrawing.file.absolutePath
53 | setDrawingBitmap(file = savedDrawing.file)
54 | openDrawingOverlay()
55 | }
56 | },
57 | { savedDrawing ->
58 | enableDeletionAppBar()
59 | selectSavedDrawingToDelete(savedDrawing)
60 | }
61 | )
62 |
63 | binding.savedDrawingsRv.let {
64 | it.setHasFixedSize(true)
65 | it.adapter = savedDrawingAdapter
66 | }
67 |
68 | savedDrawingAdapter.submitList(savedDrawings)
69 |
70 | binding.noSavedDrawingsTv.visibility =
71 | if (savedDrawings.isNullOrEmpty()) View.VISIBLE
72 | else View.GONE
73 |
74 | binding.backBtn.setOnClickListener {
75 | findNavController().popBackStack()
76 | }
77 |
78 | binding.overlayView.setOnClickListener {
79 | closeDrawingOverlay()
80 | }
81 |
82 | binding.drawingCv.setOnClickListener { }
83 |
84 | binding.cancelBtn.setOnClickListener {
85 | selectedSavedDrawingsToDelete.clear()
86 | disableDeletionAppBar()
87 | disableDeletionRadio()
88 | }
89 |
90 | binding.deleteBtn.setOnClickListener {
91 | deleteSelectedDrawings()
92 | }
93 |
94 | binding.editBtn.setOnClickListener {
95 | openedDrawingFileAbsolutePath?.let { path ->
96 | val fileName = File(path).name.split(".").first()
97 | val words = fileName.split("-")
98 | val drawingId = words.last().toInt()
99 |
100 | findNavController().navigate(
101 | SavedDrawingsFragmentDirections.actionSavedDrawingsFragmentToDrawingFragment(
102 | drawingId = drawingId,
103 | filePath = path
104 | )
105 | )
106 | }
107 | }
108 |
109 | requireActivity()
110 | .onBackPressedDispatcher
111 | .addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
112 | override fun handleOnBackPressed() {
113 | // Do custom work here
114 | if (isDeletionActionBarActive) {
115 | isEnabled = false
116 | disableDeletionAppBar()
117 | disableDeletionRadio()
118 | }
119 | // if you want onBackPressed() to be called as normal afterwards
120 | if (isEnabled) {
121 | isEnabled = false
122 | requireActivity().onBackPressed()
123 | }
124 | }
125 | }
126 | )
127 | }
128 |
129 | private fun setDrawingBitmap(file: File) {
130 | val bitmap = BitmapFactory.decodeFile(file.absolutePath)
131 | binding.drawingIv.setImageBitmap(bitmap)
132 | }
133 |
134 | private fun openDrawingOverlay() {
135 | binding.overlayView.alpha = 0f
136 | binding.overlayView.visibility = View.VISIBLE
137 |
138 | binding.overlayView
139 | .animate()
140 | .alpha(0.4f)
141 | .setDuration(ANIMATION_DURATION)
142 | .start()
143 |
144 | binding.drawingCv.alpha = 0f
145 | binding.drawingCv.visibility = View.VISIBLE
146 |
147 | binding.drawingCv
148 | .animate()
149 | .scaleX(1f)
150 | .scaleY(1f)
151 | .alpha(1f)
152 | .setDuration(ANIMATION_DURATION)
153 | .start()
154 | }
155 |
156 | private fun closeDrawingOverlay() {
157 | openedDrawingFileAbsolutePath = null
158 |
159 | binding.overlayView.visibility = View.GONE
160 |
161 | binding.drawingCv
162 | .animate()
163 | .scaleX(ANIMATION_SCALE)
164 | .scaleY(ANIMATION_SCALE)
165 | .alpha(0f)
166 | .setDuration(ANIMATION_DURATION)
167 | .start()
168 |
169 | Handler(Looper.getMainLooper()).postDelayed({
170 | binding.drawingCv.visibility = View.GONE
171 | }, ANIMATION_DURATION)
172 | }
173 |
174 | private fun enableDeletionAppBar() {
175 | if (isDeletionActionBarActive)
176 | return
177 |
178 | isDeletionActionBarActive = true
179 |
180 | binding.actionBar.setBackgroundColor(
181 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
182 | resources.getColor(R.color.red_400, null)
183 | else
184 | resources.getColor(R.color.red_400)
185 | )
186 |
187 | binding.cancelBtn.alpha = 0f
188 | binding.cancelBtn.visibility = View.VISIBLE
189 |
190 | binding.cancelBtn
191 | .animate()
192 | .alpha(1f)
193 | .setDuration(ANIMATION_DURATION)
194 | .start()
195 |
196 | binding.backBtn
197 | .animate()
198 | .alpha(0f)
199 | .setDuration(ANIMATION_DURATION)
200 | .start()
201 |
202 | Handler(Looper.getMainLooper()).postDelayed({
203 | binding.backBtn.visibility = View.GONE
204 | }, ANIMATION_DURATION)
205 |
206 | binding.appBarTitle.text = "1 Drawing Selected"
207 |
208 | binding.deleteBtn.alpha = 0f
209 | binding.deleteBtn.visibility = View.VISIBLE
210 |
211 | binding.deleteBtn
212 | .animate()
213 | .alpha(1f)
214 | .setDuration(ANIMATION_DURATION)
215 | .start()
216 |
217 | binding.searchBtn
218 | .animate()
219 | .alpha(0f)
220 | .setDuration(ANIMATION_DURATION)
221 | .start()
222 |
223 | Handler(Looper.getMainLooper()).postDelayed({
224 | binding.searchBtn.visibility = View.GONE
225 | }, ANIMATION_DURATION)
226 | }
227 |
228 | private fun disableDeletionAppBar() {
229 | if (!isDeletionActionBarActive)
230 | return
231 |
232 | isDeletionActionBarActive = false
233 |
234 | binding.actionBar.setBackgroundColor(
235 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
236 | resources.getColor(R.color.white, null)
237 | else
238 | resources.getColor(R.color.white)
239 | )
240 |
241 | binding.backBtn.alpha = 0f
242 | binding.backBtn.visibility = View.VISIBLE
243 |
244 | binding.backBtn
245 | .animate()
246 | .alpha(1f)
247 | .setDuration(ANIMATION_DURATION)
248 | .start()
249 |
250 | binding.cancelBtn
251 | .animate()
252 | .alpha(0f)
253 | .setDuration(ANIMATION_DURATION)
254 | .start()
255 |
256 | Handler(Looper.getMainLooper()).postDelayed({
257 | binding.cancelBtn.visibility = View.GONE
258 | }, ANIMATION_DURATION)
259 |
260 | binding.appBarTitle.text = "Saved Drawings"
261 |
262 | binding.searchBtn.alpha = 0f
263 | binding.searchBtn.visibility = View.VISIBLE
264 |
265 | binding.searchBtn
266 | .animate()
267 | .alpha(1f)
268 | .setDuration(ANIMATION_DURATION)
269 | .start()
270 |
271 | binding.deleteBtn
272 | .animate()
273 | .alpha(0f)
274 | .setDuration(ANIMATION_DURATION)
275 | .start()
276 |
277 | Handler(Looper.getMainLooper()).postDelayed({
278 | binding.deleteBtn.visibility = View.GONE
279 | }, ANIMATION_DURATION)
280 | }
281 |
282 | private fun disableDeletionRadio(list: ArrayList? = null) {
283 | val savedDrawings =
284 | list ?: ArrayList((binding.savedDrawingsRv.adapter as SavedDrawingAdapter).currentList)
285 |
286 | savedDrawings.forEachIndexed { index, savedDrawing ->
287 | savedDrawings[index] = savedDrawing.copy(
288 | isSelected = false,
289 | isSelectionActive = false
290 | )
291 | }
292 | (binding.savedDrawingsRv.adapter as SavedDrawingAdapter).submitList(savedDrawings)
293 |
294 | binding.noSavedDrawingsTv.visibility =
295 | if (savedDrawings.isEmpty()) View.VISIBLE
296 | else View.GONE
297 | }
298 |
299 | private fun selectSavedDrawingToDelete(savedDrawing: SavedDrawing) {
300 | if (selectedSavedDrawingsToDelete.none { it.id == savedDrawing.id }) {
301 | selectedSavedDrawingsToDelete.add(savedDrawing)
302 | } else {
303 | val savedDrawingIndex = selectedSavedDrawingsToDelete.indexOfFirst { it.id == savedDrawing.id }
304 | selectedSavedDrawingsToDelete.removeAt(savedDrawingIndex)
305 | }
306 |
307 | binding.appBarTitle.text = "${selectedSavedDrawingsToDelete.size} Drawing Selected"
308 |
309 | val savedDrawings = ArrayList((binding.savedDrawingsRv.adapter as SavedDrawingAdapter).currentList)
310 | savedDrawings.forEachIndexed { index, _ ->
311 | savedDrawings[index] = savedDrawings[index].copy(isSelectionActive = true)
312 | }
313 | val savedDrawingIndex = savedDrawings.indexOfFirst { it.id == savedDrawing.id }
314 | savedDrawings[savedDrawingIndex] =
315 | savedDrawings[savedDrawingIndex].copy(isSelected = !savedDrawings[savedDrawingIndex].isSelected)
316 | (binding.savedDrawingsRv.adapter as SavedDrawingAdapter).submitList(savedDrawings)
317 | }
318 |
319 | private fun deleteSelectedDrawings() {
320 | val savedDrawings = ArrayList((binding.savedDrawingsRv.adapter as SavedDrawingAdapter).currentList)
321 |
322 | selectedSavedDrawingsToDelete.forEach { savedDrawing ->
323 | if (savedDrawing.file.delete()) {
324 | val drawingIndex = savedDrawings.indexOfFirst { it.id == savedDrawing.id }
325 | savedDrawings.removeAt(drawingIndex)
326 | }
327 | }
328 |
329 | selectedSavedDrawingsToDelete.clear()
330 |
331 | disableDeletionAppBar()
332 | disableDeletionRadio(savedDrawings)
333 | }
334 |
335 | override fun onDestroyView() {
336 | super.onDestroyView()
337 | _binding = null
338 | }
339 |
340 | companion object {
341 | private const val ANIMATION_SCALE = 0.2f
342 | private const val ANIMATION_DURATION = 200L
343 | }
344 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_bg.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
10 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
29 |
30 |
33 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
71 |
72 |
74 |
75 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
89 |
90 |
92 |
93 |
95 |
96 |
98 |
99 |
101 |
102 |
105 |
107 |
108 |
110 |
111 |
116 |
121 |
126 |
127 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_drawing.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
22 |
23 |
33 |
34 |
45 |
46 |
58 |
59 |
70 |
71 |
77 |
78 |
84 |
85 |
86 |
87 |
88 |
89 |
99 |
100 |
108 |
109 |
117 |
118 |
131 |
132 |
143 |
144 |
155 |
156 |
168 |
169 |
177 |
178 |
190 |
191 |
203 |
204 |
213 |
214 |
220 |
221 |
222 |
223 |
234 |
235 |
238 |
239 |
249 |
250 |
258 |
259 |
269 |
270 |
271 |
272 |
292 |
293 |
303 |
304 |
313 |
314 |
323 |
324 |
333 |
334 |
340 |
341 |
351 |
352 |
368 |
369 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mohamedbenrejeb/drawapplication/ui/DrawingFragment.kt:
--------------------------------------------------------------------------------
1 | package com.mohamedbenrejeb.drawapplication.ui
2 |
3 | import android.annotation.SuppressLint
4 | import android.graphics.*
5 | import android.os.*
6 | import android.util.Log
7 | import android.view.LayoutInflater
8 | import android.view.MotionEvent
9 | import android.view.View
10 | import android.view.ViewGroup
11 | import android.widget.Button
12 | import android.widget.Toast
13 | import androidx.core.view.drawToBitmap
14 | import androidx.fragment.app.Fragment
15 | import androidx.lifecycle.lifecycleScope
16 | import androidx.navigation.fragment.findNavController
17 | import androidx.navigation.fragment.navArgs
18 | import com.mohamedbenrejeb.drawapplication.R
19 | import com.mohamedbenrejeb.drawapplication.data.DrawingData
20 | import com.mohamedbenrejeb.drawapplication.databinding.FragmentDrawingBinding
21 | import com.mohamedbenrejeb.drawapplication.models.Drawing
22 | import com.mohamedbenrejeb.drawapplication.models.GoToType
23 | import com.mohamedbenrejeb.drawapplication.models.Tool
24 | import com.mohamedbenrejeb.drawapplication.utils.drawingsDir
25 | import kotlinx.coroutines.delay
26 | import kotlinx.coroutines.launch
27 | import java.io.File
28 | import java.io.FileOutputStream
29 | import java.util.*
30 |
31 |
32 | class DrawingFragment : Fragment() {
33 |
34 | private var _binding: FragmentDrawingBinding? = null
35 | private val binding get() = _binding!!
36 |
37 | private val args by navArgs()
38 |
39 | private var selectedTool: Tool = Tool.Brush
40 | private var selectedColor: Int = R.color.red
41 | private var selectedBrushSize: Float = 20f
42 | private var selectedEraserSize: Float = 20f
43 |
44 | private val canvas = Canvas()
45 | private var bitmap: Bitmap? = null
46 | private val brushPaint = Paint().let {
47 | it.style = Paint.Style.FILL
48 | it.strokeJoin = Paint.Join.ROUND
49 | it.strokeWidth = selectedBrushSize * 2
50 | it.strokeCap = Paint.Cap.ROUND
51 | it
52 | }
53 | private val eraserPaint = Paint().let {
54 | it.style = Paint.Style.FILL
55 | it.strokeJoin = Paint.Join.ROUND
56 | it.strokeWidth = selectedEraserSize * 2
57 | it.strokeCap = Paint.Cap.ROUND
58 | it
59 | }
60 | private var lastBrushPoint: Point? = null
61 | private val bitmapList = ArrayList()
62 | private var currentBitmapIndex: Int = 0
63 |
64 | private var drawing: Drawing? = null
65 |
66 | override fun onCreateView(
67 | inflater: LayoutInflater, container: ViewGroup?,
68 | savedInstanceState: Bundle?
69 | ): View {
70 | _binding = FragmentDrawingBinding.inflate(inflater, container, false)
71 | return binding.root
72 | }
73 |
74 | @SuppressLint("ClickableViewAccessibility")
75 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
76 | super.onViewCreated(view, savedInstanceState)
77 |
78 | val isNewDrawing = args.drawingId == -1
79 | if (args.drawingId != -1) {
80 | drawing = DrawingData.getDrawingById(args.drawingId)
81 | }
82 | initDrawing(isNewDrawing)
83 |
84 | initOverlay()
85 |
86 | initUndoRedoButtons()
87 |
88 | binding.backBtn.setOnClickListener {
89 | findNavController().popBackStack()
90 | }
91 |
92 | eraserPaint.color =
93 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
94 | resources.getColor(R.color.white, null)
95 | else
96 | resources.getColor(R.color.white)
97 |
98 | initColorPicker()
99 | initToolButtons()
100 | initSlider()
101 |
102 | binding.canvasIv.setOnTouchListener { _, p1 ->
103 | if (bitmap == null) {
104 | clearBitmap()
105 | }
106 |
107 | when (selectedTool) {
108 | Tool.Brush -> {
109 | drawWithBrushAction(event = p1)
110 | }
111 | Tool.Pail -> {
112 | drawWithPailAction(event = p1)
113 | }
114 | Tool.Eraser -> {
115 | eraseAction(event = p1)
116 | }
117 | Tool.Colors -> {
118 | onSliderToolSelected(binding.brush, Tool.Brush)
119 | drawWithBrushAction(event = p1)
120 | }
121 | }
122 |
123 | binding.canvasIv.setImageBitmap(bitmap)
124 | true
125 | }
126 |
127 | binding.saveBtn.setOnClickListener {
128 | onSaveAction()
129 | }
130 |
131 | }
132 |
133 | private fun initDrawing(isNewDrawing: Boolean) {
134 | val isEdit = !isNewDrawing && args.filePath != null
135 |
136 | if (isEdit) {
137 | clearBitmap()
138 |
139 | Handler(Looper.getMainLooper()).postDelayed({
140 | canvas.drawBitmap(
141 | BitmapFactory.decodeFile(args.filePath),
142 | Matrix(),
143 | null
144 | )
145 | binding.canvasIv.setImageBitmap(bitmap)
146 | }, 100)
147 | }
148 |
149 | Log.d("isEdit", "$isEdit")
150 |
151 | binding.canvasIv.setImageBitmap(bitmap)
152 |
153 | binding.undo.isEnabled = false
154 | binding.redo.isEnabled = false
155 |
156 | if (isNewDrawing) {
157 | binding.appBarTitle.text = "New Drawing"
158 | binding.drawingIv.visibility = View.INVISIBLE
159 | } else if (isEdit && args.drawingId == 0) {
160 | binding.appBarTitle.text = "Edit Drawing"
161 | binding.drawingIv.visibility = View.INVISIBLE
162 | } else {
163 | binding.appBarTitle.text = drawing!!.text
164 | binding.drawingIv.setImageResource(drawing!!.imageResource)
165 | binding.drawingIv.visibility = View.VISIBLE
166 | }
167 | }
168 |
169 | @SuppressLint("ClickableViewAccessibility")
170 | private fun initOverlay() {
171 | if (drawing == null || drawing?.tutorialPoints?.isEmpty() == true)
172 | return
173 |
174 | binding.skipTutorialTv.visibility = View.VISIBLE
175 |
176 | binding.skipTutorialTv.setOnClickListener {
177 | binding.hand.visibility = View.GONE
178 | binding.overlayIv.visibility = View.GONE
179 | binding.skipTutorialTv.visibility = View.GONE
180 | }
181 |
182 | binding.overlayIv.post {
183 | binding.overlayIv.setOnTouchListener { view, motionEvent ->
184 | if (motionEvent.action == MotionEvent.ACTION_UP) {
185 | Log.d("clickPoint", "x: ${motionEvent.x}, y: ${motionEvent.y}")
186 | val drawAryaX = binding.drawingCv.x
187 | val drawAryaY = binding.drawingCv.y
188 | val drawAryaWidth = binding.drawingCv.width
189 | val drawAryaHeight = binding.drawingCv.height
190 |
191 | val clickX = motionEvent.x - drawAryaX
192 | val clickY = motionEvent.y - drawAryaY
193 |
194 |
195 | Log.d("clickPoint", "clickX: $clickX, clickY: $clickY")
196 |
197 | val clickXPercent = (clickX * 100) / drawAryaWidth
198 | val clickYPercent = (clickY * 100) / drawAryaHeight
199 | Log.d("clickPoint", "xPercent: $clickXPercent, yPercent: $clickYPercent")
200 | }
201 | true
202 | }
203 |
204 | var overlayBitmap = Bitmap.createBitmap(
205 | binding.overlayIv.width,
206 | binding.overlayIv.height,
207 | Bitmap.Config.ARGB_8888
208 | )
209 |
210 | val overlayCanvas = Canvas(overlayBitmap)
211 |
212 | val overlayPaint = Paint().let {
213 | it.style = Paint.Style.FILL
214 | it.strokeJoin = Paint.Join.ROUND
215 | it.strokeWidth = 100f
216 | it.strokeCap = Paint.Cap.ROUND
217 | it.color = Color.TRANSPARENT
218 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
219 | it.blendMode = BlendMode.CLEAR
220 | }
221 | it
222 | }
223 |
224 | binding.drawingCv.post {
225 | val firstX = ((drawing!!.tutorialPoints.first().xPercent * binding.drawingCv.width) / 100) + binding.drawingCv.x
226 | val firstY = ((drawing!!.tutorialPoints.first().yPercent * binding.drawingCv.height) / 100) + binding.drawingCv.y
227 | overlayCanvas.setBitmap(overlayBitmap)
228 | overlayCanvas.drawColor(Color.argb(60, 0, 0, 0))
229 | overlayCanvas.drawCircle(
230 | firstX,
231 | firstY,
232 | 50f,
233 | overlayPaint
234 | )
235 |
236 | binding.hand.post {
237 | binding.hand.x = firstX - binding.hand.width - 50
238 | binding.hand.y = firstY - 60
239 | }
240 |
241 | binding.hand.visibility = View.VISIBLE
242 | }
243 |
244 | var startX = -1f
245 | var startY = -1f
246 |
247 | val duration = 500L
248 |
249 | binding.overlayIv.setImageBitmap(overlayBitmap)
250 |
251 | val step = 5
252 | val durationStep = ((100 - step) / step) + 1
253 |
254 | lifecycleScope.launch {
255 | drawing!!.tutorialPoints.forEach { point ->
256 | val stopX = ((point.xPercent * binding.drawingCv.width) / 100) + binding.drawingCv.x
257 | val stopY = ((point.yPercent * binding.drawingCv.height) / 100) + binding.drawingCv.y
258 |
259 | if (startX == -1f || startY == -1f) {
260 | startX = stopX
261 | startY = stopY
262 | return@forEach
263 | }
264 |
265 | if (point.goToType == GoToType.JumpTo) {
266 | delay(duration / durationStep)
267 | overlayBitmap = Bitmap.createBitmap(
268 | binding.overlayIv.width,
269 | binding.overlayIv.height,
270 | Bitmap.Config.ARGB_8888
271 | )
272 | overlayCanvas.setBitmap(overlayBitmap)
273 | overlayCanvas.drawColor(Color.argb(60, 0, 0, 0))
274 | overlayCanvas.drawCircle(
275 | stopX,
276 | stopY,
277 | 50f,
278 | overlayPaint
279 | )
280 | binding.overlayIv.setImageBitmap(overlayBitmap)
281 |
282 | binding.hand.post {
283 | binding.hand.x = stopX - binding.hand.width - 50
284 | binding.hand.y = stopY - 60
285 | }
286 |
287 | startX = stopX
288 | startY = stopY
289 |
290 | return@forEach
291 | }
292 |
293 | for (i in step..100 step step) {
294 | delay(duration / durationStep)
295 | val progressX = ((stopX - startX) * i) / 100f
296 | val currentX = startX + progressX
297 |
298 | val progressY = ((stopY - startY) * i) / 100f
299 | val currentY = startY + progressY
300 |
301 | overlayBitmap = Bitmap.createBitmap(
302 | binding.overlayIv.width,
303 | binding.overlayIv.height,
304 | Bitmap.Config.ARGB_8888
305 | )
306 | overlayCanvas.setBitmap(overlayBitmap)
307 | overlayCanvas.drawColor(Color.argb(60, 0, 0, 0))
308 | overlayCanvas.drawCircle(
309 | currentX,
310 | currentY,
311 | 50f,
312 | overlayPaint
313 | )
314 | binding.overlayIv.setImageBitmap(overlayBitmap)
315 |
316 | binding.hand.post {
317 | binding.hand.x = currentX - binding.hand.width - 50
318 | binding.hand.y = currentY - 60
319 | }
320 | }
321 |
322 | startX = stopX
323 | startY = stopY
324 | }
325 | }
326 |
327 | }
328 | }
329 |
330 | private fun initUndoRedoButtons() {
331 | binding.undo.setOnClickListener {
332 | if (currentBitmapIndex > 0) {
333 | currentBitmapIndex--
334 | resetBitmap()
335 | updateUndoRedoButtonsState()
336 | }
337 | }
338 | binding.redo.setOnClickListener {
339 | if (currentBitmapIndex < bitmapList.lastIndex) {
340 | currentBitmapIndex++
341 | resetBitmap()
342 | updateUndoRedoButtonsState()
343 | }
344 | }
345 | }
346 |
347 | private fun updateUndoRedoButtonsState() {
348 | binding.undo.isEnabled = currentBitmapIndex > 0
349 | binding.redo.isEnabled = currentBitmapIndex < bitmapList.lastIndex
350 | }
351 |
352 | private fun initColorPicker() {
353 | closeColorPicker()
354 |
355 | val colorButtons = listOf(
356 | binding.colorOne,
357 | binding.colorTwo,
358 | binding.colorThree,
359 | binding.colorFour
360 | )
361 |
362 | val colors = listOf(
363 | R.color.red,
364 | R.color.green,
365 | R.color.blue,
366 | R.color.orange
367 | )
368 |
369 | val selectedColorIndex = colors.indexOf(selectedColor)
370 | onColorSelectedAction(colorButtons[selectedColorIndex], selectedColor)
371 |
372 | colorButtons.zip(colors).forEach { buttonColor ->
373 | buttonColor.first.setOnClickListener {
374 | onColorSelectedAction(buttonColor.first, buttonColor.second)
375 | }
376 | }
377 | }
378 |
379 | private fun onColorSelectedAction(button: Button, color: Int) {
380 | selectColor(color)
381 | resetColorButtons()
382 | button
383 | .animate()
384 | .scaleX(ANIMATION_SCALE)
385 | .scaleY(ANIMATION_SCALE)
386 | .setDuration(ANIMATION_DURATION)
387 | .start()
388 | }
389 |
390 | private fun selectColor(color: Int) {
391 | selectedColor = color
392 |
393 | brushPaint.color =
394 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
395 | resources.getColor(color, null)
396 | else
397 | resources.getColor(color)
398 |
399 | binding.selectedColorDot.setBackgroundColor(
400 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
401 | resources.getColor(color, null)
402 | } else {
403 | resources.getColor(color)
404 | }
405 | )
406 | }
407 |
408 | private fun resetColorButtons() {
409 | val buttons = listOf(
410 | binding.colorOne,
411 | binding.colorTwo,
412 | binding.colorThree,
413 | binding.colorFour
414 | )
415 |
416 | buttons.forEach { button ->
417 | if (button.scaleX == 1f) return@forEach
418 |
419 | button
420 | .animate()
421 | .scaleX(1f)
422 | .scaleY(1f)
423 | .setDuration(ANIMATION_DURATION)
424 | .start()
425 | }
426 | }
427 |
428 | private fun initToolButtons() {
429 | onToolSelected(binding.brush, Tool.Brush)
430 |
431 | val toolButtons = listOf(
432 | binding.brush,
433 | binding.pail,
434 | binding.eraser,
435 | binding.colors
436 | )
437 |
438 | val tools = listOf(
439 | Tool.Brush,
440 | Tool.Pail,
441 | Tool.Eraser,
442 | Tool.Colors
443 | )
444 |
445 | toolButtons.zip(tools).forEach { buttonTool ->
446 | buttonTool.first.setOnClickListener {
447 | when (buttonTool.second) {
448 | Tool.Colors ->
449 | onColorsToolSelected(buttonTool.first, buttonTool.second)
450 | Tool.Brush, Tool.Eraser ->
451 | onSliderToolSelected(buttonTool.first, buttonTool.second)
452 | else ->
453 | onToolSelected(buttonTool.first, buttonTool.second)
454 | }
455 | }
456 | }
457 | }
458 |
459 | private fun onSliderToolSelected(view: View, tool: Tool) {
460 | onToolSelected(view, tool)
461 | openSlider()
462 | }
463 |
464 | private fun onColorsToolSelected(view: View, tool: Tool) {
465 | onToolSelected(view, tool)
466 | openColorPicker()
467 | }
468 |
469 | private fun onToolSelected(view: View, tool: Tool) {
470 | selectedTool = tool
471 | resetToolButtons()
472 | view.isActivated = true
473 | closeColorPicker()
474 | closeSlider()
475 | updateSliderState()
476 | }
477 |
478 | private fun resetToolButtons() {
479 | val buttons = listOf(
480 | binding.brush,
481 | binding.pail,
482 | binding.eraser,
483 | binding.colors
484 | )
485 |
486 | buttons.forEach { button ->
487 | button.isActivated = false
488 | }
489 | }
490 |
491 | private fun initSlider() {
492 | updateSliderState()
493 |
494 | binding.slider.addOnChangeListener { _, value, _ ->
495 | if (selectedTool == Tool.Brush) {
496 | selectedBrushSize = value
497 | brushPaint.strokeWidth = selectedBrushSize * 2
498 | } else if (selectedTool == Tool.Eraser) {
499 | selectedEraserSize = value
500 | eraserPaint.strokeWidth = selectedEraserSize * 2
501 | }
502 | }
503 | }
504 |
505 | private fun updateSliderState() {
506 | binding.slider.visibility =
507 | when (selectedTool) {
508 | Tool.Brush -> {
509 | binding.slider.value = selectedBrushSize
510 | View.VISIBLE
511 | }
512 | Tool.Eraser -> {
513 | binding.slider.value = selectedEraserSize
514 | View.VISIBLE
515 | }
516 | else -> {
517 | View.INVISIBLE
518 | }
519 | }
520 | }
521 |
522 | private fun clearBitmap() {
523 | binding.canvasIv.post {
524 | editBitmap(
525 | Bitmap.createBitmap(
526 | binding.canvasIv.width,
527 | binding.canvasIv.height,
528 | Bitmap.Config.ARGB_8888
529 | )
530 | )
531 | }
532 | }
533 |
534 | private fun editBitmap(newBitmap: Bitmap) {
535 | bitmap = newBitmap
536 | bitmapList.add(newBitmap.copy(newBitmap.config, newBitmap.isMutable))
537 | canvas.setBitmap(bitmap)
538 | }
539 |
540 | private fun resetBitmap() {
541 | bitmap = Bitmap.createBitmap(
542 | binding.canvasIv.width,
543 | binding.canvasIv.height,
544 | Bitmap.Config.ARGB_8888
545 | )
546 | canvas.setBitmap(bitmap)
547 | canvas.drawBitmap(bitmapList[currentBitmapIndex], Matrix(Matrix()), null)
548 | binding.canvasIv.setImageBitmap(bitmap)
549 | }
550 |
551 | private fun addCurrentBitmapToHistory() {
552 | bitmap?.let { bitmap ->
553 | if (currentBitmapIndex != bitmapList.lastIndex) {
554 | for (i in bitmapList.lastIndex downTo (currentBitmapIndex + 1)) {
555 | Log.d("indexes", "remove at $i")
556 | bitmapList.removeAt(i)
557 | }
558 | }
559 |
560 | bitmapList.add(
561 | binding.canvasIv.drawToBitmap(
562 | Bitmap.Config.ARGB_8888
563 | )
564 | )
565 | currentBitmapIndex++
566 | updateUndoRedoButtonsState()
567 | }
568 | }
569 |
570 | private fun drawWithBrushAction(event: MotionEvent?) {
571 | when (event?.action) {
572 | MotionEvent.ACTION_DOWN -> {
573 | canvas.drawCircle(event.x, event.y, selectedBrushSize, brushPaint)
574 | lastBrushPoint = Point(event.x.toInt(), event.y.toInt())
575 | }
576 | MotionEvent.ACTION_MOVE -> {
577 | canvas.drawLine(
578 | lastBrushPoint!!.x.toFloat(),
579 | lastBrushPoint!!.y.toFloat(),
580 | event.x,
581 | event.y,
582 | brushPaint
583 | )
584 | lastBrushPoint = Point(event.x.toInt(), event.y.toInt())
585 | }
586 | MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
587 | addCurrentBitmapToHistory()
588 | }
589 | }
590 | }
591 |
592 | private fun drawWithPailAction(event: MotionEvent?) {
593 | when (event?.action) {
594 | MotionEvent.ACTION_DOWN -> {
595 |
596 | }
597 | MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
598 | canvas.drawColor(brushPaint.color)
599 | addCurrentBitmapToHistory()
600 | }
601 | }
602 | }
603 |
604 | private fun eraseAction(event: MotionEvent?) {
605 | when (event?.action) {
606 | MotionEvent.ACTION_DOWN -> {
607 | canvas.drawCircle(event.x, event.y, selectedEraserSize, eraserPaint)
608 | lastBrushPoint = Point(event.x.toInt(), event.y.toInt())
609 | }
610 | MotionEvent.ACTION_MOVE -> {
611 | canvas.drawLine(
612 | lastBrushPoint!!.x.toFloat(),
613 | lastBrushPoint!!.y.toFloat(),
614 | event.x,
615 | event.y,
616 | eraserPaint
617 | )
618 | lastBrushPoint = Point(event.x.toInt(), event.y.toInt())
619 | }
620 | MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
621 | addCurrentBitmapToHistory()
622 | }
623 | }
624 | }
625 |
626 | private fun openColorPicker() {
627 | val colorButtons = listOf(
628 | binding.colorOne,
629 | binding.colorTwo,
630 | binding.colorThree,
631 | binding.colorFour
632 | )
633 | colorButtons.forEach { button ->
634 | button.visibility = View.VISIBLE
635 | }
636 | }
637 |
638 | private fun closeColorPicker() {
639 | val colorButtons = listOf(
640 | binding.colorOne,
641 | binding.colorTwo,
642 | binding.colorThree,
643 | binding.colorFour
644 | )
645 | colorButtons.forEach { button ->
646 | button.visibility = View.INVISIBLE
647 | }
648 | }
649 |
650 | private fun openSlider() {
651 | binding.slider.visibility = View.VISIBLE
652 | }
653 |
654 | private fun closeSlider() {
655 | binding.slider.visibility = View.INVISIBLE
656 | }
657 |
658 | private fun onSaveAction() {
659 | val backgroundBitmap = Bitmap.createBitmap(binding.canvasIv.width, binding.canvasIv.height, Bitmap.Config.ARGB_8888)
660 | val drawingBitmap = Bitmap.createBitmap(binding.canvasIv.width, binding.canvasIv.height, Bitmap.Config.ARGB_8888)
661 | val outlineBimap = Bitmap.createBitmap(binding.canvasIv.width, binding.canvasIv.height, Bitmap.Config.ARGB_8888)
662 |
663 | val backgroundCanvas = Canvas(backgroundBitmap)
664 | val drawingCanvas = Canvas(drawingBitmap)
665 | val outlineCanvas = Canvas(outlineBimap)
666 |
667 | backgroundCanvas.setBitmap(backgroundBitmap)
668 | drawingCanvas.setBitmap(drawingBitmap)
669 | outlineCanvas.setBitmap(outlineBimap)
670 |
671 | binding.emptyBoardIv.draw(backgroundCanvas)
672 | binding.canvasIv.draw(drawingCanvas)
673 | binding.drawingIv.draw(outlineCanvas)
674 |
675 | val saveBitmap = Bitmap.createBitmap(binding.canvasIv.width, binding.canvasIv.height, Bitmap.Config.ARGB_8888)
676 | val saveCanvas = Canvas(saveBitmap)
677 |
678 | saveCanvas.drawBitmap(backgroundBitmap, Matrix(), null)
679 | saveCanvas.drawBitmap(drawingBitmap, Matrix(), null)
680 | saveCanvas.drawBitmap(outlineBimap, Matrix(), null)
681 |
682 | val filePath = drawingsDir
683 | File(filePath).mkdirs()
684 | val file =
685 | if (args.filePath != null)
686 | File(args.filePath!!)
687 | else if (args.drawingId == -1)
688 | File(filePath, "${UUID.randomUUID()}-0.jpg")
689 | else
690 | File(filePath, "${UUID.randomUUID()}-${args.drawingId}.jpg")
691 |
692 | try {
693 | val isSaved = saveBitmap.compress(Bitmap.CompressFormat.JPEG, 100, FileOutputStream(file))
694 | if (isSaved) {
695 | Toast.makeText(requireContext(), "Image saved successfully!", Toast.LENGTH_SHORT)
696 | .show()
697 | }
698 | } catch (e: Exception) {
699 | e.printStackTrace()
700 | }
701 | }
702 |
703 | override fun onDestroyView() {
704 | super.onDestroyView()
705 | _binding = null
706 | }
707 |
708 | companion object {
709 | private const val ANIMATION_DURATION: Long = 200
710 | private const val ANIMATION_SCALE = 1.3f
711 | }
712 | }
--------------------------------------------------------------------------------