├── .gitignore ├── .idea ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── dictionaries │ └── rajat.xml ├── discord.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── kotlinc.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── gamdestroyerr │ │ └── roomnote │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── gamdestroyerr │ │ │ └── roomnote │ │ │ ├── adapters │ │ │ ├── DiffUtilCallback.kt │ │ │ └── RvNotesAdapter.kt │ │ │ ├── db │ │ │ ├── DAO.kt │ │ │ └── NoteDatabase.kt │ │ │ ├── model │ │ │ └── Note.kt │ │ │ ├── repository │ │ │ └── NoteRepository.kt │ │ │ ├── ui │ │ │ ├── activity │ │ │ │ ├── NoteActivity.kt │ │ │ │ └── SplashActivity.kt │ │ │ └── fragments │ │ │ │ ├── NoteContentFragment.kt │ │ │ │ └── NoteFragment.kt │ │ │ ├── utils │ │ │ ├── ContentUriToActualFilePath.kt │ │ │ ├── Extensions.kt │ │ │ └── SwipeToDelete.kt │ │ │ └── viewmodel │ │ │ ├── NoteActivityViewModel.kt │ │ │ └── NoteActivityViewModelFactory.kt │ └── res │ │ ├── drawable-hdpi │ │ └── logo.png │ │ ├── drawable-ldpi │ │ └── logo.png │ │ ├── drawable-mdpi │ │ └── logo.png │ │ ├── drawable-xhdpi │ │ └── logo.png │ │ ├── drawable-xxhdpi │ │ └── logo.png │ │ ├── drawable-xxxhdpi │ │ └── logo.png │ │ ├── drawable │ │ ├── bottom_sheet_rounded.xml │ │ ├── date_rounded.xml │ │ ├── fab_btn_corner.xml │ │ ├── ic_baseline_color_lens_24.xml │ │ ├── ic_baseline_format_paint_24.xml │ │ ├── ic_baseline_more_horiz_24.xml │ │ ├── ic_edit.xml │ │ ├── ic_no_data.xml │ │ ├── ic_outline_calendar_today_24.xml │ │ ├── ic_round_add_24.xml │ │ ├── ic_round_add_a_photo_24.xml │ │ ├── ic_round_add_photo_alternate_24.xml │ │ ├── ic_round_arrow_back_24.xml │ │ ├── ic_round_clear_24.xml │ │ ├── ic_round_delete_24.xml │ │ ├── ic_round_search_24.xml │ │ ├── note_item_rounded.xml │ │ ├── note_item_rounded_over.xml │ │ ├── rounded_search.xml │ │ ├── shape_image.xml │ │ └── splash_background.xml │ │ ├── font │ │ ├── google_sans_bold.ttf │ │ ├── google_sans_medium.ttf │ │ └── google_sans_regular.ttf │ │ ├── layout │ │ ├── activity_note.xml │ │ ├── bottom_sheet_dialog.xml │ │ ├── fragment_note.xml │ │ ├── fragment_note_content.xml │ │ └── note_item_layout.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ ├── xml-v25 │ │ └── shortcuts.xml │ │ └── xml │ │ ├── fileprovider.xml │ │ └── shortcuts.xml │ └── test │ └── java │ └── com │ └── gamdestroyerr │ └── roomnote │ └── ExampleUnitTest.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 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Room Note -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 20 | 22 | 23 | 24 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | xmlns:android 33 | 34 | ^$ 35 | 36 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | xmlns:.* 44 | 45 | ^$ 46 | 47 | 48 | BY_NAME 49 | 50 |
51 |
52 | 53 | 54 | 55 | .*:id 56 | 57 | http://schemas.android.com/apk/res/android 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | .*:name 67 | 68 | http://schemas.android.com/apk/res/android 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | name 78 | 79 | ^$ 80 | 81 | 82 | 83 |
84 |
85 | 86 | 87 | 88 | style 89 | 90 | ^$ 91 | 92 | 93 | 94 |
95 |
96 | 97 | 98 | 99 | .* 100 | 101 | ^$ 102 | 103 | 104 | BY_NAME 105 | 106 |
107 |
108 | 109 | 110 | 111 | .* 112 | 113 | http://schemas.android.com/apk/res/android 114 | 115 | 116 | ANDROID_ATTRIBUTE_ORDER 117 | 118 |
119 |
120 | 121 | 122 | 123 | .* 124 | 125 | .* 126 | 127 | 128 | BY_NAME 129 | 130 |
131 |
132 |
133 |
134 | 135 | 137 |
138 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/dictionaries/rajat.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MVVM-Notes-app 2 | This is simple notes app that follows MVVM architectural design pattern and uses android jetpack components. 3 | 4 | ## MVVM Architecture 5 | 6 | __MVVM__ - MVVM stands for Model, View, ViewModel. MVVM is one of the architectural patterns which enhances separation of concerns, it allows separating the user interface logic from the business (or the back-end) logic. Its target is to achieve the following principle “Keeping UI code simple and free of app logic in order to make it easier to manage”. 7 | 8 | 9 | ![mvvm_architecture](https://user-images.githubusercontent.com/60071765/94697016-50584e00-0355-11eb-924e-4ea28814b94e.png) 10 | 11 | 12 | ## Android Jetpack components:- 13 | 1. __Navigation Components__ - Navigation component helps you implement navigation, from simple button clicks to more complex patterns, such as app bars and the navigation drawer. The Navigation component also ensures a consistent and predictable user experience by adhering to an established set of principles. 14 | 15 | 2. __Android Room Persistence__ - It is a SQLite object mapping library. Use it to Avoid boilerplate code and easily convert SQLite table data to Java objects. Room provides compile time checks of SQLite statements and can return RxJava, Flowable and LiveData observables. 16 | 17 | 3. __Kotlin Coroutines__ - A coroutine is a concurrency design pattern that you can use on Android to simplify code that executes asynchronously. On Android, coroutines help to manage long-running tasks that might otherwise block the main thread and cause your app to become unresponsive. 18 | 19 | 4. __ViewModel__ - It manages UI-related data in a lifecycle-conscious way. It stores UI-related data that isn't destroyed on app rotations. 20 | 21 | 5. __LiveData__ - It notifies views of any database changes. Use LiveData to build data objects that notify views when the underlying database changes. 22 | 23 | 6. __Kotlin__ - Kotlin is a modern statically typed programming language used by over 60% of professional Android developers that helps boost productivity, developer satisfaction, and code safety. 24 | 25 | It also uses RecyclerView with DiffUtill to improves overall app performances 26 | 27 | ## Features:- 28 | 1. Save Note In a Local db 29 | 2. Update 30 | 3. Swipe To Delete 31 | 4. Search 32 | 5. Color Picker (Colorful notes) 33 | 5. RecyclerView Animations 34 | 35 | ## App ScreenShots:- 36 | 37 | ![ezgif com-gif-maker](https://user-images.githubusercontent.com/60071765/94704102-302c8d00-035d-11eb-9035-e04487341b14.gif) 38 | ![ezgif com-gif-maker (1)](https://user-images.githubusercontent.com/60071765/94704994-28211d00-035e-11eb-859a-7ed354b1e2be.gif) 39 | 40 | 41 | 42 | 43 | ![screenshot_20200930-192853_not](https://user-images.githubusercontent.com/60071765/94698129-9feb4980-0356-11eb-836b-859bfc29bd01.png) 44 | ![screenshot_20200930-192914_not](https://user-images.githubusercontent.com/60071765/94698702-33bd1580-0357-11eb-873e-df51a27ff3e7.png) 45 | 46 | ## Libraries Used:- 47 |

