├── .gitignore
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── gradle.xml
├── misc.xml
├── sonarlint
│ └── issuestore
│ │ ├── 0
│ │ ├── a
│ │ │ └── 0a347c2b22731d68c28b734fdee4846dbce801ac
│ │ └── e
│ │ │ └── 0e7e206f2ef4ca898cc1c6f9c43509c2fda87a46
│ │ ├── 1
│ │ ├── 5
│ │ │ └── 15f87573f2562809f55b4301227653a12fbd9ab9
│ │ └── c
│ │ │ └── 1c207e90608557c98eca3b54458ee0eada3295f7
│ │ ├── 3
│ │ └── b
│ │ │ └── 3b7c6a8d1a8fd5e96594f7d90e01a4c3359f1642
│ │ ├── 4
│ │ ├── 2
│ │ │ └── 42074a9a394d39e1bd035d39c39e9fc65da3ceca
│ │ ├── 4
│ │ │ └── 4441730111cf49bcd402fa82db8e034f9671b584
│ │ └── e
│ │ │ ├── 4e42000e473f66e96cc60d6eeaadcf9bfda3041e
│ │ │ └── 4e779135634d0188e6f5f509ab7c317d5416da3a
│ │ ├── 5
│ │ └── 9
│ │ │ └── 59d2e248db7a62e8b2be3c9c58faa64c30785f4d
│ │ ├── 7
│ │ ├── 0
│ │ │ └── 70dc84dcb33fd7fb7f4419f7bb1ef84b246ea625
│ │ └── 3
│ │ │ └── 73ccd3dd938dc14c03197221d9f58927d1a489fa
│ │ ├── 8
│ │ ├── 5
│ │ │ └── 85eb642c59a8bc10cf60b7f81ae9f3c7be070e07
│ │ ├── 9
│ │ │ ├── 89443d9b758e1f297c019b03131d50d594a36c6b
│ │ │ └── 894a81455c4de60975f3026a8743156115a134d3
│ │ ├── c
│ │ │ └── 8c55c3ccc257e5907959013f99656e4c8ec3903e
│ │ └── d
│ │ │ └── 8db0821229949e8f2a589d10bc7ea9a135da6c8e
│ │ ├── 9
│ │ └── d
│ │ │ └── 9d1b1002fe90e56522647df3334da3b1b7bd68e3
│ │ ├── a
│ │ └── a
│ │ │ └── aa8bff76e11859170c4a52d2983d4f1a4fad9417
│ │ ├── b
│ │ └── 6
│ │ │ └── b697311ca27d5c6645f75c4077cf3eb564cf6ed1
│ │ ├── c
│ │ ├── 1
│ │ │ └── c127b77fc102fc3fda632a76ddc99d4ccf7a10d5
│ │ ├── 3
│ │ │ └── c33522a5f4d7f9914d0112918f975f2d45b30441
│ │ ├── 5
│ │ │ └── c577566c98eb6117440fee6c0f306f715d60d91e
│ │ └── a
│ │ │ └── ca9e34c62628440135fb142a1fba5f028ec2359b
│ │ ├── d
│ │ └── 8
│ │ │ └── d883e197a3fa06a093ef00c6b5d9a5ef7510e3e0
│ │ ├── e
│ │ └── 1
│ │ │ └── e143825a3c2a2113b7aa40f2393d0114f79190e9
│ │ ├── f
│ │ ├── 0
│ │ │ └── f07866736216be0ee2aba49e392191aeae700a35
│ │ ├── 4
│ │ │ └── f4a01d6a4fcb971362ec00a83903fd3902f52164
│ │ ├── c
│ │ │ ├── fc90888977fdecd39953e843c6e3c6c7fd123ed2
│ │ │ └── fca1737f836c684f59ec001226079c63e8ebd8ce
│ │ └── f
│ │ │ └── ff8ad05914c6aa4ce7fb60c92125f63a8a5ef08b
│ │ └── index.pb
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── notesapp
│ │ ├── ExampleInstrumentedTest.kt
│ │ ├── HiltTestRunner.kt
│ │ ├── di
│ │ └── TestAppModule.kt
│ │ └── feature_note
│ │ └── presentation
│ │ └── notes
│ │ └── NoteScreenTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── notesapp
│ │ │ ├── NotesApp.kt
│ │ │ ├── di
│ │ │ └── AppModule.kt
│ │ │ ├── feature_note
│ │ │ ├── data
│ │ │ │ ├── data_source
│ │ │ │ │ ├── NoteDao.kt
│ │ │ │ │ └── NoteDataBase.kt
│ │ │ │ └── repository
│ │ │ │ │ └── NoteRepositoryImp.kt
│ │ │ ├── domain
│ │ │ │ ├── model
│ │ │ │ │ └── Note.kt
│ │ │ │ ├── repository
│ │ │ │ │ └── NoteRepository.kt
│ │ │ │ ├── use_case
│ │ │ │ │ ├── AddNotes.kt
│ │ │ │ │ ├── DeleteNotes.kt
│ │ │ │ │ ├── GetNote.kt
│ │ │ │ │ ├── GetNotes.kt
│ │ │ │ │ └── NoteUseCases.kt
│ │ │ │ └── util
│ │ │ │ │ ├── NoteOrder.kt
│ │ │ │ │ └── OrderType.kt
│ │ │ └── presentation
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── add_edit_notes
│ │ │ │ ├── AddEditNoteEvent.kt
│ │ │ │ ├── AddEditNoteScreen.kt
│ │ │ │ ├── AddEditNoteViewModel.kt
│ │ │ │ ├── NoteTextFieldState.kt
│ │ │ │ └── components
│ │ │ │ │ └── TransParentHintTextField.kt
│ │ │ │ ├── notes
│ │ │ │ ├── NoteScreen.kt
│ │ │ │ ├── NotesEvent.kt
│ │ │ │ ├── NotesState.kt
│ │ │ │ ├── NotesViewModel.kt
│ │ │ │ └── components
│ │ │ │ │ ├── DefaultRadioButton.kt
│ │ │ │ │ ├── NoteItem.kt
│ │ │ │ │ └── OrderSection.kt
│ │ │ │ └── util
│ │ │ │ └── Screen.kt
│ │ │ └── ui
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Shape.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.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
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── notesapp
│ ├── ExampleUnitTest.kt
│ └── feature_note
│ ├── data
│ └── repository
│ │ └── FakeNoteRepository.kt
│ └── domain
│ └── use_case
│ └── GetNotesTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.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 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | xmlns:android
18 |
19 | ^$
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | xmlns:.*
29 |
30 | ^$
31 |
32 |
33 | BY_NAME
34 |
35 |
36 |
37 |
38 |
39 |
40 | .*:id
41 |
42 | http://schemas.android.com/apk/res/android
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | .*:name
52 |
53 | http://schemas.android.com/apk/res/android
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | name
63 |
64 | ^$
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | style
74 |
75 | ^$
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | .*
85 |
86 | ^$
87 |
88 |
89 | BY_NAME
90 |
91 |
92 |
93 |
94 |
95 |
96 | .*
97 |
98 | http://schemas.android.com/apk/res/android
99 |
100 |
101 | ANDROID_ATTRIBUTE_ORDER
102 |
103 |
104 |
105 |
106 |
107 |
108 | .*
109 |
110 | .*
111 |
112 |
113 | BY_NAME
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/0/a/0a347c2b22731d68c28b734fdee4846dbce801ac:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/0/a/0a347c2b22731d68c28b734fdee4846dbce801ac
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/0/e/0e7e206f2ef4ca898cc1c6f9c43509c2fda87a46:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/0/e/0e7e206f2ef4ca898cc1c6f9c43509c2fda87a46
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/1/5/15f87573f2562809f55b4301227653a12fbd9ab9:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/1/5/15f87573f2562809f55b4301227653a12fbd9ab9
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/1/c/1c207e90608557c98eca3b54458ee0eada3295f7:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/1/c/1c207e90608557c98eca3b54458ee0eada3295f7
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/3/b/3b7c6a8d1a8fd5e96594f7d90e01a4c3359f1642:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/3/b/3b7c6a8d1a8fd5e96594f7d90e01a4c3359f1642
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/4/2/42074a9a394d39e1bd035d39c39e9fc65da3ceca:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/4/2/42074a9a394d39e1bd035d39c39e9fc65da3ceca
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/4/4/4441730111cf49bcd402fa82db8e034f9671b584:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/4/4/4441730111cf49bcd402fa82db8e034f9671b584
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/4/e/4e42000e473f66e96cc60d6eeaadcf9bfda3041e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/4/e/4e42000e473f66e96cc60d6eeaadcf9bfda3041e
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/4/e/4e779135634d0188e6f5f509ab7c317d5416da3a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/4/e/4e779135634d0188e6f5f509ab7c317d5416da3a
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/5/9/59d2e248db7a62e8b2be3c9c58faa64c30785f4d:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/5/9/59d2e248db7a62e8b2be3c9c58faa64c30785f4d
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/7/0/70dc84dcb33fd7fb7f4419f7bb1ef84b246ea625:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/7/0/70dc84dcb33fd7fb7f4419f7bb1ef84b246ea625
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/7/3/73ccd3dd938dc14c03197221d9f58927d1a489fa:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/7/3/73ccd3dd938dc14c03197221d9f58927d1a489fa
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/8/5/85eb642c59a8bc10cf60b7f81ae9f3c7be070e07:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/8/5/85eb642c59a8bc10cf60b7f81ae9f3c7be070e07
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/8/9/89443d9b758e1f297c019b03131d50d594a36c6b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/8/9/89443d9b758e1f297c019b03131d50d594a36c6b
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/8/9/894a81455c4de60975f3026a8743156115a134d3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/8/9/894a81455c4de60975f3026a8743156115a134d3
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/8/c/8c55c3ccc257e5907959013f99656e4c8ec3903e
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/8/d/8db0821229949e8f2a589d10bc7ea9a135da6c8e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/8/d/8db0821229949e8f2a589d10bc7ea9a135da6c8e
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/9/d/9d1b1002fe90e56522647df3334da3b1b7bd68e3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/9/d/9d1b1002fe90e56522647df3334da3b1b7bd68e3
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/a/a/aa8bff76e11859170c4a52d2983d4f1a4fad9417:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/a/a/aa8bff76e11859170c4a52d2983d4f1a4fad9417
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/b/6/b697311ca27d5c6645f75c4077cf3eb564cf6ed1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/b/6/b697311ca27d5c6645f75c4077cf3eb564cf6ed1
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/c/1/c127b77fc102fc3fda632a76ddc99d4ccf7a10d5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/c/1/c127b77fc102fc3fda632a76ddc99d4ccf7a10d5
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/c/3/c33522a5f4d7f9914d0112918f975f2d45b30441:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/c/3/c33522a5f4d7f9914d0112918f975f2d45b30441
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/c/5/c577566c98eb6117440fee6c0f306f715d60d91e:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/c/5/c577566c98eb6117440fee6c0f306f715d60d91e
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/c/a/ca9e34c62628440135fb142a1fba5f028ec2359b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/c/a/ca9e34c62628440135fb142a1fba5f028ec2359b
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/d/8/d883e197a3fa06a093ef00c6b5d9a5ef7510e3e0:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/d/8/d883e197a3fa06a093ef00c6b5d9a5ef7510e3e0
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/e/1/e143825a3c2a2113b7aa40f2393d0114f79190e9:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/e/1/e143825a3c2a2113b7aa40f2393d0114f79190e9
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/f/0/f07866736216be0ee2aba49e392191aeae700a35
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/f/4/f4a01d6a4fcb971362ec00a83903fd3902f52164
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/f/c/fc90888977fdecd39953e843c6e3c6c7fd123ed2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/f/c/fc90888977fdecd39953e843c6e3c6c7fd123ed2
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/f/c/fca1737f836c684f59ec001226079c63e8ebd8ce:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/f/c/fca1737f836c684f59ec001226079c63e8ebd8ce
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/f/f/ff8ad05914c6aa4ce7fb60c92125f63a8a5ef08b:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/f/f/ff8ad05914c6aa4ce7fb60c92125f63a8a5ef08b
--------------------------------------------------------------------------------
/.idea/sonarlint/issuestore/index.pb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/.idea/sonarlint/issuestore/index.pb
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
Notes App Using Jetpack Compose
2 |
3 | [download apk from here](https://github.com/ELTEGANI/NotesApp/files/7609684/apk.zip)
4 |
5 |
6 |
7 |
8 | - [MVVM](https://developer.android.com/jetpack/guide?gclid=CjwKCAjwp_GJBhBmEiwALWBQk1owtAt-_GofEWOEfTFbUSnFwVrSBYjPI79ne6TNg6hgdIBrKlghMBoCD10QAvD_BwE&gclsrc=aw.ds) MVVM Architecture (Model - View - ViewModel)
9 | - [Clean-architecture](https://www.raywenderlich.com/3595916-clean-architecture-tutorial-for-android-getting-started) MVVM with Clean Architecture is pretty good in such cases. It goes one step further in separating the responsibilities of your code base.
10 | - [Jetpack Compose](https://developer.android.com/jetpack/compose?gclid=CjwKCAjwp_GJBhBmEiwALWBQky8AeNwcOvTsnGOMhZwBOKXNtzEV0rYpLRchJpoMeaJ2pbYp-JxzbxoCS1oQAvD_BwE&gclsrc=aw.ds) Modern design practices, Jetpack libraries enable fewer crashes and memory leaks. Describe your UI.
11 | - [kotlin](https://developer.android.com/kotlin?gclid=CjwKCAjwp_GJBhBmEiwALWBQkxkmTa4zClljSKWZm5xUmt5cWJo0zE4f24zmZW1uVLP6cHeF9BnjOxoCRYEQAvD_BwE&gclsrc=aw.ds) Modern, concise and safe programming language. Easy to pick up, so you can create powerful applications immediately
12 | - [Dagger - Hilt](https://developer.android.com/training/dependency-injection/hilt-android) dependency injection.
13 | - [Room](https://developer.android.com/jetpack/androidx/releases/room) Room persistence library.
14 | - [navigation](https://developer.android.com/guide/navigation?gclid=CjwKCAjwp_GJBhBmEiwALWBQk-LCvnuj3tWA07HX5cXG6p3HHjNnmH2Ong6Lk-_3sTGxh3yiW9Z3rRoCzLgQAvD_BwE&gclsrc=aw.ds) Android Jetpack's Navigation component helps you implement navigation
15 | - [flow](https://developer.android.com/kotlin/flow) notify domain layer data to views.
16 | - [coroutines](https://developer.android.com/kotlin/coroutines?gclid=CjwKCAjwp_GJBhBmEiwALWBQk4flWebxYMw9cmq_-zbKtyjEHLkwa6n_LgA5QWEm9lFwXJJPlXdgVxoCaB0QAvD_BwE&gclsrc=aw.ds) A coroutine is a concurrency design pattern that you can use on Android to simplify code that executes asynchronously.
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | id 'dagger.hilt.android.plugin'
5 | id 'kotlin-kapt'
6 | }
7 |
8 | android {
9 | compileSdk 31
10 | defaultConfig {
11 | applicationId "com.example.notesapp"
12 | minSdk 21
13 | targetSdk 31
14 | versionCode 1
15 | versionName "1.0"
16 | testInstrumentationRunner "com.example.notesapp.HiltTestRunner"
17 | vectorDrawables {
18 | useSupportLibrary true
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 | kotlinOptions {
33 | jvmTarget = '1.8'
34 | useIR = true
35 | }
36 | buildFeatures {
37 | compose true
38 | }
39 | composeOptions {
40 | kotlinCompilerExtensionVersion compose_version
41 | kotlinCompilerVersion '1.5.10'
42 | }
43 | packagingOptions {
44 | resources {
45 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
46 | }
47 | }
48 | }
49 |
50 | dependencies {
51 |
52 | implementation 'androidx.core:core-ktx:1.7.0'
53 | implementation 'androidx.appcompat:appcompat:1.4.0'
54 | implementation 'com.google.android.material:material:1.4.0'
55 | implementation "androidx.compose.ui:ui:$compose_version"
56 | implementation "androidx.compose.material:material:$compose_version"
57 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
58 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
59 | implementation 'androidx.activity:activity-compose:1.4.0'
60 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
61 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
62 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
63 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
64 |
65 | // Compose dependencies
66 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0"
67 | implementation "androidx.navigation:navigation-compose:2.4.0-beta02"
68 | implementation "androidx.compose.material:material-icons-extended:$compose_version"
69 | implementation "androidx.hilt:hilt-navigation-compose:1.0.0-beta01"
70 |
71 | // Coroutines
72 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0'
73 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1'
74 |
75 | //Dagger - Hilt
76 | implementation "com.google.dagger:hilt-android:2.38.1"
77 | kapt "com.google.dagger:hilt-android-compiler:2.37"
78 | implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
79 | kapt "androidx.hilt:hilt-compiler:1.0.0"
80 |
81 | // Room
82 | implementation "androidx.room:room-runtime:2.3.0"
83 | kapt "androidx.room:room-compiler:2.3.0"
84 |
85 | // Kotlin Extensions and Coroutines support for Room
86 | implementation "androidx.room:room-ktx:2.3.0"
87 |
88 | // Local unit tests
89 | testImplementation "androidx.test:core:1.4.0"
90 | testImplementation "junit:junit:4.13.2"
91 | testImplementation "androidx.arch.core:core-testing:2.1.0"
92 | testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2"
93 | testImplementation "com.google.truth:truth:1.1.3"
94 | testImplementation "com.squareup.okhttp3:mockwebserver:4.9.1"
95 | testImplementation "io.mockk:mockk:1.10.5"
96 | debugImplementation "androidx.compose.ui:ui-test-manifest:1.1.0-beta03"
97 |
98 | // Instrumentation tests
99 | androidTestImplementation 'com.google.dagger:hilt-android-testing:2.37'
100 | kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.37'
101 | androidTestImplementation "junit:junit:4.13.2"
102 | androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2"
103 | androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
104 | androidTestImplementation "com.google.truth:truth:1.1.3"
105 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
106 | androidTestImplementation 'androidx.test:core-ktx:1.4.0'
107 | androidTestImplementation "com.squareup.okhttp3:mockwebserver:4.9.1"
108 | androidTestImplementation "io.mockk:mockk-android:1.10.5"
109 | androidTestImplementation 'androidx.test:runner:1.4.0'
110 | }
--------------------------------------------------------------------------------
/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/example/notesapp/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp
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.example.notesapp", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/notesapp/HiltTestRunner.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import androidx.test.runner.AndroidJUnitRunner
6 | import dagger.hilt.android.testing.HiltTestApplication
7 |
8 |
9 | class HiltTestRunner : AndroidJUnitRunner(){
10 | override fun newApplication(
11 | cl: ClassLoader?,
12 | className: String?,
13 | context: Context?
14 | ): Application {
15 | return super.newApplication(cl,HiltTestApplication::class.java.name, context)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/notesapp/di/TestAppModule.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.di
2 |
3 | import android.app.Application
4 | import androidx.room.Room
5 | import com.example.notesapp.feature_note.data.data_source.NoteDataBase
6 | import com.example.notesapp.feature_note.data.repository.NoteRepositoryImp
7 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
8 | import com.example.notesapp.feature_note.domain.use_case.*
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 |
16 | @Module
17 | @InstallIn(SingletonComponent::class)
18 | object TestAppModule {
19 | @Provides
20 | @Singleton
21 | fun provideNoteDataBase(application: Application):NoteDataBase{
22 | return Room.inMemoryDatabaseBuilder(application,NoteDataBase::class.java)
23 | .build()
24 | }
25 |
26 | @Provides
27 | @Singleton
28 | fun provideNoteRepository(noteDataBase: NoteDataBase):NoteRepository{
29 | return NoteRepositoryImp(noteDataBase.noteDao)
30 | }
31 |
32 | @Provides
33 | @Singleton
34 | fun provideNoteUseCase(noteRepository: NoteRepository):NoteUseCases{
35 | return NoteUseCases(
36 | getNotes = GetNotes(noteRepository),
37 | deleteNotes = DeleteNotes(noteRepository),
38 | addNote = AddNotes(noteRepository),
39 | getNote = GetNote(noteRepository)
40 | )
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/notesapp/feature_note/presentation/notes/NoteScreenTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes
2 |
3 | import androidx.compose.animation.ExperimentalAnimationApi
4 | import androidx.compose.ui.test.junit4.createAndroidComposeRule
5 | import androidx.navigation.compose.NavHost
6 | import androidx.navigation.compose.composable
7 | import androidx.navigation.compose.rememberNavController
8 | import com.example.notesapp.di.AppModule
9 | import com.example.notesapp.feature_note.presentation.MainActivity
10 | import com.example.notesapp.feature_note.presentation.util.Screen
11 | import com.example.notesapp.ui.theme.CleanArchitectureNoteAppTheme
12 | import dagger.hilt.android.testing.HiltAndroidRule
13 | import dagger.hilt.android.testing.HiltAndroidTest
14 | import dagger.hilt.android.testing.UninstallModules
15 | import junit.framework.TestCase
16 | import org.junit.Before
17 | import org.junit.Rule
18 |
19 |
20 | @HiltAndroidTest
21 | @UninstallModules(AppModule::class)
22 | class NoteScreenTest{
23 | @get:Rule(order = 0)
24 | val hiltRule = HiltAndroidRule(this)
25 |
26 | @get:Rule(order = 1)
27 | val composeRule = createAndroidComposeRule()
28 |
29 | @ExperimentalAnimationApi
30 | @Before
31 | fun setUp(){
32 | hiltRule.inject()
33 | composeRule.setContent {
34 | val navController = rememberNavController()
35 | CleanArchitectureNoteAppTheme{
36 | NavHost(navController = navController, startDestination = Screen.NotesScreen.route){
37 | composable(route = Screen.NotesScreen.route) { NotesScreen(navController = navController) }
38 | }
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/NotesApp.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class NotesApp:Application()
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/di/AppModule.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.di
2 |
3 | import android.app.Application
4 | import androidx.room.Room
5 | import com.example.notesapp.feature_note.data.data_source.NoteDataBase
6 | import com.example.notesapp.feature_note.data.repository.NoteRepositoryImp
7 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
8 | import com.example.notesapp.feature_note.domain.use_case.*
9 | import dagger.Module
10 | import dagger.Provides
11 | import dagger.hilt.InstallIn
12 | import dagger.hilt.components.SingletonComponent
13 | import javax.inject.Singleton
14 |
15 |
16 | @Module
17 | @InstallIn(SingletonComponent::class)
18 | object AppModule {
19 | @Provides
20 | @Singleton
21 | fun provideNoteDataBase(application: Application):NoteDataBase{
22 | return Room.databaseBuilder(application,NoteDataBase::class.java,NoteDataBase.DATABASE_NAME)
23 | .build()
24 | }
25 |
26 | @Provides
27 | @Singleton
28 | fun provideNoteRepository(noteDataBase: NoteDataBase):NoteRepository{
29 | return NoteRepositoryImp(noteDataBase.noteDao)
30 | }
31 |
32 | @Provides
33 | @Singleton
34 | fun provideNoteUseCase(noteRepository: NoteRepository):NoteUseCases{
35 | return NoteUseCases(
36 | getNotes = GetNotes(noteRepository),
37 | deleteNotes = DeleteNotes(noteRepository),
38 | addNote = AddNotes(noteRepository),
39 | getNote = GetNote(noteRepository)
40 | )
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/data/data_source/NoteDao.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.data.data_source
2 |
3 | import androidx.room.*
4 | import com.example.notesapp.feature_note.domain.model.Note
5 | import kotlinx.coroutines.flow.Flow
6 |
7 |
8 | @Dao
9 | interface NoteDao {
10 | @Query("SELECT * FROM note")
11 | fun getNotes():Flow>
12 |
13 | @Query("SELECT * FROM note WHERE id = :id")
14 | suspend fun getNoteById(id:Int):Note?
15 |
16 | @Insert(onConflict = OnConflictStrategy.REPLACE)
17 | suspend fun insertNote(note: Note)
18 |
19 | @Delete
20 | suspend fun deleteNote(note: Note)
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/data/data_source/NoteDataBase.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.data.data_source
2 |
3 | import androidx.room.Database
4 | import androidx.room.RoomDatabase
5 | import com.example.notesapp.feature_note.domain.model.Note
6 |
7 |
8 | @Database(
9 | entities = [Note::class],
10 | version = 1
11 | )
12 | abstract class NoteDataBase : RoomDatabase() {
13 | abstract val noteDao:NoteDao
14 |
15 | companion object{
16 | const val DATABASE_NAME = "notes_db"
17 | }
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/data/repository/NoteRepositoryImp.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.data.repository
2 |
3 | import com.example.notesapp.feature_note.data.data_source.NoteDao
4 | import com.example.notesapp.feature_note.domain.model.Note
5 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | class NoteRepositoryImp(
9 | private val noteDao: NoteDao
10 | ) : NoteRepository{
11 |
12 | override fun getNotes(): Flow> {
13 | return noteDao.getNotes()
14 | }
15 |
16 | override suspend fun getNoteById(id: Int): Note? {
17 | return noteDao.getNoteById(id)
18 | }
19 |
20 | override suspend fun insertNote(note: Note) {
21 | noteDao.insertNote(note)
22 | }
23 |
24 | override suspend fun deleteNote(note: Note) {
25 | noteDao.deleteNote(note)
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/model/Note.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.model
2 |
3 | import androidx.compose.ui.graphics.Color
4 | import androidx.compose.ui.graphics.Color.Companion.LightGray
5 | import androidx.room.Entity
6 | import androidx.room.PrimaryKey
7 | import com.example.notesapp.ui.theme.BabyBlue
8 | import com.example.notesapp.ui.theme.RedOrange
9 | import com.example.notesapp.ui.theme.RedPink
10 | import com.example.notesapp.ui.theme.Violet
11 | import java.lang.Exception
12 |
13 |
14 | @Entity
15 | data class Note(
16 | val title:String,
17 | val content:String,
18 | val timesStamp:String,
19 | val color:Int,
20 | @PrimaryKey val id: Int? = null
21 | ){
22 | companion object{
23 | val noteColors = listOf(RedOrange,LightGray, Violet, BabyBlue, RedPink)
24 | }
25 | }
26 |
27 | class InvalidNoteException(message:String):Exception(message)
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/repository/NoteRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.repository
2 |
3 | import com.example.notesapp.feature_note.domain.model.Note
4 | import kotlinx.coroutines.flow.Flow
5 |
6 |
7 | interface NoteRepository {
8 | fun getNotes():Flow>
9 | suspend fun getNoteById(id:Int):Note?
10 | suspend fun insertNote(note: Note)
11 | suspend fun deleteNote(note: Note)
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/use_case/AddNotes.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.use_case
2 |
3 | import com.example.notesapp.feature_note.domain.model.InvalidNoteException
4 | import com.example.notesapp.feature_note.domain.model.Note
5 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
6 | import kotlin.jvm.Throws
7 |
8 |
9 | class AddNotes(
10 | private val notesRepository: NoteRepository
11 | ){
12 |
13 | @Throws(InvalidNoteException::class)
14 | suspend operator fun invoke(note:Note){
15 | if(note.title.isBlank()){
16 | throw InvalidNoteException("The title of the note can't empty")
17 | }
18 | if(note.content.isBlank()){
19 | throw InvalidNoteException("The content of the note can't empty")
20 | }
21 | notesRepository.insertNote(note)
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/use_case/DeleteNotes.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.use_case
2 |
3 | import com.example.notesapp.feature_note.domain.model.Note
4 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
5 |
6 | class DeleteNotes(private val repository: NoteRepository) {
7 | suspend operator fun invoke(note:Note){
8 | repository.deleteNote(note)
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/use_case/GetNote.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.use_case
2 |
3 | import com.example.notesapp.feature_note.domain.model.Note
4 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
5 |
6 | class GetNote(
7 | private val noteRepository: NoteRepository
8 | ) {
9 | suspend operator fun invoke(id:Int): Note?{
10 | return noteRepository.getNoteById(id)
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/use_case/GetNotes.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.use_case
2 | import com.example.notesapp.feature_note.domain.model.Note
3 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
4 | import com.example.notesapp.feature_note.domain.util.NoteOrder
5 | import com.example.notesapp.feature_note.domain.util.OrderType
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.map
8 |
9 |
10 | class GetNotes (
11 | private val repository: NoteRepository
12 | ){
13 | operator fun invoke(noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending)): Flow> {
14 | return repository.getNotes().map {notes->
15 | when(noteOrder.orderType){
16 | is OrderType.Ascending->{
17 | when(noteOrder){
18 | is NoteOrder.Title -> notes.sortedBy { it.title.lowercase() }
19 | is NoteOrder.Date -> notes.sortedBy { it.timesStamp }
20 | is NoteOrder.Color -> notes.sortedBy { it.color }
21 | }
22 | }
23 | is OrderType.Descending->{
24 | when(noteOrder){
25 | is NoteOrder.Title -> notes.sortedByDescending { it.title.lowercase() }
26 | is NoteOrder.Date -> notes.sortedByDescending { it.timesStamp }
27 | is NoteOrder.Color -> notes.sortedByDescending { it.color }
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/use_case/NoteUseCases.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.use_case
2 |
3 |
4 |
5 | data class NoteUseCases(
6 | val getNotes: GetNotes,
7 | val deleteNotes: DeleteNotes,
8 | val addNote:AddNotes,
9 | val getNote: GetNote
10 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/util/NoteOrder.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.util
2 |
3 |
4 | sealed class NoteOrder(var orderType: OrderType){
5 | class Title(orderType: OrderType):NoteOrder(orderType)
6 | class Date(orderType: OrderType):NoteOrder(orderType)
7 | class Color(orderType: OrderType):NoteOrder(orderType)
8 |
9 | fun copy(orderType: OrderType): NoteOrder{
10 | return when(this){
11 | is Title -> Title(orderType)
12 | is Date -> Date(orderType)
13 | is Color -> Color(orderType)
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/domain/util/OrderType.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.util
2 |
3 |
4 | sealed class OrderType{
5 | object Ascending:OrderType()
6 | object Descending:OrderType()
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.animation.ExperimentalAnimationApi
7 | import androidx.compose.material.MaterialTheme
8 | import androidx.compose.material.Surface
9 | import androidx.navigation.NavType
10 | import androidx.navigation.compose.NavHost
11 | import androidx.navigation.compose.composable
12 | import androidx.navigation.compose.rememberNavController
13 | import androidx.navigation.navArgument
14 | import com.example.notesapp.feature_note.presentation.add_edit_notes.AddEditNoteScreen
15 | import com.example.notesapp.feature_note.presentation.notes.NotesScreen
16 | import com.example.notesapp.feature_note.presentation.util.Screen
17 | import com.example.notesapp.ui.theme.CleanArchitectureNoteAppTheme
18 | import dagger.hilt.android.AndroidEntryPoint
19 |
20 |
21 |
22 | @AndroidEntryPoint
23 | class MainActivity : ComponentActivity() {
24 | @ExperimentalAnimationApi
25 | override fun onCreate(savedInstanceState: Bundle?) {
26 | super.onCreate(savedInstanceState)
27 | setContent {
28 | CleanArchitectureNoteAppTheme {
29 | Surface(
30 | color = MaterialTheme.colors.background
31 | ) {
32 | val navController = rememberNavController()
33 | NavHost(
34 | navController = navController,
35 | startDestination = Screen.NotesScreen.route){
36 | composable(route = Screen.NotesScreen.route){
37 | NotesScreen(navController = navController)
38 | }
39 | composable(route = Screen.AddEditNoteScreen.route + "?noteId={noteId}¬eColor={noteColor}",
40 | arguments = listOf(
41 | navArgument(name = "noteId"){
42 | type = NavType.IntType
43 | defaultValue = -1
44 | },
45 | navArgument(name = "noteColor"){
46 | type = NavType.IntType
47 | defaultValue = -1
48 | },
49 | )
50 | ){
51 | val color = it.arguments?.getInt("noteColor") ?: -1
52 | AddEditNoteScreen(navController = navController, noteColor = color)
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/add_edit_notes/AddEditNoteEvent.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.add_edit_notes
2 |
3 | import androidx.compose.ui.focus.FocusState
4 |
5 |
6 | sealed class AddEditNoteEvent {
7 | data class EnteredTitle(val value:String):AddEditNoteEvent()
8 | data class ChangeTitleFocus(val focus:FocusState):AddEditNoteEvent()
9 | data class EnteredContent(val value:String):AddEditNoteEvent()
10 | data class ChangeContentFocus(val focusState:FocusState):AddEditNoteEvent()
11 | data class changeColor(val color:Int):AddEditNoteEvent()
12 | object saveNote : AddEditNoteEvent()
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/add_edit_notes/AddEditNoteScreen.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.add_edit_notes
2 |
3 | import androidx.compose.animation.Animatable
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.border
7 | import androidx.compose.foundation.clickable
8 | import androidx.compose.foundation.layout.*
9 | import androidx.compose.foundation.shape.CircleShape
10 | import androidx.compose.material.*
11 | import androidx.compose.material.icons.Icons
12 | import androidx.compose.material.icons.filled.Save
13 | import androidx.compose.runtime.*
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.draw.clip
16 | import androidx.compose.ui.draw.shadow
17 | import androidx.compose.ui.graphics.Color
18 | import androidx.compose.ui.graphics.toArgb
19 | import androidx.compose.ui.unit.dp
20 | import androidx.hilt.navigation.compose.hiltViewModel
21 | import androidx.navigation.NavController
22 | import com.example.notesapp.feature_note.domain.model.Note
23 | import com.example.notesapp.feature_note.presentation.add_edit_notes.components.TransparentHintTextField
24 | import kotlinx.coroutines.flow.collectLatest
25 | import kotlinx.coroutines.launch
26 |
27 |
28 | @Composable
29 | fun AddEditNoteScreen(
30 | navController: NavController,
31 | noteColor:Int,
32 | viewModel: AddEditNoteViewModel = hiltViewModel()
33 | ){
34 | val titleState = viewModel.noteTitle.value
35 | val contentState = viewModel.noteContent.value
36 | val scaffoldState = rememberScaffoldState()
37 | val noteBackgroundAnimatable = remember {
38 | Animatable(
39 | Color(if(noteColor != -1) noteColor else viewModel.noteColor.value)
40 | )
41 | }
42 | val scope = rememberCoroutineScope()
43 | LaunchedEffect(key1 = true){
44 | viewModel.eventFlow.collectLatest {event->
45 | when(event){
46 | is AddEditNoteViewModel.UiEvent.ShowSnackbar ->{
47 | scaffoldState.snackbarHostState.showSnackbar(
48 | message = event.message
49 | )
50 | }
51 |
52 | is AddEditNoteViewModel.UiEvent.SaveNote ->{
53 | navController.navigateUp()
54 | }
55 |
56 | }
57 | }
58 | }
59 | Scaffold (
60 | floatingActionButton = {
61 | FloatingActionButton(onClick = {
62 | viewModel.onEvent(AddEditNoteEvent.saveNote)
63 | }
64 | ,backgroundColor = MaterialTheme.colors.primary) {
65 | Icon(imageVector = Icons.Default.Save, contentDescription ="Save Note")
66 | }
67 | },scaffoldState = scaffoldState
68 | ){
69 | Column (
70 | modifier = Modifier
71 | .fillMaxSize()
72 | .background(noteBackgroundAnimatable.value)
73 | .padding(16.dp)
74 | ){
75 | Row(
76 | modifier = Modifier
77 | .fillMaxWidth()
78 | .padding(8.dp),
79 | horizontalArrangement = Arrangement.SpaceBetween
80 | ) {
81 | Note.noteColors.forEach {color->
82 | val colorInt = color.toArgb()
83 | Box(
84 | modifier = Modifier
85 | .size(50.dp)
86 | .shadow(15.dp, CircleShape)
87 | .clip(CircleShape)
88 | .background(color)
89 | .border(
90 | width = 3.dp,
91 | color = if (viewModel.noteColor.value == colorInt) {
92 | Color.Black
93 | } else Color.Transparent,
94 | shape = CircleShape
95 | )
96 | .clickable {
97 | scope.launch {
98 | noteBackgroundAnimatable.animateTo(
99 | targetValue = Color(colorInt),
100 | animationSpec = tween(
101 | durationMillis = 500
102 | )
103 | )
104 | }
105 | viewModel.onEvent(AddEditNoteEvent.changeColor(colorInt))
106 | }
107 | )
108 | }
109 | }
110 | Spacer(modifier = Modifier.height(16.dp))
111 | TransparentHintTextField(text =
112 | titleState.text, hint =titleState.hint,
113 | onValueChange = {
114 | viewModel.onEvent(AddEditNoteEvent.EnteredTitle(it))
115 | }, onFocusChange = {
116 | viewModel.onEvent(AddEditNoteEvent.ChangeTitleFocus(it))
117 | },
118 | isHintVisible = titleState.isHintVisible,
119 | singleLine = true,
120 | textStyle = MaterialTheme.typography.h5)
121 | Spacer(modifier = Modifier.height(16.dp))
122 | TransparentHintTextField(text =
123 | contentState.text, hint =contentState.hint,
124 | onValueChange = {
125 | viewModel.onEvent(AddEditNoteEvent.EnteredContent(it))
126 | }, onFocusChange = {
127 | viewModel.onEvent(AddEditNoteEvent.ChangeContentFocus(it))
128 | },
129 | isHintVisible = titleState.isHintVisible,
130 | singleLine = true,
131 | textStyle = MaterialTheme.typography.body1,
132 | modifier = Modifier.fillMaxHeight()
133 | )
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/add_edit_notes/AddEditNoteViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.add_edit_notes
2 |
3 | import androidx.compose.runtime.State
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.ui.graphics.toArgb
6 | import androidx.lifecycle.SavedStateHandle
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.viewModelScope
9 | import com.example.notesapp.feature_note.domain.model.InvalidNoteException
10 | import com.example.notesapp.feature_note.domain.model.Note
11 | import com.example.notesapp.feature_note.domain.use_case.NoteUseCases
12 | import dagger.hilt.android.lifecycle.HiltViewModel
13 | import kotlinx.coroutines.flow.MutableSharedFlow
14 | import kotlinx.coroutines.flow.asSharedFlow
15 | import kotlinx.coroutines.launch
16 | import javax.inject.Inject
17 |
18 |
19 | @HiltViewModel
20 | class AddEditNoteViewModel @Inject constructor(
21 | private val noteUseCases: NoteUseCases,
22 | savedStateHandle: SavedStateHandle
23 | ) : ViewModel() {
24 |
25 | private val _noteTitle = mutableStateOf(NoteTextFieldState(
26 | hint = "Enter title..."
27 | ))
28 | val noteTitle: State = _noteTitle
29 |
30 | private val _noteContent = mutableStateOf(NoteTextFieldState(
31 | hint = "Enter some content"
32 | ))
33 | val noteContent: State = _noteContent
34 |
35 | private val _noteColor = mutableStateOf(Note.noteColors.random().toArgb())
36 | val noteColor: State = _noteColor
37 |
38 | private val _eventFlow = MutableSharedFlow()
39 | val eventFlow = _eventFlow.asSharedFlow()
40 |
41 | private var currentNoteId: Int? = null
42 |
43 | init {
44 | savedStateHandle.get("noteId")?.let { noteId ->
45 | if(noteId != -1) {
46 | viewModelScope.launch {
47 | noteUseCases.getNote(noteId)?.also { note ->
48 | currentNoteId = note.id
49 | _noteTitle.value = noteTitle.value.copy(
50 | text = note.title,
51 | isHintVisible = false
52 | )
53 | _noteContent.value = _noteContent.value.copy(
54 | text = note.content,
55 | isHintVisible = false
56 | )
57 | _noteColor.value = note.color
58 | }
59 | }
60 | }
61 | }
62 | }
63 |
64 | fun onEvent(event: AddEditNoteEvent) {
65 | when(event) {
66 | is AddEditNoteEvent.EnteredTitle -> {
67 | _noteTitle.value = noteTitle.value.copy(
68 | text = event.value
69 | )
70 | }
71 | is AddEditNoteEvent.ChangeTitleFocus -> {
72 | _noteTitle.value = noteTitle.value.copy(
73 | isHintVisible = !event.focus.isFocused &&
74 | noteTitle.value.text.isBlank()
75 | )
76 | }
77 | is AddEditNoteEvent.EnteredContent -> {
78 | _noteContent.value = _noteContent.value.copy(
79 | text = event.value
80 | )
81 | }
82 | is AddEditNoteEvent.ChangeContentFocus -> {
83 | _noteContent.value = _noteContent.value.copy(
84 | isHintVisible = !event.focusState.isFocused &&
85 | _noteContent.value.text.isBlank()
86 | )
87 | }
88 | is AddEditNoteEvent.changeColor -> {
89 | _noteColor.value = event.color
90 | }
91 | is AddEditNoteEvent.saveNote -> {
92 | viewModelScope.launch {
93 | try {
94 | noteUseCases.addNote(
95 | Note(
96 | title = noteTitle.value.text,
97 | content = noteContent.value.text,
98 | timesStamp = System.currentTimeMillis().toString(),
99 | color = noteColor.value,
100 | id = currentNoteId
101 | )
102 | )
103 | _eventFlow.emit(UiEvent.SaveNote)
104 | } catch(e: InvalidNoteException) {
105 | _eventFlow.emit(
106 | UiEvent.ShowSnackbar(
107 | message = e.message ?: "Couldn't save note"
108 | )
109 | )
110 | }
111 | }
112 | }
113 | }
114 | }
115 |
116 | sealed class UiEvent {
117 | data class ShowSnackbar(val message: String): UiEvent()
118 | object SaveNote: UiEvent()
119 | }
120 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/add_edit_notes/NoteTextFieldState.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.add_edit_notes
2 |
3 |
4 |
5 | data class NoteTextFieldState(
6 | val text:String = "",
7 | val hint:String = "",
8 | val isHintVisible:Boolean = true
9 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/add_edit_notes/components/TransParentHintTextField.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.add_edit_notes.components
2 |
3 | import androidx.compose.foundation.layout.Box
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.foundation.text.BasicTextField
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.focus.FocusState
10 | import androidx.compose.ui.focus.onFocusChanged
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.text.TextStyle
13 |
14 |
15 | @Composable
16 | fun TransparentHintTextField(
17 | text:String,
18 | hint:String,
19 | modifier:Modifier = Modifier,
20 | isHintVisible:Boolean = true,
21 | onValueChange:(String) -> Unit,
22 | textStyle: TextStyle = TextStyle(),
23 | singleLine:Boolean = false,
24 | onFocusChange:(FocusState) -> Unit
25 | ){
26 | Box(
27 | modifier = modifier
28 | ){
29 | BasicTextField(
30 | value = text,
31 | onValueChange = onValueChange,
32 | singleLine = singleLine,
33 | textStyle = textStyle,
34 | modifier = Modifier
35 | .fillMaxWidth()
36 | .onFocusChanged {
37 | onFocusChange(it)
38 | }
39 | )
40 | if(isHintVisible){
41 | Text(text = hint, style = textStyle, color = Color.DarkGray)
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/NoteScreen.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes
2 |
3 | import androidx.compose.animation.*
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.foundation.lazy.LazyColumn
7 | import androidx.compose.foundation.lazy.items
8 | import androidx.compose.material.*
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.filled.Add
11 | import androidx.compose.material.icons.filled.Sort
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.runtime.rememberCoroutineScope
14 | import androidx.compose.ui.Alignment
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.unit.dp
17 | import androidx.hilt.navigation.compose.hiltViewModel
18 | import androidx.navigation.NavController
19 | import com.example.notesapp.feature_note.presentation.notes.components.NoteItem
20 | import com.example.notesapp.feature_note.presentation.notes.components.OrderSection
21 | import com.example.notesapp.feature_note.presentation.util.Screen
22 | import kotlinx.coroutines.launch
23 |
24 |
25 | @ExperimentalAnimationApi
26 | @Composable
27 | fun NotesScreen(
28 | navController: NavController,
29 | viewModel: NotesViewModel = hiltViewModel()
30 | ) {
31 | val state = viewModel.state.value
32 | val scaffoldState = rememberScaffoldState()
33 | val scope = rememberCoroutineScope()
34 |
35 | Scaffold(
36 | floatingActionButton = {
37 | FloatingActionButton(
38 | onClick = {
39 | navController.navigate(Screen.AddEditNoteScreen.route)
40 | },
41 | backgroundColor = MaterialTheme.colors.primary
42 | ) {
43 | Icon(imageVector = Icons.Default.Add, contentDescription = "Add note")
44 | }
45 | },
46 | scaffoldState = scaffoldState
47 | ) {
48 | Column(
49 | modifier = Modifier
50 | .fillMaxSize()
51 | .padding(16.dp)
52 | ) {
53 | Row(
54 | modifier = Modifier.fillMaxWidth(),
55 | horizontalArrangement = Arrangement.SpaceBetween,
56 | verticalAlignment = Alignment.CenterVertically
57 | ) {
58 | Text(
59 | text = "Your note",
60 | style = MaterialTheme.typography.h4
61 | )
62 | IconButton(
63 | onClick = {
64 | viewModel.onEvent(NotesEvent.ToggleOrderSection)
65 | },
66 | ) {
67 | Icon(
68 | imageVector = Icons.Default.Sort,
69 | contentDescription = "Sort"
70 | )
71 | }
72 | }
73 | AnimatedVisibility(
74 | visible = state.isOrderSectionVisible,
75 | enter = fadeIn() + slideInVertically(),
76 | exit = fadeOut() + slideOutVertically()
77 | ) {
78 | OrderSection(
79 | modifier = Modifier
80 | .fillMaxWidth()
81 | .padding(vertical = 16.dp),
82 | noteOrder = state.noteOrder,
83 | onOrderChange = {
84 | viewModel.onEvent(NotesEvent.Order(it))
85 | }
86 | )
87 | }
88 | Spacer(modifier = Modifier.height(16.dp))
89 | LazyColumn(modifier = Modifier.fillMaxSize()) {
90 | items(state.notes) { note ->
91 | NoteItem(
92 | note = note,
93 | modifier = Modifier
94 | .fillMaxWidth()
95 | .clickable {
96 | navController.navigate(
97 | Screen.AddEditNoteScreen.route +
98 | "?noteId=${note.id}¬eColor=${note.color}"
99 | )
100 | },
101 | onDeleteClick = {
102 | viewModel.onEvent(NotesEvent.DeleteNote(note))
103 | scope.launch {
104 | val result = scaffoldState.snackbarHostState.showSnackbar(
105 | message = "Note deleted",
106 | actionLabel = "Undo"
107 | )
108 | if(result == SnackbarResult.ActionPerformed) {
109 | viewModel.onEvent(NotesEvent.RestoreNote)
110 | }
111 | }
112 | }
113 | )
114 | Spacer(modifier = Modifier.height(16.dp))
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/NotesEvent.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes
2 |
3 | import com.example.notesapp.feature_note.domain.model.Note
4 | import com.example.notesapp.feature_note.domain.util.NoteOrder
5 |
6 | sealed class NotesEvent{
7 | data class Order(val noteOrder: NoteOrder):NotesEvent()
8 | data class DeleteNote(val note: Note):NotesEvent()
9 | object RestoreNote : NotesEvent()
10 | object ToggleOrderSection : NotesEvent()
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/NotesState.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes
2 |
3 | import com.example.notesapp.feature_note.domain.model.Note
4 | import com.example.notesapp.feature_note.domain.util.NoteOrder
5 | import com.example.notesapp.feature_note.domain.util.OrderType
6 |
7 | data class NotesState(
8 | val notes : List = emptyList(),
9 | val noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending),
10 | val isOrderSectionVisible:Boolean = false
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/NotesViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes
2 |
3 | import androidx.compose.animation.expandVertically
4 | import androidx.compose.runtime.State
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.viewModelScope
8 | import com.example.notesapp.feature_note.domain.model.Note
9 | import com.example.notesapp.feature_note.domain.use_case.NoteUseCases
10 | import com.example.notesapp.feature_note.domain.util.NoteOrder
11 | import com.example.notesapp.feature_note.domain.util.OrderType
12 | import dagger.hilt.android.lifecycle.HiltViewModel
13 | import kotlinx.coroutines.Job
14 | import kotlinx.coroutines.flow.launchIn
15 | import kotlinx.coroutines.flow.onEach
16 | import kotlinx.coroutines.launch
17 | import javax.inject.Inject
18 |
19 |
20 | @HiltViewModel
21 | class NotesViewModel @Inject constructor(
22 | private val noteUseCases: NoteUseCases
23 | ) : ViewModel(){
24 |
25 | private val _state = mutableStateOf(NotesState())
26 | val state:State = _state
27 | private var recentDeleteNotes : Note? = null
28 | private var getNotesJob: Job? = null
29 |
30 | init {
31 | getNotes(NoteOrder.Date(OrderType.Descending))
32 | }
33 | fun onEvent(event: NotesEvent){
34 | when(event){
35 | is NotesEvent.Order->{
36 | if(state.value.noteOrder::class == event.noteOrder::class &&
37 | state.value.noteOrder.orderType == event.noteOrder.orderType){
38 | return
39 | }
40 | getNotes(event.noteOrder)
41 | }
42 |
43 | is NotesEvent.DeleteNote->{
44 | viewModelScope.launch {
45 | noteUseCases.deleteNotes(event.note)
46 | recentDeleteNotes = event.note
47 | }
48 | }
49 |
50 | is NotesEvent.RestoreNote->{
51 | viewModelScope.launch {
52 | noteUseCases.addNote(recentDeleteNotes ?: return@launch)
53 | recentDeleteNotes = null
54 | }
55 | }
56 |
57 | is NotesEvent.ToggleOrderSection->{
58 | _state.value = state.value.copy(
59 | isOrderSectionVisible = !state.value.isOrderSectionVisible
60 | )
61 | }
62 | }
63 | }
64 |
65 | private fun getNotes(notesOrder: NoteOrder){
66 | getNotesJob?.cancel()
67 | getNotesJob = noteUseCases.getNotes(notesOrder).onEach {notes->
68 | _state.value = state.value.copy(
69 | notes = notes,
70 | noteOrder = notesOrder
71 | )
72 | }.launchIn(viewModelScope)
73 | }
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/components/DefaultRadioButton.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes.components
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.Spacer
5 | import androidx.compose.foundation.layout.width
6 | import androidx.compose.foundation.layout.wrapContentSize
7 | import androidx.compose.material.MaterialTheme
8 | import androidx.compose.material.RadioButton
9 | import androidx.compose.material.RadioButtonDefaults
10 | import androidx.compose.material.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.unit.dp
15 |
16 |
17 | @Composable
18 | fun DefaultRadioButton(
19 | text:String,
20 | selected:Boolean,
21 | onSelect: ()-> Unit,
22 | modifier: Modifier = Modifier
23 | ){
24 | Row (
25 | modifier = modifier,
26 | verticalAlignment = Alignment.CenterVertically
27 | ){
28 | RadioButton(
29 | selected = selected,
30 | onClick = onSelect,
31 | colors = RadioButtonDefaults.colors(
32 | selectedColor = MaterialTheme.colors.primary,
33 | unselectedColor = MaterialTheme.colors.onBackground
34 | )
35 | )
36 | Spacer(modifier = Modifier.width(8.dp))
37 | Text(text = text,style = MaterialTheme.typography.body1)
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/components/NoteItem.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes.components
2 |
3 | import androidx.compose.foundation.Canvas
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.material.Icon
6 | import androidx.compose.material.IconButton
7 | import androidx.compose.material.MaterialTheme
8 | import androidx.compose.material.Text
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.filled.Delete
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.geometry.CornerRadius
15 | import androidx.compose.ui.geometry.Offset
16 | import androidx.compose.ui.geometry.Size
17 | import androidx.compose.ui.graphics.Color
18 | import androidx.compose.ui.graphics.Path
19 | import androidx.compose.ui.graphics.drawscope.clipPath
20 | import androidx.compose.ui.graphics.drawscope.clipRect
21 | import androidx.compose.ui.text.style.TextOverflow
22 | import androidx.compose.ui.unit.Dp
23 | import androidx.compose.ui.unit.dp
24 | import androidx.core.graphics.ColorUtils
25 | import com.example.notesapp.feature_note.domain.model.Note
26 |
27 |
28 | @Composable
29 | fun NoteItem(
30 | note:Note,
31 | modifier: Modifier = Modifier,
32 | cornerRadius: Dp = 10.dp,
33 | cutCornerRadius: Dp = 30.dp,
34 | onDeleteClick: () -> Unit
35 | ){
36 | Box(
37 | modifier = Modifier
38 | ){
39 | Canvas(modifier = Modifier.matchParentSize()){
40 | val clipPath = Path().apply {
41 | lineTo(size.width - cutCornerRadius.toPx(),0f)
42 | lineTo(size.width,cutCornerRadius.toPx())
43 | lineTo(size.width,size.height)
44 | lineTo(0f,size.height)
45 | close()
46 | }
47 | clipPath(clipPath){
48 | drawRoundRect(
49 | color = Color(note.color),
50 | size = size,
51 | cornerRadius = CornerRadius(cornerRadius.toPx())
52 | )
53 | drawRoundRect(
54 | color = Color(
55 | ColorUtils.blendARGB(note.color,0x000000,0.2f)
56 | ),
57 | topLeft= Offset(size.width - cutCornerRadius.toPx(),-100f),
58 | size = Size(cutCornerRadius.toPx()+100f,cutCornerRadius.toPx()+100f),
59 | cornerRadius = CornerRadius(cornerRadius.toPx())
60 | )
61 | }
62 | }
63 | Column(
64 | modifier = Modifier
65 | .fillMaxSize()
66 | .padding(16.dp)
67 | .padding(end = 32.dp)
68 | ) {
69 | Text(
70 | text = note.title,
71 | style = MaterialTheme.typography.h6,
72 | color = MaterialTheme.colors.surface,
73 | maxLines = 1,
74 | overflow = TextOverflow.Ellipsis
75 | )
76 | Spacer(modifier = Modifier.height(8.dp))
77 | Text(
78 | text = note.content,
79 | style = MaterialTheme.typography.body1,
80 | color = MaterialTheme.colors.surface,
81 | maxLines = 10,
82 | overflow = TextOverflow.Ellipsis
83 | )
84 | }
85 | IconButton(
86 | onClick = onDeleteClick,
87 | modifier = Modifier.align(Alignment.BottomEnd)) {
88 | Icon(imageVector = Icons.Default.Delete, contentDescription = "Delete note")
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/notes/components/OrderSection.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.notes.components
2 |
3 | import androidx.compose.foundation.layout.*
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.Modifier
6 | import androidx.compose.ui.unit.dp
7 | import com.example.notesapp.feature_note.domain.util.NoteOrder
8 | import com.example.notesapp.feature_note.domain.util.OrderType
9 |
10 |
11 | @Composable
12 | fun OrderSection(
13 | modifier: Modifier = Modifier,
14 | noteOrder: NoteOrder = NoteOrder.Date(OrderType.Descending),
15 | onOrderChange: (NoteOrder) -> Unit
16 | ) {
17 | Column(
18 | modifier = modifier
19 | ) {
20 | Row(
21 | modifier = Modifier.fillMaxWidth()
22 | ) {
23 | DefaultRadioButton(
24 | text = "Title",
25 | selected = noteOrder is NoteOrder.Title,
26 | onSelect = { onOrderChange(NoteOrder.Title(noteOrder.orderType)) }
27 | )
28 | Spacer(modifier = Modifier.width(8.dp))
29 | DefaultRadioButton(
30 | text = "Date",
31 | selected = noteOrder is NoteOrder.Date,
32 | onSelect = { onOrderChange(NoteOrder.Date(noteOrder.orderType)) }
33 | )
34 | Spacer(modifier = Modifier.width(8.dp))
35 | DefaultRadioButton(
36 | text = "Color",
37 | selected = noteOrder is NoteOrder.Color,
38 | onSelect = { onOrderChange(NoteOrder.Color(noteOrder.orderType)) }
39 | )
40 | }
41 | Spacer(modifier = Modifier.height(16.dp))
42 | Row(
43 | modifier = Modifier.fillMaxWidth()
44 | ) {
45 | DefaultRadioButton(
46 | text = "Ascending",
47 | selected = noteOrder.orderType is OrderType.Ascending,
48 | onSelect = {
49 | onOrderChange(noteOrder.copy(OrderType.Ascending))
50 | }
51 | )
52 | Spacer(modifier = Modifier.width(8.dp))
53 | DefaultRadioButton(
54 | text = "Descending",
55 | selected = noteOrder.orderType is OrderType.Descending,
56 | onSelect = {
57 | onOrderChange(noteOrder.copy(OrderType.Descending))
58 | }
59 | )
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/feature_note/presentation/util/Screen.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.presentation.util
2 |
3 |
4 |
5 | sealed class Screen(val route: String){
6 | object NotesScreen: Screen("notes_screen")
7 | object AddEditNoteScreen:Screen("add_edit_note_screen")
8 | }
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val DarkGray = Color(0xFF202020)
6 | val LightBlue = Color(0xFFD7E8DE)
7 |
8 | val RedOrange = Color(0xffffab91)
9 | val RedPink = Color(0xfff48fb1)
10 | val BabyBlue = Color(0xff81deea)
11 | val Violet = Color(0xffcf94da)
12 | val LightGreen = Color(0xffe7ed9b)
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.ui.theme
2 |
3 | import androidx.compose.material.MaterialTheme
4 | import androidx.compose.material.darkColors
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.graphics.Color
7 |
8 | private val DarkColorPalette = darkColors(
9 | primary = Color.White,
10 | background = DarkGray,
11 | onBackground = Color.White,
12 | surface = LightBlue,
13 | onSurface = DarkGray
14 | )
15 |
16 | @Composable
17 | fun CleanArchitectureNoteAppTheme(darkTheme: Boolean = true, content: @Composable() () -> Unit) {
18 | MaterialTheme(
19 | colors = DarkColorPalette,
20 | typography = Typography,
21 | shapes = Shapes,
22 | content = content
23 | )
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/notesapp/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.ui.theme
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | /* Other default text styles to override
17 | button = TextStyle(
18 | fontFamily = FontFamily.Default,
19 | fontWeight = FontWeight.W500,
20 | fontSize = 14.sp
21 | ),
22 | caption = TextStyle(
23 | fontFamily = FontFamily.Default,
24 | fontWeight = FontWeight.Normal,
25 | fontSize = 12.sp
26 | )
27 | */
28 | )
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NotesApp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/notesapp/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp
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/test/java/com/example/notesapp/feature_note/data/repository/FakeNoteRepository.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.data.repository
2 |
3 | import com.example.notesapp.feature_note.domain.model.Note
4 | import com.example.notesapp.feature_note.domain.repository.NoteRepository
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.flow
7 |
8 |
9 | class FakeNoteRepository : NoteRepository{
10 |
11 | private val notes = mutableListOf()
12 |
13 | override fun getNotes(): Flow> {
14 | return flow { emit(notes) }
15 | }
16 |
17 | override suspend fun getNoteById(id: Int): Note? {
18 | return notes.find { it.id == id }
19 | }
20 |
21 | override suspend fun insertNote(note: Note) {
22 | notes.add(note)
23 | }
24 |
25 | override suspend fun deleteNote(note: Note) {
26 | notes.remove(note)
27 | }
28 |
29 |
30 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/example/notesapp/feature_note/domain/use_case/GetNotesTest.kt:
--------------------------------------------------------------------------------
1 | package com.example.notesapp.feature_note.domain.use_case
2 |
3 | import com.example.notesapp.feature_note.data.repository.FakeNoteRepository
4 | import com.example.notesapp.feature_note.domain.model.Note
5 | import com.example.notesapp.feature_note.domain.util.NoteOrder
6 | import com.example.notesapp.feature_note.domain.util.OrderType
7 | import com.google.common.truth.Truth.assertThat
8 | import kotlinx.coroutines.flow.first
9 | import kotlinx.coroutines.runBlocking
10 | import org.junit.Before
11 | import org.junit.Test
12 |
13 | class GetNotesTest{
14 |
15 | private lateinit var getNotes: GetNotes
16 | private lateinit var fakeNoteRepository: FakeNoteRepository
17 |
18 | @Before
19 | fun seUp(){
20 | fakeNoteRepository = FakeNoteRepository()
21 | getNotes = GetNotes(fakeNoteRepository)
22 |
23 | val notesToInsert = mutableListOf()
24 | ('a'..'z').forEachIndexed{index,c->
25 | notesToInsert.add(
26 | Note(
27 | title = c.toString(),
28 | content = c.toString(),
29 | timesStamp = index.toString(),
30 | color = index
31 | )
32 | )
33 | }
34 | notesToInsert.shuffle()
35 | runBlocking {
36 | notesToInsert.forEach { fakeNoteRepository.insertNote(it) }
37 | }
38 | }
39 |
40 | @Test
41 | fun `Order notes by title ascending, correct order`() = runBlocking{
42 | val notes = getNotes(NoteOrder.Title(OrderType.Ascending)).first()
43 | for (i in 0..notes.size - 2){
44 | assertThat(notes[i].title).isLessThan(notes[i+1].title)
45 | }
46 | }
47 |
48 | @Test
49 | fun `Order notes by title descending, correct order`() = runBlocking{
50 | val notes = getNotes(NoteOrder.Title(OrderType.Descending)).first()
51 | for (i in 0..notes.size - 2){
52 | assertThat(notes[i].title).isGreaterThan(notes[i+1].title)
53 | }
54 | }
55 |
56 | @Test
57 | fun `Order notes by date ascending, correct order`() = runBlocking{
58 | val notes = getNotes(NoteOrder.Date(OrderType.Ascending)).first()
59 | for (i in 0..notes.size - 2){
60 | assertThat(notes[i].timesStamp).isLessThan(notes[i+1].timesStamp)
61 | }
62 | }
63 |
64 |
65 | @Test
66 | fun `Order notes by date descending, correct order`() = runBlocking{
67 | val notes = getNotes(NoteOrder.Date(OrderType.Descending)).first()
68 | for (i in 0..notes.size - 2){
69 | assertThat(notes[i].timesStamp).isGreaterThan(notes[i+1].timesStamp)
70 | }
71 | }
72 |
73 | @Test
74 | fun `Order notes by color ascending, correct order`() = runBlocking{
75 | val notes = getNotes(NoteOrder.Color(OrderType.Ascending)).first()
76 | for (i in 0..notes.size - 2){
77 | assertThat(notes[i].color).isLessThan(notes[i+1].color)
78 | }
79 | }
80 |
81 |
82 | @Test
83 | fun `Order notes by color descending, correct order`() = runBlocking{
84 | val notes = getNotes(NoteOrder.Color(OrderType.Descending)).first()
85 | for (i in 0..notes.size - 2){
86 | assertThat(notes[i].color).isGreaterThan(notes[i+1].color)
87 | }
88 | }
89 |
90 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext {
4 | compose_version = '1.0.5'
5 | }
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 | dependencies {
11 | classpath "com.android.tools.build:gradle:7.0.3"
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
13 | classpath "com.google.dagger:hilt-android-gradle-plugin:2.38.1"
14 |
15 | // NOTE: Do not place your application dependencies here; they belong
16 | // in the individual module build.gradle files
17 | }
18 | }
19 |
20 | task clean(type: Delete) {
21 | delete rootProject.buildDir
22 | }
--------------------------------------------------------------------------------
/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 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ELTEGANI/NotesApp/fa06931f212f24bbc901c92cf3ea844a8d9f3fdb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Oct 04 22:01:14 CAT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | jcenter() // Warning: this repository is going to shut down soon
7 | }
8 | }
9 | rootProject.name = "NotesApp"
10 | include ':app'
11 |
--------------------------------------------------------------------------------