Library used

48 | 57 | 58 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | apply plugin: "androidx.navigation.safeargs" 5 | 6 | android { 7 | compileSdkVersion 30 8 | buildToolsVersion "30.0.2" 9 | 10 | defaultConfig { 11 | applicationId "com.gamdestroyerr.roomnote" 12 | minSdkVersion 23 13 | targetSdkVersion 30 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildFeatures { 21 | viewBinding = true 22 | } 23 | 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | 29 | kotlinOptions { 30 | jvmTarget = JavaVersion.VERSION_1_8.toString() 31 | } 32 | 33 | buildTypes { 34 | release { 35 | minifyEnabled true 36 | shrinkResources true 37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 38 | } 39 | } 40 | } 41 | 42 | dependencies { 43 | 44 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 45 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 46 | def lifecycle_version = "2.2.0" 47 | def room_version = "2.3.0-alpha03" 48 | 49 | implementation fileTree(dir: "libs", include: ["*.jar"]) 50 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 51 | implementation 'androidx.core:core-ktx:1.3.2' 52 | implementation 'androidx.appcompat:appcompat:1.2.0' 53 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 54 | testImplementation 'junit:junit:4.13.1' 55 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 57 | 58 | //RecyclerView 59 | implementation "androidx.recyclerview:recyclerview:1.2.0-beta01" 60 | 61 | // RecyclerView Animator 62 | implementation 'jp.wasabeef:recyclerview-animators:3.0.0' 63 | 64 | // ViewModel 65 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 66 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 67 | 68 | 69 | // LiveData 70 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 71 | 72 | // Annotation processor 73 | implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0" 74 | 75 | //Room 76 | implementation "androidx.room:room-runtime:$room_version" 77 | kapt "androidx.room:room-compiler:$room_version" 78 | 79 | //Kotlin Extensions and Coroutines support for Room 80 | implementation "androidx.room:room-ktx:$room_version" 81 | 82 | // Coroutines 83 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9' 84 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' 85 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1' 86 | 87 | // Navigation Components 88 | implementation "androidx.fragment:fragment-ktx:1.3.0-beta02" 89 | implementation "androidx.navigation:navigation-fragment-ktx:2.3.2" 90 | implementation "androidx.navigation:navigation-ui-ktx:2.3.2" 91 | 92 | //material Components 93 | implementation 'com.google.android.material:material:1.3.0-alpha04' 94 | 95 | //color picker library 96 | implementation 'com.thebluealliance:spectrum:0.7.1' 97 | 98 | // Glide 99 | implementation 'com.github.bumptech.glide:glide:4.11.0' 100 | annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' 101 | 102 | implementation 'com.yahiaangelo.markdownedittext:markdownedittext:1.1.1' 103 | implementation "io.noties.markwon:core:4.6.0" 104 | implementation "io.noties.markwon:ext-strikethrough:4.6.0" 105 | implementation "io.noties.markwon:ext-tasklist:4.6.0" 106 | } -------------------------------------------------------------------------------- /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/gamdestroyerr/roomnote/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote 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.gamdestroyerr.roomnote", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 16 | 21 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/adapters/DiffUtilCallback.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.adapters 2 | 3 | import androidx.recyclerview.widget.DiffUtil 4 | import com.gamdestroyerr.roomnote.model.Note 5 | 6 | class DiffUtilCallback : DiffUtil.ItemCallback() { 7 | override fun areItemsTheSame(oldItem: Note, newItem: Note): Boolean { 8 | return oldItem.id == newItem.id 9 | } 10 | 11 | override fun areContentsTheSame(oldItem: Note, newItem: Note): Boolean { 12 | return oldItem.id == newItem.id 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/adapters/RvNotesAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.adapters 2 | 3 | import android.net.Uri 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import android.widget.TextView 9 | import androidx.core.view.isVisible 10 | import androidx.navigation.Navigation 11 | import androidx.navigation.fragment.FragmentNavigatorExtras 12 | import androidx.recyclerview.widget.RecyclerView 13 | import com.bumptech.glide.Glide 14 | import com.gamdestroyerr.roomnote.R 15 | import com.gamdestroyerr.roomnote.databinding.NoteItemLayoutBinding 16 | import com.gamdestroyerr.roomnote.model.Note 17 | import com.gamdestroyerr.roomnote.ui.fragments.NoteFragmentDirections 18 | import com.gamdestroyerr.roomnote.utils.hideKeyboard 19 | import com.gamdestroyerr.roomnote.utils.loadHiRezThumbnail 20 | import com.google.android.material.card.MaterialCardView 21 | import com.google.android.material.textview.MaterialTextView 22 | import io.noties.markwon.AbstractMarkwonPlugin 23 | import io.noties.markwon.Markwon 24 | import io.noties.markwon.MarkwonVisitor 25 | import io.noties.markwon.ext.strikethrough.StrikethroughPlugin 26 | import io.noties.markwon.ext.tasklist.TaskListPlugin 27 | import org.commonmark.node.SoftLineBreak 28 | import java.io.File 29 | 30 | 31 | class RvNotesAdapter : androidx.recyclerview.widget.ListAdapter< 32 | Note, 33 | RvNotesAdapter.NotesViewHolder>( 34 | DiffUtilCallback() 35 | ) { 36 | 37 | inner class NotesViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 38 | private val contentBinding = NoteItemLayoutBinding.bind(itemView) 39 | val title: MaterialTextView = contentBinding.noteItemTitle 40 | val content: TextView = contentBinding.noteContentItemTitle 41 | val date: MaterialTextView = contentBinding.noteDate 42 | val image: ImageView = contentBinding.itemNoteImage 43 | val parent: MaterialCardView = contentBinding.noteItemLayoutParent 44 | val markWon = Markwon.builder(itemView.context) 45 | .usePlugin(StrikethroughPlugin.create()) 46 | .usePlugin(TaskListPlugin.create(itemView.context)) 47 | .usePlugin(object : AbstractMarkwonPlugin() { 48 | override fun configureVisitor(builder: MarkwonVisitor.Builder) { 49 | super.configureVisitor(builder) 50 | builder.on( 51 | SoftLineBreak::class.java 52 | ) { visitor, _ -> visitor.forceNewLine() } 53 | } 54 | }) 55 | .build() 56 | } 57 | 58 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NotesViewHolder { 59 | return NotesViewHolder( 60 | LayoutInflater.from(parent.context) 61 | .inflate(R.layout.note_item_layout, parent, false) 62 | ) 63 | } 64 | 65 | override fun onBindViewHolder(holder: NotesViewHolder, position: Int) { 66 | 67 | getItem(position).let { note -> 68 | 69 | holder.apply { 70 | parent.transitionName = "recyclerView_${note.id}" 71 | 72 | title.text = note.title 73 | markWon.setMarkdown(content, note.content) 74 | date.text = note.date 75 | if (note.imagePath != null) { 76 | image.visibility = View.VISIBLE 77 | val uri = Uri.fromFile(File(note.imagePath)) 78 | if (File(note.imagePath).exists()) 79 | itemView.context.loadHiRezThumbnail(uri, image) 80 | } else { 81 | Glide.with(itemView).clear(image) 82 | image.isVisible = false 83 | } 84 | 85 | parent.setCardBackgroundColor(note.color) 86 | 87 | itemView.setOnClickListener { 88 | val action = NoteFragmentDirections.actionNoteFragmentToNoteContentFragment() 89 | .setNote(note) 90 | val extras = FragmentNavigatorExtras(parent to "recyclerView_${note.id}") 91 | it.hideKeyboard() 92 | Navigation.findNavController(it).navigate(action, extras) 93 | } 94 | content.setOnClickListener { 95 | val action = NoteFragmentDirections.actionNoteFragmentToNoteContentFragment() 96 | .setNote(note) 97 | val extras = FragmentNavigatorExtras(parent to "recyclerView_${note.id}") 98 | it.hideKeyboard() 99 | Navigation.findNavController(it).navigate(action, extras) 100 | } 101 | } 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/db/DAO.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.db 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.* 5 | import com.gamdestroyerr.roomnote.model.Note 6 | 7 | @Dao 8 | interface DAO { 9 | 10 | @Insert(onConflict = OnConflictStrategy.IGNORE) 11 | suspend fun addNote(note: Note) 12 | 13 | @Update 14 | suspend fun updateNote(note: Note) 15 | 16 | @Query("SELECT * FROM Note ORDER BY id DESC") 17 | fun getAllNote(): LiveData> 18 | 19 | @Query("SELECT * FROM Note WHERE title LIKE :query OR content LIKE :query OR date LIKE :query ORDER BY id DESC") 20 | fun searchNote(query: String): LiveData> 21 | 22 | @Delete 23 | suspend fun deleteNote(note: Note) 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/db/NoteDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.db 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | import com.gamdestroyerr.roomnote.model.Note 8 | 9 | @Database( 10 | entities = [Note::class], 11 | version = 1, 12 | exportSchema = false 13 | ) 14 | abstract class NoteDatabase : RoomDatabase() { 15 | 16 | abstract fun getNoteDao() : DAO 17 | 18 | companion object { 19 | 20 | @Volatile 21 | private var instance : NoteDatabase? = null 22 | private val LOCK = Any() 23 | 24 | operator fun invoke (context: Context) = instance ?: synchronized(LOCK) { 25 | instance ?: createDatabase(context).also { 26 | instance = it 27 | } 28 | } 29 | 30 | private fun createDatabase (context: Context) = Room.databaseBuilder( 31 | context.applicationContext, 32 | NoteDatabase::class.java, 33 | "note_database" 34 | ).build() 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/model/Note.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.model 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | import java.io.Serializable 6 | 7 | @Entity 8 | data class Note( 9 | @PrimaryKey(autoGenerate = true) 10 | var id: Int = 0, 11 | val title: String, 12 | val content: String, 13 | val date: String, 14 | val color: Int = -1, 15 | val imagePath: String?, 16 | ) : Serializable -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/repository/NoteRepository.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.repository 2 | 3 | import com.gamdestroyerr.roomnote.db.NoteDatabase 4 | import com.gamdestroyerr.roomnote.model.Note 5 | 6 | class NoteRepository( 7 | private val db: NoteDatabase, 8 | ) { 9 | 10 | fun getNote() = 11 | db.getNoteDao().getAllNote() 12 | 13 | fun searchNote(query: String) = 14 | db.getNoteDao().searchNote(query) 15 | 16 | suspend fun addNote(note: Note) = 17 | db.getNoteDao().addNote(note) 18 | 19 | suspend fun updateNote(note: Note) = 20 | db.getNoteDao().updateNote(note) 21 | 22 | suspend fun deleteNote(note: Note) = 23 | db.getNoteDao().deleteNote(note) 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/ui/activity/NoteActivity.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.ui.activity 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.lifecycle.ViewModelProvider 6 | import com.gamdestroyerr.roomnote.databinding.ActivityNoteBinding 7 | import com.gamdestroyerr.roomnote.db.NoteDatabase 8 | import com.gamdestroyerr.roomnote.repository.NoteRepository 9 | import com.gamdestroyerr.roomnote.utils.shortToast 10 | import com.gamdestroyerr.roomnote.viewmodel.NoteActivityViewModel 11 | import com.gamdestroyerr.roomnote.viewmodel.NoteActivityViewModelFactory 12 | 13 | class NoteActivity : AppCompatActivity() { 14 | 15 | lateinit var noteActivityViewModel: NoteActivityViewModel 16 | private lateinit var binding: ActivityNoteBinding 17 | 18 | override fun onCreate(savedInstanceState: Bundle?) { 19 | super.onCreate(savedInstanceState) 20 | binding = ActivityNoteBinding.inflate(layoutInflater) 21 | try { 22 | setContentView(binding.root) 23 | val noteRepository = NoteRepository(NoteDatabase(this)) 24 | val noteViewModelProviderFactory = NoteActivityViewModelFactory(noteRepository) 25 | noteActivityViewModel = ViewModelProvider( 26 | this, 27 | noteViewModelProviderFactory 28 | )[NoteActivityViewModel::class.java] 29 | } catch (e: Exception) { 30 | shortToast("error occurred") 31 | } 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/ui/activity/SplashActivity.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.ui.activity 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.appcompat.app.AppCompatActivity 6 | 7 | class SplashActivity : AppCompatActivity() { 8 | 9 | override fun onCreate(savedInstanceState: Bundle?) { 10 | super.onCreate(savedInstanceState) 11 | 12 | val intent = Intent(this@SplashActivity, NoteActivity::class.java) 13 | startActivity(intent) 14 | finish() 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/ui/fragments/NoteContentFragment.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.ui.fragments 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.app.Activity.RESULT_OK 6 | import android.content.Intent 7 | import android.content.pm.PackageManager 8 | import android.graphics.Color 9 | import android.graphics.drawable.Drawable 10 | import android.net.Uri 11 | import android.os.Bundle 12 | import android.provider.MediaStore 13 | import android.text.Spannable 14 | import android.text.SpannableString 15 | import android.text.style.ImageSpan 16 | import android.util.Log 17 | import android.view.ContextMenu 18 | import android.view.MenuItem 19 | import android.view.View 20 | import androidx.activity.OnBackPressedCallback 21 | import androidx.core.app.ActivityCompat 22 | import androidx.core.content.ContextCompat 23 | import androidx.core.content.FileProvider 24 | import androidx.core.os.bundleOf 25 | import androidx.core.view.ViewCompat 26 | import androidx.core.view.isVisible 27 | import androidx.fragment.app.Fragment 28 | import androidx.fragment.app.activityViewModels 29 | import androidx.fragment.app.setFragmentResult 30 | import androidx.navigation.NavController 31 | import androidx.navigation.Navigation 32 | import androidx.navigation.fragment.navArgs 33 | import androidx.transition.Transition 34 | import androidx.transition.TransitionListenerAdapter 35 | import com.gamdestroyerr.roomnote.R 36 | import com.gamdestroyerr.roomnote.databinding.BottomSheetDialogBinding 37 | import com.gamdestroyerr.roomnote.databinding.FragmentNoteContentBinding 38 | import com.gamdestroyerr.roomnote.model.Note 39 | import com.gamdestroyerr.roomnote.ui.activity.NoteActivity 40 | import com.gamdestroyerr.roomnote.utils.* 41 | import com.gamdestroyerr.roomnote.viewmodel.NoteActivityViewModel 42 | import com.google.android.material.bottomsheet.BottomSheetBehavior 43 | import com.google.android.material.bottomsheet.BottomSheetDialog 44 | import com.google.android.material.transition.MaterialContainerTransform 45 | import kotlinx.coroutines.* 46 | import java.io.File 47 | import java.text.SimpleDateFormat 48 | import java.util.* 49 | 50 | class NoteContentFragment : Fragment(R.layout.fragment_note_content) { 51 | 52 | private lateinit var navController: NavController 53 | private lateinit var contentBinding: FragmentNoteContentBinding 54 | private lateinit var result: String 55 | private lateinit var photoFile: File 56 | private var note: Note? = null 57 | private var color = -1 58 | private val noteActivityViewModel: NoteActivityViewModel by activityViewModels() 59 | private val currentDate = SimpleDateFormat.getDateInstance().format(Date()) 60 | private val REQUEST_IMAGE_CAPTURE = 100 61 | private val SELECT_IMAGE_FROM_STORAGE = 101 62 | private val job = CoroutineScope(Dispatchers.Main) 63 | private val args: NoteContentFragmentArgs by navArgs() 64 | 65 | override fun onCreate(savedInstanceState: Bundle?) { 66 | super.onCreate(savedInstanceState) 67 | 68 | val animation = MaterialContainerTransform().apply { 69 | drawingViewId = R.id.fragment 70 | scrimColor = Color.TRANSPARENT 71 | duration = 300L 72 | setAllContainerColors(requireContext().themeColor(R.attr.colorSurface)) 73 | } 74 | sharedElementEnterTransition = animation 75 | sharedElementReturnTransition = animation 76 | addSharedElementListener() 77 | } 78 | 79 | @SuppressLint("InflateParams", "QueryPermissionsNeeded") 80 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 81 | super.onViewCreated(view, savedInstanceState) 82 | contentBinding = FragmentNoteContentBinding.bind(view) 83 | 84 | /* Sets the unique transition name for the layout that is 85 | being inflated using SharedElementEnterTransition class */ 86 | ViewCompat.setTransitionName( 87 | contentBinding.noteContentFragmentParent, 88 | "recyclerView_${args.note?.id}" 89 | ) 90 | 91 | navController = Navigation.findNavController(view) 92 | val activity = activity as NoteActivity 93 | registerForContextMenu(contentBinding.noteImage) 94 | 95 | contentBinding.backBtn.setOnClickListener { 96 | requireView().hideKeyboard() 97 | saveNoteAndGoBack() 98 | } 99 | 100 | try { 101 | contentBinding.etNoteContent.setOnFocusChangeListener { _, hasFocus -> 102 | if (hasFocus) { 103 | contentBinding.bottomBar.visibility = View.VISIBLE 104 | contentBinding.etNoteContent.setStylesBar(contentBinding.styleBar) 105 | } else contentBinding.bottomBar.visibility = View.GONE 106 | } 107 | } catch (e: Throwable) { 108 | Log.d("TAG", e.stackTraceToString()) 109 | } 110 | 111 | contentBinding.noteOptionsMenu.setOnClickListener { 112 | val bottomSheetDialog = BottomSheetDialog( 113 | requireContext(), 114 | R.style.BottomSheetDialogTheme, 115 | ) 116 | val bottomSheetView: View = layoutInflater.inflate( 117 | R.layout.bottom_sheet_dialog, 118 | null, 119 | ) 120 | 121 | with(bottomSheetDialog) { 122 | setContentView(bottomSheetView) 123 | show() 124 | } 125 | val bottomSheetBinding = BottomSheetDialogBinding.bind(bottomSheetView) 126 | 127 | bottomSheetBinding.apply { 128 | colorPicker.apply { 129 | setSelectedColor(color) 130 | setOnColorSelectedListener { value -> 131 | color = value 132 | contentBinding.apply { 133 | noteContentFragmentParent.setBackgroundColor(color) 134 | toolbarFragmentNoteContent.setBackgroundColor(color) 135 | bottomBar.setBackgroundColor(color) 136 | activity.window.statusBarColor = color 137 | } 138 | bottomSheetBinding.bottomSheetParent.setCardBackgroundColor(color) 139 | } 140 | } 141 | bottomSheetParent.setCardBackgroundColor(color) 142 | } 143 | bottomSheetView.post { 144 | bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED 145 | } 146 | bottomSheetBinding.addImage.setOnClickListener { 147 | val permission = ActivityCompat.checkSelfPermission( 148 | activity, 149 | Manifest.permission.CAMERA, 150 | ) 151 | if (permission != PackageManager.PERMISSION_GRANTED) { 152 | 153 | val permissionArray = arrayOf(Manifest.permission.CAMERA) 154 | ActivityCompat.requestPermissions( 155 | activity, 156 | permissionArray, 157 | REQUEST_IMAGE_CAPTURE 158 | ) 159 | ActivityCompat.OnRequestPermissionsResultCallback { requestCode, 160 | permissions, 161 | grantResults -> 162 | when (requestCode) { 163 | REQUEST_IMAGE_CAPTURE -> { 164 | if (permissions[0] == Manifest.permission.CAMERA && 165 | grantResults.isNotEmpty() 166 | ) { 167 | Log.d("tag", "this function is called") 168 | takePictureIntent() 169 | } 170 | } 171 | } 172 | } 173 | } 174 | if (permission == PackageManager.PERMISSION_GRANTED) { 175 | takePictureIntent() 176 | bottomSheetDialog.dismiss() 177 | } 178 | } 179 | @Suppress("DEPRECATION") 180 | bottomSheetBinding.selectImage.setOnClickListener { 181 | Intent(Intent.ACTION_GET_CONTENT).also { chooseIntent -> 182 | chooseIntent.type = "image/*" 183 | chooseIntent.resolveActivity(activity.packageManager!!.also { 184 | startActivityForResult(chooseIntent, SELECT_IMAGE_FROM_STORAGE) 185 | }) 186 | } 187 | bottomSheetDialog.dismiss() 188 | } 189 | } 190 | 191 | //opens with existing note item 192 | setUpNote() 193 | 194 | activity.onBackPressedDispatcher.addCallback( 195 | viewLifecycleOwner, 196 | object : OnBackPressedCallback(true) { 197 | override fun handleOnBackPressed() { 198 | saveNoteAndGoBack() 199 | } 200 | }) 201 | } 202 | 203 | private fun addSharedElementListener() { 204 | (sharedElementEnterTransition as Transition).addListener( 205 | object : TransitionListenerAdapter() { 206 | override fun onTransitionStart(transition: Transition) { 207 | super.onTransitionStart(transition) 208 | if (args.note?.imagePath != null) { 209 | contentBinding.noteImage.isVisible = true 210 | val uri = Uri.fromFile(File(args.note?.imagePath!!)) 211 | job.launch { 212 | requireContext().asyncImageLoader(uri, contentBinding.noteImage, this) 213 | } 214 | } else contentBinding.noteImage.isVisible = false 215 | } 216 | } 217 | ) 218 | } 219 | 220 | /** 221 | * This Method handles the save and update operation. 222 | * 223 | * Checks if the note arg is null 224 | * It will save the note with a unique id. 225 | * 226 | * If note arg has data it will update 227 | * note to save any changes. */ 228 | private fun saveNoteAndGoBack() { 229 | 230 | if (contentBinding.etTitle.text.toString().isEmpty() && 231 | contentBinding.etNoteContent.text.toString().isEmpty() 232 | ) { 233 | result = "Empty Note Discarded" 234 | setFragmentResult("key", bundleOf("bundleKey" to result)) 235 | navController.navigate( 236 | NoteContentFragmentDirections 237 | .actionNoteContentFragmentToNoteFragment() 238 | ) 239 | 240 | } else { 241 | note = args.note 242 | when (note) { 243 | null -> { 244 | noteActivityViewModel.saveNote( 245 | Note( 246 | 0, 247 | contentBinding.etTitle.text.toString(), 248 | contentBinding.etNoteContent.getMD(), 249 | currentDate, 250 | color, 251 | noteActivityViewModel.setImagePath(), 252 | ) 253 | ) 254 | result = "Note Saved" 255 | setFragmentResult( 256 | "key", 257 | bundleOf("bundleKey" to result) 258 | ) 259 | navController.navigate( 260 | NoteContentFragmentDirections 261 | .actionNoteContentFragmentToNoteFragment() 262 | ) 263 | 264 | } 265 | else -> { 266 | updateNote() 267 | navController.popBackStack() 268 | } 269 | } 270 | } 271 | } 272 | 273 | private fun updateNote() { 274 | if (note != null) { 275 | noteActivityViewModel.updateNote( 276 | Note( 277 | note!!.id, 278 | contentBinding.etTitle.text.toString(), 279 | contentBinding.etNoteContent.getMD(), 280 | currentDate, 281 | color, 282 | noteActivityViewModel.setImagePath(), 283 | ) 284 | ) 285 | } 286 | } 287 | 288 | @SuppressLint("QueryPermissionsNeeded") 289 | @Suppress("DEPRECATION") 290 | private fun takePictureIntent() { 291 | 292 | Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { captureIntent -> 293 | photoFile = getPhotoFile(requireActivity()) 294 | val fileProvider = FileProvider.getUriForFile( 295 | requireContext(), 296 | getString(R.string.fileAuthority), 297 | photoFile 298 | ) 299 | captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider) 300 | captureIntent.resolveActivity(activity?.packageManager!!.also { 301 | startActivityForResult(captureIntent, REQUEST_IMAGE_CAPTURE) 302 | }) 303 | } 304 | } 305 | 306 | private fun menuIconWithText(r: Drawable, title: String): CharSequence { 307 | r.setBounds(0, 0, r.intrinsicWidth, r.intrinsicHeight) 308 | val sb = SpannableString(" $title") 309 | val imageSpan = ImageSpan(r, ImageSpan.ALIGN_BOTTOM) 310 | sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) 311 | return sb 312 | } 313 | 314 | private fun setUpNote() { 315 | val note = args.note 316 | val title = contentBinding.etTitle 317 | val content = contentBinding.etNoteContent 318 | val lastEdited = contentBinding.lastEdited 319 | val savedImage = noteActivityViewModel.setImagePath() 320 | 321 | if (note == null) { 322 | lastEdited.text = 323 | getString(R.string.edited_on, SimpleDateFormat.getDateInstance().format(Date())) 324 | setImage(noteActivityViewModel.setImagePath()) 325 | } 326 | 327 | if (note != null) { 328 | title.setText(note.title) 329 | content.renderMD(note.content) 330 | lastEdited.text = getString(R.string.edited_on, note.date) 331 | color = note.color 332 | if (savedImage != null) setImage(savedImage) 333 | else noteActivityViewModel.saveImagePath(note.imagePath) 334 | contentBinding.apply { 335 | job.launch { 336 | delay(10) 337 | noteContentFragmentParent.setBackgroundColor(color) 338 | noteImage.isVisible = true 339 | } 340 | toolbarFragmentNoteContent.setBackgroundColor(color) 341 | bottomBar.setBackgroundColor(color) 342 | } 343 | activity?.window?.statusBarColor = note.color 344 | } 345 | } 346 | 347 | /** 348 | * This method gets a filePath as a string and converts it into URI 349 | * then passes that URI and the target imageView to and extension function 350 | * loadImage that will the image to its given target*/ 351 | private fun setImage(filePath: String?) { 352 | if (filePath != null) { 353 | val uri = Uri.fromFile(File(filePath)) 354 | contentBinding.noteImage.isVisible = true 355 | try { 356 | job.launch { 357 | requireContext().asyncImageLoader(uri, contentBinding.noteImage, this) 358 | } 359 | } catch (e: Exception) { 360 | context?.shortToast(e.message) 361 | contentBinding.noteImage.isVisible = false 362 | } 363 | } else contentBinding.noteImage.isVisible = false 364 | } 365 | 366 | @Suppress("DEPRECATION") 367 | override fun onActivityResult( 368 | requestCode: Int, 369 | resultCode: Int, 370 | data: Intent? 371 | ) { 372 | if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { 373 | noteActivityViewModel.saveImagePath(photoFile.absolutePath) 374 | setImage(photoFile.absolutePath) 375 | } 376 | if (requestCode == SELECT_IMAGE_FROM_STORAGE && resultCode == RESULT_OK) { 377 | val uri = data?.data 378 | Log.d("Tag", uri.toString()) 379 | if (uri != null) { 380 | val selectedImagePath = getImageUrlWithAuthority( 381 | requireContext(), 382 | uri, 383 | requireActivity() 384 | ) 385 | noteActivityViewModel.saveImagePath(selectedImagePath) 386 | setImage(selectedImagePath) 387 | } 388 | } 389 | super.onActivityResult(requestCode, resultCode, data) 390 | } 391 | 392 | 393 | override fun onCreateContextMenu( 394 | menu: ContextMenu, 395 | v: View, 396 | menuInfo: ContextMenu.ContextMenuInfo? 397 | ) { 398 | super.onCreateContextMenu(menu, v, menuInfo) 399 | menu.add( 400 | 0, 401 | 1, 402 | 1, 403 | menuIconWithText( 404 | ContextCompat.getDrawable( 405 | requireContext(), 406 | R.drawable.ic_round_delete_24 407 | )!!, getString(R.string.delete) 408 | ) 409 | ) 410 | } 411 | 412 | override fun onContextItemSelected(item: MenuItem): Boolean { 413 | when (item.itemId) { 414 | 1 -> { 415 | if (note?.imagePath != null) { 416 | val toDelete = File(note?.imagePath!!) 417 | if (toDelete.exists()) { 418 | toDelete.delete() 419 | } 420 | } 421 | if (noteActivityViewModel.setImagePath() != null) { 422 | val toDelete = File(noteActivityViewModel.setImagePath()!!) 423 | if (toDelete.exists()) { 424 | toDelete.delete() 425 | } 426 | noteActivityViewModel.saveImagePath(null) 427 | } 428 | 429 | contentBinding.noteImage.isVisible = false 430 | updateNote() 431 | context?.shortToast("Deleted") 432 | } 433 | } 434 | return super.onContextItemSelected(item) 435 | } 436 | 437 | override fun onDestroy() { 438 | super.onDestroy() 439 | if (job.isActive) { 440 | job.cancel() 441 | } 442 | } 443 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/ui/fragments/NoteFragment.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.ui.fragments 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.res.Configuration.ORIENTATION_LANDSCAPE 5 | import android.content.res.Configuration.ORIENTATION_PORTRAIT 6 | import android.graphics.Color 7 | import android.os.Bundle 8 | import android.text.Editable 9 | import android.text.TextWatcher 10 | import android.util.Log 11 | import android.view.View 12 | import android.view.inputmethod.EditorInfo 13 | import androidx.core.content.ContextCompat 14 | import androidx.core.view.isVisible 15 | import androidx.fragment.app.Fragment 16 | import androidx.fragment.app.activityViewModels 17 | import androidx.fragment.app.setFragmentResultListener 18 | import androidx.navigation.Navigation 19 | import androidx.recyclerview.widget.ItemTouchHelper 20 | import androidx.recyclerview.widget.RecyclerView 21 | import androidx.recyclerview.widget.StaggeredGridLayoutManager 22 | import com.gamdestroyerr.roomnote.R 23 | import com.gamdestroyerr.roomnote.adapters.RvNotesAdapter 24 | import com.gamdestroyerr.roomnote.databinding.FragmentNoteBinding 25 | import com.gamdestroyerr.roomnote.ui.activity.NoteActivity 26 | import com.gamdestroyerr.roomnote.utils.SwipeToDelete 27 | import com.gamdestroyerr.roomnote.utils.hideKeyboard 28 | import com.gamdestroyerr.roomnote.viewmodel.NoteActivityViewModel 29 | import com.google.android.material.snackbar.BaseTransientBottomBar 30 | import com.google.android.material.snackbar.Snackbar 31 | import com.google.android.material.transition.MaterialElevationScale 32 | import kotlinx.coroutines.CoroutineScope 33 | import kotlinx.coroutines.Dispatchers 34 | import kotlinx.coroutines.delay 35 | import kotlinx.coroutines.launch 36 | import java.io.File 37 | import java.util.concurrent.TimeUnit 38 | 39 | class NoteFragment : Fragment(R.layout.fragment_note) { 40 | 41 | private val noteActivityViewModel: NoteActivityViewModel by activityViewModels() 42 | private lateinit var rvAdapter: RvNotesAdapter 43 | private lateinit var noteBinding: FragmentNoteBinding 44 | 45 | override fun onCreate(savedInstanceState: Bundle?) { 46 | super.onCreate(savedInstanceState) 47 | 48 | exitTransition = MaterialElevationScale(false).apply { 49 | duration = 350 50 | } 51 | enterTransition = MaterialElevationScale(true).apply { 52 | duration = 350 53 | } 54 | } 55 | 56 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 57 | super.onViewCreated(view, savedInstanceState) 58 | noteBinding = FragmentNoteBinding.bind(view) 59 | val activity = activity as NoteActivity 60 | val navController = Navigation.findNavController(view) 61 | 62 | requireView().hideKeyboard() 63 | 64 | CoroutineScope(Dispatchers.Main).launch { 65 | delay(10) 66 | activity.window.statusBarColor = Color.WHITE 67 | } 68 | 69 | 70 | val count = parentFragmentManager.backStackEntryCount 71 | Log.d("backStackCount", count.toString()) 72 | noteActivityViewModel.saveImagePath(null) 73 | 74 | 75 | //Receives confirmation from the noteContentFragment 76 | setFragmentResultListener("key") { _, bundle -> 77 | when (val result = bundle.getString("bundleKey")) { 78 | "Note Saved", "Empty Note Discarded" -> { 79 | CoroutineScope(Dispatchers.Main).launch { 80 | Snackbar.make(view, result, Snackbar.LENGTH_SHORT).apply { 81 | animationMode = Snackbar.ANIMATION_MODE_FADE 82 | setAnchorView(R.id.addNoteFab) 83 | }.show() 84 | noteBinding.rvNote.isVisible = false 85 | delay(300) 86 | recyclerViewDisplay() 87 | noteBinding.rvNote.isVisible = true 88 | } 89 | } 90 | } 91 | } 92 | 93 | //sets up RecyclerView 94 | recyclerViewDisplay() 95 | swipeToDelete(noteBinding.rvNote) 96 | 97 | //implements search function 98 | noteBinding.search.addTextChangedListener(object : TextWatcher { 99 | 100 | override fun beforeTextChanged( 101 | s: CharSequence?, 102 | start: Int, 103 | count: Int, 104 | after: Int 105 | ) { 106 | noteBinding.noData.isVisible = false 107 | } 108 | 109 | override fun onTextChanged( 110 | s: CharSequence?, 111 | start: Int, 112 | before: Int, 113 | count: Int 114 | ) { 115 | if (s.toString().isNotEmpty()) { 116 | noteBinding.clearText.visibility = View.VISIBLE 117 | val text = s.toString() 118 | val query = "%$text%" 119 | if (query.isNotEmpty()) { 120 | noteActivityViewModel.searchNote(query).observe(viewLifecycleOwner) { 121 | rvAdapter.submitList(it) 122 | } 123 | } else { 124 | observerDataChanges() 125 | } 126 | } else { 127 | observerDataChanges() 128 | } 129 | } 130 | 131 | override fun afterTextChanged(s: Editable?) { 132 | if (s.toString().isEmpty()) { 133 | noteBinding.clearText.visibility = View.GONE 134 | } 135 | } 136 | 137 | }) 138 | 139 | noteBinding.search.setOnEditorActionListener { v, actionId, _ -> 140 | if (actionId == EditorInfo.IME_ACTION_SEARCH) { 141 | v.clearFocus() 142 | requireView().hideKeyboard() 143 | } 144 | return@setOnEditorActionListener true 145 | } 146 | 147 | noteBinding.clearText.setOnClickListener { 148 | clearTxtFunction() 149 | it.isVisible = false 150 | noteBinding.noData.isVisible = false 151 | } 152 | 153 | 154 | 155 | noteBinding.addNoteFab.setOnClickListener { 156 | noteBinding.appBarLayout1.visibility = View.INVISIBLE 157 | navController.navigate(NoteFragmentDirections.actionNoteFragmentToNoteContentFragment()) 158 | } 159 | noteBinding.innerFab.setOnClickListener { 160 | navController.navigate(NoteFragmentDirections.actionNoteFragmentToNoteContentFragment()) 161 | } 162 | 163 | 164 | 165 | noteBinding.rvNote.setOnScrollChangeListener { _, scrollX, scrollY, _, oldScrollY -> 166 | when { 167 | scrollY > oldScrollY -> { 168 | noteBinding.chatFabText.isVisible = false 169 | 170 | } 171 | scrollX == scrollY -> { 172 | noteBinding.chatFabText.isVisible = true 173 | 174 | } 175 | else -> { 176 | noteBinding.chatFabText.isVisible = true 177 | } 178 | } 179 | } 180 | } //onViewCreated closed 181 | 182 | private fun recyclerViewDisplay() { 183 | @SuppressLint("SwitchIntDef") 184 | when (resources.configuration.orientation) { 185 | ORIENTATION_PORTRAIT -> setUpRecyclerView(2) 186 | ORIENTATION_LANDSCAPE -> setUpRecyclerView(3) 187 | } 188 | } 189 | 190 | private fun setUpRecyclerView(spanCount: Int) { 191 | noteBinding.rvNote.apply { 192 | layoutManager = 193 | StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL) 194 | setHasFixedSize(true) 195 | rvAdapter = RvNotesAdapter() 196 | rvAdapter.stateRestorationPolicy = 197 | RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY 198 | adapter = rvAdapter 199 | postponeEnterTransition(300L, TimeUnit.MILLISECONDS) 200 | viewTreeObserver.addOnPreDrawListener { 201 | startPostponedEnterTransition() 202 | true 203 | } 204 | } 205 | observerDataChanges() 206 | } 207 | 208 | private fun observerDataChanges() { 209 | noteActivityViewModel.getAllNotes().observe(viewLifecycleOwner) { list -> 210 | noteBinding.noData.isVisible = list.isEmpty() 211 | rvAdapter.submitList(list) 212 | } 213 | } 214 | 215 | private fun swipeToDelete(recyclerView: RecyclerView) { 216 | 217 | val swipeToDeleteCallback = object : SwipeToDelete() { 218 | override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { 219 | val position = viewHolder.absoluteAdapterPosition 220 | val note = rvAdapter.currentList[position] 221 | var actionBtnTapped = false 222 | noteActivityViewModel.deleteNote(note) 223 | noteBinding.search.apply { 224 | hideKeyboard() 225 | clearFocus() 226 | } 227 | if (noteBinding.search.text.toString().isEmpty()) { 228 | observerDataChanges() 229 | } 230 | val snackBar = Snackbar.make( 231 | requireView(), "Note Deleted", Snackbar.LENGTH_LONG 232 | ).addCallback(object : BaseTransientBottomBar.BaseCallback() { 233 | @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") 234 | override fun onDismissed(transientBottomBar: Snackbar?, event: Int) { 235 | when (!actionBtnTapped) { 236 | (note?.imagePath?.isNotEmpty()) -> { 237 | val toDelete = File(note.imagePath) 238 | if (toDelete.exists()) { 239 | toDelete.delete() 240 | } 241 | } 242 | } 243 | super.onDismissed(transientBottomBar, event) 244 | } 245 | 246 | override fun onShown(transientBottomBar: Snackbar?) { 247 | transientBottomBar?.setAction("UNDO") { 248 | noteActivityViewModel.saveNote(note) 249 | noteBinding.noData.isVisible = false 250 | actionBtnTapped = true 251 | 252 | } 253 | super.onShown(transientBottomBar) 254 | } 255 | }).apply { 256 | animationMode = Snackbar.ANIMATION_MODE_FADE 257 | setAnchorView(R.id.addNoteFab) 258 | } 259 | snackBar.setActionTextColor( 260 | ContextCompat.getColor( 261 | requireContext(), 262 | R.color.yellow 263 | ) 264 | ) 265 | snackBar.show() 266 | } 267 | } 268 | val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback) 269 | itemTouchHelper.attachToRecyclerView(recyclerView) 270 | } 271 | 272 | private fun clearTxtFunction() { 273 | noteBinding.search.apply { 274 | text.clear() 275 | hideKeyboard() 276 | clearFocus() 277 | observerDataChanges() 278 | } 279 | } 280 | 281 | } //class NoteFragment closed -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/utils/ContentUriToActualFilePath.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.utils 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.os.Environment 6 | import androidx.fragment.app.FragmentActivity 7 | import java.io.* 8 | import java.text.SimpleDateFormat 9 | import java.util.* 10 | 11 | /*Converts the content Uri to InputStream 12 | and copies/writes it to file obtained from getPhotoFile() 13 | and finally returns its absolute path */ 14 | 15 | fun getImageUrlWithAuthority( 16 | context: Context, 17 | uri: Uri, 18 | activity: FragmentActivity 19 | ): String? { 20 | var inputStream: InputStream? = null 21 | if (uri.authority != null) { 22 | try { 23 | inputStream = context.contentResolver.openInputStream(uri) 24 | val file = getPhotoFile(activity) 25 | val out = FileOutputStream(file) 26 | inputStream.use { input -> 27 | out.use { 28 | input?.copyTo(it) 29 | } 30 | } 31 | return file.absolutePath 32 | } catch (e: FileNotFoundException) { 33 | e.printStackTrace() 34 | } finally { 35 | try { 36 | inputStream?.close() 37 | } catch (e: IOException) { 38 | e.printStackTrace() 39 | } 40 | } 41 | } 42 | return null 43 | } 44 | 45 | /** Creates a temp file, 46 | * this file is in app specific storage so other app cannot access it, 47 | * and starting from android 11 no file manager has access to android 48 | * directory in internal storage. 49 | *@param activity Takes a FragmentActivity 50 | *@return File - Takes a File */ 51 | fun getPhotoFile( 52 | activity: FragmentActivity 53 | ): File { 54 | val privateStorageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES) 55 | val timeStamp: String = 56 | SimpleDateFormat( 57 | "yyyy_MM_dd_HH:mm:ss", 58 | Locale.getDefault() 59 | ).format(Date()) 60 | val file = File.createTempFile( 61 | "IMG_${timeStamp}_", 62 | ".jpg", 63 | privateStorageDir 64 | ) 65 | 66 | if (Integer.parseInt(file.length().toString()) == 0) { 67 | file.delete() 68 | } 69 | return file 70 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/utils/Extensions.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.utils 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.content.Context.INPUT_METHOD_SERVICE 6 | import android.graphics.Bitmap 7 | import android.graphics.Color 8 | import android.net.Uri 9 | import android.util.Log 10 | import android.view.View 11 | import android.view.inputmethod.InputMethodManager 12 | import android.view.inputmethod.InputMethodManager.HIDE_NOT_ALWAYS 13 | import android.widget.ImageView 14 | import android.widget.Toast.LENGTH_SHORT 15 | import android.widget.Toast.makeText 16 | import androidx.annotation.AttrRes 17 | import androidx.annotation.ColorInt 18 | import androidx.core.content.res.use 19 | import com.bumptech.glide.Glide 20 | import com.bumptech.glide.load.engine.DiskCacheStrategy 21 | import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions 22 | import com.bumptech.glide.request.FutureTarget 23 | import kotlinx.coroutines.CoroutineScope 24 | import kotlinx.coroutines.CoroutineStart 25 | import kotlinx.coroutines.Dispatchers 26 | import kotlinx.coroutines.async 27 | 28 | fun View.hideKeyboard() = 29 | (context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager) 30 | .hideSoftInputFromWindow(windowToken, HIDE_NOT_ALWAYS) 31 | 32 | fun Context.loadHiRezThumbnail( 33 | uri: Uri?, 34 | image: ImageView 35 | ) = Glide.with(this) 36 | .load(uri) 37 | .override(500, 500) 38 | .diskCacheStrategy(DiskCacheStrategy.ALL) 39 | .thumbnail(0.1f) 40 | .transition(DrawableTransitionOptions.withCrossFade(200)) 41 | .into(image) 42 | 43 | suspend fun Context.asyncImageLoader( 44 | uri: Uri?, 45 | image: ImageView, 46 | job: CoroutineScope, 47 | ) { 48 | val bitmap = job.async(Dispatchers.IO, CoroutineStart.DEFAULT) { 49 | val futureTarget: FutureTarget = Glide.with(this@asyncImageLoader) 50 | .asBitmap() 51 | .load(uri) 52 | .submit(1500, 1500) 53 | return@async futureTarget.get() 54 | } 55 | try { 56 | Glide.with(this) 57 | .load(bitmap.await()) 58 | .thumbnail(0.01f) 59 | .skipMemoryCache(true) 60 | .diskCacheStrategy(DiskCacheStrategy.NONE) 61 | .transition(DrawableTransitionOptions.withCrossFade(200)) 62 | .into(image) 63 | } catch (e: IllegalArgumentException) { 64 | Log.e("asyncImageLoader", e.stackTraceToString()) 65 | } 66 | } 67 | /** 68 | * Retrieve a color from the current [android.content.res.Resources.Theme]. 69 | */ 70 | @ColorInt 71 | @SuppressLint("Recycle") 72 | fun Context.themeColor( 73 | @AttrRes themeAttrId: Int 74 | ): Int { 75 | return obtainStyledAttributes( 76 | intArrayOf(themeAttrId) 77 | ).use { 78 | it.getColor(0, Color.MAGENTA) 79 | } 80 | } 81 | 82 | fun Context.shortToast(message: String?) = makeText(this, message, LENGTH_SHORT).show() -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/utils/SwipeToDelete.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.utils 2 | 3 | import androidx.recyclerview.widget.ItemTouchHelper 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | abstract class SwipeToDelete : ItemTouchHelper.SimpleCallback( 7 | ItemTouchHelper.UP or ItemTouchHelper.DOWN, 8 | ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) { 9 | 10 | override fun onMove( 11 | recyclerView: RecyclerView, 12 | viewHolder: RecyclerView.ViewHolder, 13 | target: RecyclerView.ViewHolder 14 | ): Boolean { 15 | return true 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/viewmodel/NoteActivityViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.viewmodel 2 | 3 | 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.ViewModel 6 | import androidx.lifecycle.viewModelScope 7 | import com.gamdestroyerr.roomnote.model.Note 8 | import com.gamdestroyerr.roomnote.repository.NoteRepository 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | 12 | class NoteActivityViewModel(private val repositoryObject: NoteRepository) : ViewModel() { 13 | 14 | fun saveNote(newNote: Note) = viewModelScope.launch(Dispatchers.IO) { 15 | repositoryObject.addNote(newNote) 16 | } 17 | 18 | private var imagePath: String? = null 19 | 20 | fun saveImagePath(path: String?) { 21 | imagePath = path 22 | } 23 | 24 | fun setImagePath(): String? { 25 | if (imagePath != null) 26 | return imagePath 27 | return null 28 | } 29 | 30 | fun updateNote(existingNote: Note) = viewModelScope.launch(Dispatchers.IO) { 31 | repositoryObject.updateNote(existingNote) 32 | } 33 | 34 | fun deleteNote(existingNote: Note) = viewModelScope.launch(Dispatchers.IO) { 35 | repositoryObject.deleteNote(existingNote) 36 | } 37 | 38 | fun searchNote(query: String): LiveData> { 39 | return repositoryObject.searchNote(query) 40 | } 41 | 42 | fun getAllNotes(): LiveData> = repositoryObject.getNote() 43 | 44 | override fun onCleared() { 45 | imagePath = null 46 | super.onCleared() 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/gamdestroyerr/roomnote/viewmodel/NoteActivityViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote.viewmodel 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import com.gamdestroyerr.roomnote.repository.NoteRepository 6 | 7 | @Suppress("UNCHECKED_CAST") 8 | class NoteActivityViewModelFactory(private val repository: NoteRepository) : 9 | ViewModelProvider.NewInstanceFactory() { 10 | override fun create(modelClass: Class): T { 11 | return NoteActivityViewModel(repository) as T 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/drawable-hdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/drawable-ldpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/drawable-xxxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bottom_sheet_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/date_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/fab_btn_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_color_lens_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_format_paint_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_more_horiz_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_edit.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_no_data.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 32 | 33 | 35 | 36 | 39 | 42 | 45 | 49 | 50 | 53 | 55 | 56 | 58 | 59 | 62 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 126 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_calendar_today_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_add_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_add_a_photo_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_add_photo_alternate_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_clear_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_delete_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_round_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/note_item_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/note_item_rounded_over.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rounded_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_image.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/font/google_sans_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/font/google_sans_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/google_sans_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/font/google_sans_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/google_sans_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/font/google_sans_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bottom_sheet_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 26 | 27 | 45 | 46 | 63 | 64 | 78 | 79 | 86 | 87 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_note.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 25 | 26 | 40 | 41 | 52 | 53 | 72 | 73 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 105 | 106 | 109 | 110 | 124 | 125 | 126 | 127 | 142 | 143 | 156 | 157 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_note_content.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 35 | 36 | 54 | 55 | 72 | 73 | 74 | 75 | 84 | 85 | 89 | 90 | 97 | 98 | 112 | 113 | 127 | 128 | 129 | 130 | 131 | 132 | 142 | 143 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /app/src/main/res/layout/note_item_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 24 | 25 | 36 | 37 | 57 | 58 | 78 | 79 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /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.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 15 | 16 | 21 | 22 | 27 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #262627 4 | #3700B3 5 | #151515 6 | #FBC02D 7 | 8 | 9 | #D4C4E3 10 | #9387F5 11 | #A8DAF0 12 | #F0C390 13 | #C3F09C 14 | #F3722C 15 | #F8961E 16 | #F9844A 17 | #F9C74F 18 | #8690B1 19 | #FFCC00 20 | #FF3B30 21 | #FF9500 22 | #5AC8FA 23 | #34C759 24 | 25 | 26 | 27 | @color/white 28 | @color/card_purple 29 | @color/card_yellow 30 | @color/card_red 31 | @color/card_violet 32 | @color/card_blue 33 | @color/card_green 34 | @color/peach 35 | @color/green 36 | @color/purple 37 | @color/darkPurple 38 | @color/blue 39 | @color/orangeRed 40 | @color/yellowOrange 41 | @color/MangoTango 42 | @color/maizeCrayola 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFAA00 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Notes App 3 | My Note 4 | 5 | back 6 | save 7 | Title 8 | Note 9 | Lorem Ipsum is simply dummy text of 10 | the printing and typesetting industry. Lorem Ipsum has been 11 | the industry\'s standard dummy text ever since the 1500s, 12 | when an unknown printer took a galley of type and scrambled 13 | it to make a type specimen book. It has survived not only five 14 | centuries, but also the leap into electronic typesetting, 15 | remaining essentially unchanged. 16 | 17 | What is Lorem Ipsum? 18 | dd/mm/yyyy 19 | Edited on: %1$s 20 | Search Here… 21 | clear 22 | Select Color 23 | Add Note 24 | no data image 25 | NoteContentImage 26 | Take Picture 27 | Delete 28 | com.gamdestroyerr.roomnote.fileProvider 29 | Select Image 30 | fab 31 | New Note 32 | Add New Note 33 | New Note Shortcut Disabled 34 | deleteFab 35 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 16 | 17 | 20 | 21 | 26 | 27 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/xml-v25/shortcuts.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/fileprovider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/shortcuts.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/test/java/com/gamdestroyerr/roomnote/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.gamdestroyerr.roomnote 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 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | ext.kotlin_version = "1.4.20" 4 | repositories { 5 | google() 6 | jcenter() 7 | } 8 | dependencies { 9 | def nav_version = "2.3.0" 10 | classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" 11 | classpath 'com.android.tools.build:gradle:4.1.1' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } -------------------------------------------------------------------------------- /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 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/noob-gamedestroyer/MVVM-Notes-app/e14ee701e03a8ac8fb322807f75b5f763d78343f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Oct 18 17:08:17 IST 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | rootProject.name = "Room Note" --------------------------------------------------------------------------------