├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── discord.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ └── output-metadata.json ├── schemas │ ├── com.jjewuz.justnotes.Todos.TodoDatabase │ │ └── 4.json │ └── com.jjewuz.justnotes.notes.NoteDatabase │ │ ├── 1.json │ │ ├── 2.json │ │ ├── 3.json │ │ ├── 4.json │ │ └── 5.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jjewuz │ │ └── justnotes │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── mobilebert.tflite │ │ └── mobilebert_vocab.txt │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── jjewuz │ │ │ └── justnotes │ │ │ ├── Activities │ │ │ ├── AddCategory.kt │ │ │ ├── AddEditNoteActivity.kt │ │ │ ├── MainActivity.kt │ │ │ ├── Profile.kt │ │ │ └── SettingsActivity.kt │ │ │ ├── Category │ │ │ ├── Category.kt │ │ │ ├── CategoryAdapter.kt │ │ │ ├── CategoryDao.kt │ │ │ ├── CategoryRepository.kt │ │ │ └── CategoryViewModel.kt │ │ │ ├── FirebaseMessagingService.kt │ │ │ ├── Fragments │ │ │ ├── NotesFragment.kt │ │ │ └── TodoFragment.kt │ │ │ ├── Notes │ │ │ ├── Note.kt │ │ │ ├── NoteDatabase.kt │ │ │ ├── NoteRVAdapter.kt │ │ │ ├── NoteRepository.kt │ │ │ ├── NoteViewModel.kt │ │ │ ├── NoteWidget.kt │ │ │ └── NotesDao.kt │ │ │ ├── Notifications │ │ │ ├── NotificationHelper.kt │ │ │ └── NotificationReciever.kt │ │ │ ├── Todos │ │ │ ├── Todo.kt │ │ │ ├── TodoAdapter.kt │ │ │ ├── TodoDao.kt │ │ │ ├── TodoDatabase.kt │ │ │ └── TodoViewModel.kt │ │ │ └── Utils │ │ │ ├── GridAdapter.kt │ │ │ ├── MobileBertInterpreter.kt │ │ │ ├── TextHelper.kt │ │ │ └── Utils.kt │ └── res │ │ ├── anim │ │ ├── fade_in.xml │ │ └── fade_out.xml │ │ ├── drawable-anydpi │ │ └── ic_back.xml │ │ ├── drawable-hdpi │ │ └── ic_back.png │ │ ├── drawable-mdpi │ │ └── ic_back.png │ │ ├── drawable-night-xxxhdpi │ │ └── logo.png │ │ ├── drawable-nodpi │ │ └── example_appwidget_preview.png │ │ ├── drawable-notnight-xxxhdpi │ │ └── logo.png │ │ ├── drawable-v21 │ │ ├── app_widget_background.xml │ │ └── app_widget_inner_view_background.xml │ │ ├── drawable-v31 │ │ └── buttonc.xml │ │ ├── drawable-xhdpi │ │ └── ic_back.png │ │ ├── drawable-xxhdpi │ │ └── ic_back.png │ │ ├── drawable │ │ ├── account.xml │ │ ├── add.xml │ │ ├── ai.xml │ │ ├── analytics.xml │ │ ├── avd_anim.xml │ │ ├── backup.xml │ │ ├── bold.xml │ │ ├── bug_report.xml │ │ ├── buttonc.xml │ │ ├── check.xml │ │ ├── checklist.xml │ │ ├── circle.xml │ │ ├── clear.xml │ │ ├── clear_text.xml │ │ ├── color_cursor.xml │ │ ├── copy.xml │ │ ├── custom_popup.xml │ │ ├── delete.xml │ │ ├── down.xml │ │ ├── edit.xml │ │ ├── empty.xml │ │ ├── error.xml │ │ ├── font.xml │ │ ├── github.xml │ │ ├── important.xml │ │ ├── info.xml │ │ ├── italic.xml │ │ ├── jjewuz.xml │ │ ├── label.xml │ │ ├── load.xml │ │ ├── menu.xml │ │ ├── note.xml │ │ ├── note_notification.xml │ │ ├── opening.xml │ │ ├── palette.xml │ │ ├── person.xml │ │ ├── pin.xml │ │ ├── preview.xml │ │ ├── redo.xml │ │ ├── reminders.xml │ │ ├── rounded_corner.xml │ │ ├── rounded_dropdown_item.xml │ │ ├── save.xml │ │ ├── screenshot.xml │ │ ├── scrollbar.xml │ │ ├── security.xml │ │ ├── settings.xml │ │ ├── share.xml │ │ ├── site.xml │ │ ├── star.xml │ │ ├── strike.xml │ │ ├── swap.xml │ │ ├── tab_indicator_default.xml │ │ ├── tab_indicator_selected.xml │ │ ├── tab_selector.xml │ │ ├── tg.png │ │ ├── tonal_button_style.xml │ │ ├── translate.xml │ │ ├── under.xml │ │ ├── undo.xml │ │ ├── unload.xml │ │ ├── useful.xml │ │ ├── view.xml │ │ ├── vk.png │ │ ├── weblate.xml │ │ └── widget.xml │ │ ├── font │ │ ├── raleway.xml │ │ ├── raleway_i.ttf │ │ └── raleway_r.ttf │ │ ├── layout │ │ ├── activity_add_category.xml │ │ ├── activity_add_edit_note.xml │ │ ├── activity_main.xml │ │ ├── activity_profile.xml │ │ ├── activity_settings.xml │ │ ├── add_category_dialog.xml │ │ ├── auth_credential.xml │ │ ├── backup_card.xml │ │ ├── color_picker.xml │ │ ├── contions.xml │ │ ├── dialog_color_picker.xml │ │ ├── dialog_password.xml │ │ ├── fragment_backup.xml │ │ ├── fragment_notes.xml │ │ ├── fragment_todo.xml │ │ ├── item_category.xml │ │ ├── item_color_circle.xml │ │ ├── login_register.xml │ │ ├── note_options.xml │ │ ├── note_rv_item.xml │ │ ├── note_sheet.xml │ │ ├── note_widget.xml │ │ ├── nv_header.xml │ │ ├── option_button.xml │ │ ├── spinner_dropdown_item.xml │ │ ├── spinner_item.xml │ │ ├── theme_chooser.xml │ │ ├── todo_add.xml │ │ └── todo_item.xml │ │ ├── menu │ │ ├── formatting.xml │ │ ├── main_menu.xml │ │ ├── profile_extra.xml │ │ ├── text_editor.xml │ │ └── toolbar.xml │ │ ├── mipmap-anydpi-v26 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_monochrome.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_monochrome.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_monochrome.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_monochrome.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_background.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_monochrome.png │ │ ├── values-ar │ │ └── strings.xml │ │ ├── values-cs │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-hr │ │ └── strings.xml │ │ ├── values-land │ │ └── dimens.xml │ │ ├── values-nb-rNO │ │ └── strings.xml │ │ ├── values-night-v31 │ │ └── themes.xml │ │ ├── values-night │ │ └── themes.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-pt │ │ └── strings.xml │ │ ├── values-ru │ │ └── strings.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-v31 │ │ ├── colors.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ ├── values-vi │ │ └── strings.xml │ │ ├── values-w1240dp │ │ └── dimens.xml │ │ ├── values-w600dp │ │ └── dimens.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes.xml │ │ └── xml │ │ ├── locales_config.xml │ │ ├── note_widget_info.xml │ │ └── shortcuts.xml │ └── test │ └── java │ └── com │ └── jjewuz │ └── justnotes │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── mke2fs.conf └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: jjewuz 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. Google Pixel 8 Pro] 28 | - OS: [e.g. Android 14, MIUI 14] 29 | - App version [e.g. 5.3.0] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: jjewuz 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | google-services.json 17 | release 18 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Maxim "jjewuz" Denisov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![App logo](app/src/main/res/mipmap-mdpi/ic_launcher.png) JustNotes ![GitHub release (with filter)](https://img.shields.io/github/v/release/jjewuz/JustNotes) ![GitHub Repo stars](https://img.shields.io/github/stars/jjewuz/JustNotes) ![GitHub License](https://img.shields.io/github/license/jjewuz/JustNotes) 2 | 3 | 4 | JustNotes is simple android app for taking notes, like there have been tens of thousands before. App uses Room database. 5 | 6 | 7 | Get it on Google Play 8 | 9 | 10 | 11 | Get it on GitHub 12 | 13 | 14 | ## Features 15 | - [x] Material You Design 16 | - [x] Text formatting 17 | - [x] Import and export .txt documents 18 | - [x] Lock app with system biometry 19 | - [x] Widget for homescreen 20 | - [x] Lock notes with pin-code 21 | - [x] Local and cloud backup 22 | - [x] Change text color 23 | - [x] Labels for notes 24 | - [x] Reminders (With notifications) 25 | - [x] Notes in notifications 26 | - [x] Change note background (in 5.8.2) 27 | 28 | ## Screenshots 29 | 30 | 31 | 32 | 33 | 34 | ## App errors 35 | If you faced some app issue, you can report it with GitHub issues page. Do not forget to write the name of your device, android version, app build number, app version and steps on how to get the error. 36 | 37 | ## Translations 38 | The application is translated into different languages thanks to the community using [Weblate](https://hosted.weblate.org/projects/justnotes/). 39 | 40 | ## Channel 41 | 42 | https://t.me/jjewuzhub 43 | 44 | ## License 45 | - All code is licensed under MIT License. 46 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | id 'org.jetbrains.kotlin.android' 6 | id 'com.google.gms.google-services' 7 | id 'com.google.android.gms.oss-licenses-plugin' 8 | } 9 | 10 | android { 11 | compileSdk 35 12 | 13 | defaultConfig { 14 | applicationId "com.jjewuz.justnotes" 15 | minSdk 30 16 | targetSdk 35 17 | versionCode 210 18 | versionName "6.0.0" 19 | 20 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 21 | 22 | kapt { 23 | arguments { 24 | arg("room.schemaLocation", "$projectDir/schemas") 25 | } 26 | } 27 | } 28 | 29 | buildTypes { 30 | release { 31 | minifyEnabled true 32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 33 | signingConfig signingConfigs.debug 34 | } 35 | } 36 | compileOptions { 37 | sourceCompatibility JavaVersion.VERSION_17 38 | targetCompatibility JavaVersion.VERSION_17 39 | } 40 | kotlinOptions { 41 | jvmTarget = '17' 42 | } 43 | packagingOptions { 44 | resources { 45 | excludes += ['META-INF/atomicfu.kotlin_module'] 46 | } 47 | } 48 | buildFeatures { 49 | viewBinding true 50 | buildConfig true 51 | } 52 | namespace 'com.jjewuz.justnotes' 53 | lint { 54 | checkReleaseBuilds false 55 | } 56 | } 57 | 58 | dependencies { 59 | 60 | implementation 'androidx.core:core-ktx:1.15.0' 61 | implementation 'androidx.appcompat:appcompat:1.7.0' 62 | implementation 'com.google.android.material:material:1.12.0' 63 | implementation 'androidx.constraintlayout:constraintlayout:2.2.1' 64 | implementation 'androidx.navigation:navigation-fragment-ktx:2.8.9' 65 | implementation 'androidx.preference:preference-ktx:1.2.1' 66 | implementation 'androidx.core:core-ktx:1.15.0' 67 | implementation 'androidx.coordinatorlayout:coordinatorlayout:1.3.0' 68 | implementation 'androidx.drawerlayout:drawerlayout:1.2.0' 69 | implementation 'com.google.firebase:firebase-analytics-ktx:22.4.0' 70 | implementation 'com.google.firebase:firebase-firestore:25.1.3' 71 | implementation 'com.google.firebase:firebase-storage:21.0.1' 72 | implementation 'com.google.firebase:firebase-auth:23.2.0' 73 | implementation 'androidx.activity:activity-ktx:1.10.1' 74 | implementation 'androidx.activity:activity:1.10.1' 75 | testImplementation 'junit:junit:' 76 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 77 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 78 | implementation 'androidx.work:work-runtime-ktx:2.10.0' 79 | implementation 'com.google.android.play:app-update-ktx:2.1.0' 80 | implementation 'com.google.android.play:review-ktx:2.0.2' 81 | implementation 'com.google.android.gms:play-services-oss-licenses:17.1.0' 82 | implementation 'com.google.firebase:firebase-messaging:24.1.1' 83 | implementation "androidx.work:work-runtime-ktx:2.10.0" 84 | 85 | implementation "androidx.biometric:biometric-ktx:1.2.0-alpha05" 86 | implementation "androidx.core:core-splashscreen:1.0.1" 87 | implementation 'androidx.navigation:navigation-ui-ktx:2.8.9' 88 | implementation "androidx.viewpager2:viewpager2:1.1.0" 89 | 90 | implementation "androidx.room:room-ktx:2.6.1" 91 | kapt "androidx.room:room-compiler:2.6.1" 92 | androidTestImplementation "androidx.room:room-testing:2.6.1" 93 | 94 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7" 95 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7" 96 | implementation "androidx.lifecycle:lifecycle-common-java8:2.8.7" 97 | 98 | api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines" 99 | api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines" 100 | 101 | implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutVersion" 102 | 103 | testImplementation "junit:junit:$rootProject.junitVersion" 104 | androidTestImplementation "androidx.arch.core:core-testing:$rootProject.coreTestingVersion" 105 | androidTestImplementation("androidx.test.espresso:espresso-core:$rootProject.espressoVersion", { 106 | exclude group: 'com.android.support', module: 'support-annotations' 107 | }) 108 | androidTestImplementation "androidx.test.ext:junit:$rootProject.androidxJunitVersion" 109 | 110 | implementation 'de.raphaelebner:roomdatabasebackup:1.0.0-beta13' 111 | 112 | implementation "com.google.ai.edge.litert:litert:1.2.0" 113 | } -------------------------------------------------------------------------------- /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/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.jjewuz.justnotes", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 206, 15 | "versionName": "5.8.2", 16 | "outputFile": "app-release.apk" 17 | } 18 | ], 19 | "elementType": "File", 20 | "baselineProfiles": [ 21 | { 22 | "minApi": 28, 23 | "maxApi": 30, 24 | "baselineProfiles": [ 25 | "baselineProfiles/1/app-release.dm" 26 | ] 27 | }, 28 | { 29 | "minApi": 31, 30 | "maxApi": 2147483647, 31 | "baselineProfiles": [ 32 | "baselineProfiles/0/app-release.dm" 33 | ] 34 | } 35 | ], 36 | "minSdkVersionForDexing": 30 37 | } -------------------------------------------------------------------------------- /app/schemas/com.jjewuz.justnotes.Todos.TodoDatabase/4.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 4, 5 | "identityHash": "996ae8211b7a8dabfc7ee6e6d73b8844", 6 | "entities": [ 7 | { 8 | "tableName": "todos", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`text` TEXT NOT NULL, `is_completed` INTEGER NOT NULL, `time` TEXT NOT NULL DEFAULT '', `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "text", 13 | "columnName": "text", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "isCompleted", 19 | "columnName": "is_completed", 20 | "affinity": "INTEGER", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "setTime", 25 | "columnName": "time", 26 | "affinity": "TEXT", 27 | "notNull": true, 28 | "defaultValue": "''" 29 | }, 30 | { 31 | "fieldPath": "id", 32 | "columnName": "id", 33 | "affinity": "INTEGER", 34 | "notNull": true 35 | } 36 | ], 37 | "primaryKey": { 38 | "autoGenerate": true, 39 | "columnNames": [ 40 | "id" 41 | ] 42 | }, 43 | "indices": [], 44 | "foreignKeys": [] 45 | } 46 | ], 47 | "views": [], 48 | "setupQueries": [ 49 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 50 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '996ae8211b7a8dabfc7ee6e6d73b8844')" 51 | ] 52 | } 53 | } -------------------------------------------------------------------------------- /app/schemas/com.jjewuz.justnotes.notes.NoteDatabase/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 1, 5 | "identityHash": "47ce3b57a9748c61e5a4f24f0a901704", 6 | "entities": [ 7 | { 8 | "tableName": "notesTable", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`Title` TEXT NOT NULL, `description` TEXT NOT NULL, `timestamp` TEXT NOT NULL, `security` TEXT NOT NULL DEFAULT '', `label` TEXT NOT NULL DEFAULT '', `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "noteTitle", 13 | "columnName": "Title", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "noteDescription", 19 | "columnName": "description", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "timeStamp", 25 | "columnName": "timestamp", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "security", 31 | "columnName": "security", 32 | "affinity": "TEXT", 33 | "notNull": true, 34 | "defaultValue": "''" 35 | }, 36 | { 37 | "fieldPath": "label", 38 | "columnName": "label", 39 | "affinity": "TEXT", 40 | "notNull": true, 41 | "defaultValue": "''" 42 | }, 43 | { 44 | "fieldPath": "id", 45 | "columnName": "id", 46 | "affinity": "INTEGER", 47 | "notNull": true 48 | } 49 | ], 50 | "primaryKey": { 51 | "autoGenerate": true, 52 | "columnNames": [ 53 | "id" 54 | ] 55 | }, 56 | "indices": [], 57 | "foreignKeys": [] 58 | } 59 | ], 60 | "views": [], 61 | "setupQueries": [ 62 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 63 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '47ce3b57a9748c61e5a4f24f0a901704')" 64 | ] 65 | } 66 | } -------------------------------------------------------------------------------- /app/schemas/com.jjewuz.justnotes.notes.NoteDatabase/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 2, 5 | "identityHash": "47ce3b57a9748c61e5a4f24f0a901704", 6 | "entities": [ 7 | { 8 | "tableName": "notesTable", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`Title` TEXT NOT NULL, `description` TEXT NOT NULL, `timestamp` TEXT NOT NULL, `security` TEXT NOT NULL DEFAULT '', `label` TEXT NOT NULL DEFAULT '', `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "noteTitle", 13 | "columnName": "Title", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "noteDescription", 19 | "columnName": "description", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "timeStamp", 25 | "columnName": "timestamp", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "security", 31 | "columnName": "security", 32 | "affinity": "TEXT", 33 | "notNull": true, 34 | "defaultValue": "''" 35 | }, 36 | { 37 | "fieldPath": "label", 38 | "columnName": "label", 39 | "affinity": "TEXT", 40 | "notNull": true, 41 | "defaultValue": "''" 42 | }, 43 | { 44 | "fieldPath": "id", 45 | "columnName": "id", 46 | "affinity": "INTEGER", 47 | "notNull": true 48 | } 49 | ], 50 | "primaryKey": { 51 | "autoGenerate": true, 52 | "columnNames": [ 53 | "id" 54 | ] 55 | }, 56 | "indices": [], 57 | "foreignKeys": [] 58 | } 59 | ], 60 | "views": [], 61 | "setupQueries": [ 62 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 63 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '47ce3b57a9748c61e5a4f24f0a901704')" 64 | ] 65 | } 66 | } -------------------------------------------------------------------------------- /app/schemas/com.jjewuz.justnotes.notes.NoteDatabase/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 3, 5 | "identityHash": "47ce3b57a9748c61e5a4f24f0a901704", 6 | "entities": [ 7 | { 8 | "tableName": "notesTable", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`Title` TEXT NOT NULL, `description` TEXT NOT NULL, `timestamp` TEXT NOT NULL, `security` TEXT NOT NULL DEFAULT '', `label` TEXT NOT NULL DEFAULT '', `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "noteTitle", 13 | "columnName": "Title", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "noteDescription", 19 | "columnName": "description", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "timeStamp", 25 | "columnName": "timestamp", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "security", 31 | "columnName": "security", 32 | "affinity": "TEXT", 33 | "notNull": true, 34 | "defaultValue": "''" 35 | }, 36 | { 37 | "fieldPath": "label", 38 | "columnName": "label", 39 | "affinity": "TEXT", 40 | "notNull": true, 41 | "defaultValue": "''" 42 | }, 43 | { 44 | "fieldPath": "id", 45 | "columnName": "id", 46 | "affinity": "INTEGER", 47 | "notNull": true 48 | } 49 | ], 50 | "primaryKey": { 51 | "autoGenerate": true, 52 | "columnNames": [ 53 | "id" 54 | ] 55 | }, 56 | "indices": [], 57 | "foreignKeys": [] 58 | } 59 | ], 60 | "views": [], 61 | "setupQueries": [ 62 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 63 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '47ce3b57a9748c61e5a4f24f0a901704')" 64 | ] 65 | } 66 | } -------------------------------------------------------------------------------- /app/schemas/com.jjewuz.justnotes.notes.NoteDatabase/4.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 4, 5 | "identityHash": "0ddf46b5837f77dd030d2866aaa850a5", 6 | "entities": [ 7 | { 8 | "tableName": "notesTable", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`Title` TEXT NOT NULL, `description` TEXT NOT NULL, `timestamp` TEXT NOT NULL, `security` TEXT NOT NULL DEFAULT '', `label` TEXT NOT NULL DEFAULT '', `categoryId` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "noteTitle", 13 | "columnName": "Title", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "noteDescription", 19 | "columnName": "description", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "timeStamp", 25 | "columnName": "timestamp", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "security", 31 | "columnName": "security", 32 | "affinity": "TEXT", 33 | "notNull": true, 34 | "defaultValue": "''" 35 | }, 36 | { 37 | "fieldPath": "label", 38 | "columnName": "label", 39 | "affinity": "TEXT", 40 | "notNull": true, 41 | "defaultValue": "''" 42 | }, 43 | { 44 | "fieldPath": "categoryId", 45 | "columnName": "categoryId", 46 | "affinity": "INTEGER", 47 | "notNull": false 48 | }, 49 | { 50 | "fieldPath": "id", 51 | "columnName": "id", 52 | "affinity": "INTEGER", 53 | "notNull": true 54 | } 55 | ], 56 | "primaryKey": { 57 | "autoGenerate": true, 58 | "columnNames": [ 59 | "id" 60 | ] 61 | }, 62 | "indices": [], 63 | "foreignKeys": [] 64 | }, 65 | { 66 | "tableName": "categories", 67 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)", 68 | "fields": [ 69 | { 70 | "fieldPath": "id", 71 | "columnName": "id", 72 | "affinity": "INTEGER", 73 | "notNull": true 74 | }, 75 | { 76 | "fieldPath": "name", 77 | "columnName": "name", 78 | "affinity": "TEXT", 79 | "notNull": true 80 | } 81 | ], 82 | "primaryKey": { 83 | "autoGenerate": true, 84 | "columnNames": [ 85 | "id" 86 | ] 87 | }, 88 | "indices": [], 89 | "foreignKeys": [] 90 | } 91 | ], 92 | "views": [], 93 | "setupQueries": [ 94 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 95 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0ddf46b5837f77dd030d2866aaa850a5')" 96 | ] 97 | } 98 | } -------------------------------------------------------------------------------- /app/schemas/com.jjewuz.justnotes.notes.NoteDatabase/5.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 5, 5 | "identityHash": "04f17c700b43eb6d0a0b8c9509ddba6c", 6 | "entities": [ 7 | { 8 | "tableName": "notesTable", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`Title` TEXT NOT NULL, `description` TEXT NOT NULL, `timestamp` TEXT NOT NULL, `security` TEXT NOT NULL DEFAULT '', `label` TEXT NOT NULL DEFAULT '', `categoryId` INTEGER, `bgId` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", 10 | "fields": [ 11 | { 12 | "fieldPath": "noteTitle", 13 | "columnName": "Title", 14 | "affinity": "TEXT", 15 | "notNull": true 16 | }, 17 | { 18 | "fieldPath": "noteDescription", 19 | "columnName": "description", 20 | "affinity": "TEXT", 21 | "notNull": true 22 | }, 23 | { 24 | "fieldPath": "timeStamp", 25 | "columnName": "timestamp", 26 | "affinity": "TEXT", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "security", 31 | "columnName": "security", 32 | "affinity": "TEXT", 33 | "notNull": true, 34 | "defaultValue": "''" 35 | }, 36 | { 37 | "fieldPath": "label", 38 | "columnName": "label", 39 | "affinity": "TEXT", 40 | "notNull": true, 41 | "defaultValue": "''" 42 | }, 43 | { 44 | "fieldPath": "categoryId", 45 | "columnName": "categoryId", 46 | "affinity": "INTEGER", 47 | "notNull": false 48 | }, 49 | { 50 | "fieldPath": "bgId", 51 | "columnName": "bgId", 52 | "affinity": "INTEGER", 53 | "notNull": true, 54 | "defaultValue": "-1" 55 | }, 56 | { 57 | "fieldPath": "id", 58 | "columnName": "id", 59 | "affinity": "INTEGER", 60 | "notNull": true 61 | } 62 | ], 63 | "primaryKey": { 64 | "autoGenerate": true, 65 | "columnNames": [ 66 | "id" 67 | ] 68 | }, 69 | "indices": [], 70 | "foreignKeys": [] 71 | }, 72 | { 73 | "tableName": "categories", 74 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)", 75 | "fields": [ 76 | { 77 | "fieldPath": "id", 78 | "columnName": "id", 79 | "affinity": "INTEGER", 80 | "notNull": true 81 | }, 82 | { 83 | "fieldPath": "name", 84 | "columnName": "name", 85 | "affinity": "TEXT", 86 | "notNull": true 87 | } 88 | ], 89 | "primaryKey": { 90 | "autoGenerate": true, 91 | "columnNames": [ 92 | "id" 93 | ] 94 | }, 95 | "indices": [], 96 | "foreignKeys": [] 97 | } 98 | ], 99 | "views": [], 100 | "setupQueries": [ 101 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 102 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '04f17c700b43eb6d0a0b8c9509ddba6c')" 103 | ] 104 | } 105 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/jjewuz/justnotes/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes 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.mdenisov338.justnotesapp", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/assets/mobilebert.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjewuz/JustNotes/de1110d45184f47b24c6fd06549abc9ae48f4977/app/src/main/assets/mobilebert.tflite -------------------------------------------------------------------------------- /app/src/main/assets/mobilebert_vocab.txt: -------------------------------------------------------------------------------- 1 | [PAD] 2 | [UNK] 3 | [CLS] 4 | [SEP] 5 | [MASK] 6 | . 7 | , 8 | ? 9 | ! 10 | "" 11 | () 12 | ... 13 | - 14 | ' 15 | " 16 | [ 17 | ] 18 | { 19 | } 20 | 0 21 | 1 22 | 2 23 | 3 24 | 4 25 | 5 26 | 6 27 | 7 28 | 8 29 | 9 30 | я 31 | ты 32 | он 33 | она 34 | мы 35 | вы 36 | они 37 | это 38 | кто 39 | что 40 | где 41 | когда 42 | как 43 | почему 44 | зачем 45 | да 46 | нет 47 | может 48 | быть 49 | разработчик 50 | ##чик 51 | разработка 52 | ##ка 53 | программирование 54 | ##вание 55 | код 56 | кодер 57 | ##ер 58 | кодинг 59 | ##инг 60 | технология 61 | ##ия 62 | данные 63 | ##ные 64 | машинное 65 | ##ное 66 | обучение 67 | ##ние 68 | искусственный 69 | ##венный 70 | интеллект 71 | алгоритм 72 | ##м 73 | глубокое 74 | ##ое 75 | нейросеть 76 | нейрон 77 | анализ 78 | английский 79 | русский 80 | слово 81 | предложение 82 | модель 83 | текст 84 | данных 85 | работа 86 | система 87 | проект 88 | кодировать 89 | ##ровать 90 | команда 91 | результат 92 | интерпретатор 93 | мобильный 94 | приложение 95 | сервер 96 | клиент 97 | облако 98 | база 99 | данных 100 | файл 101 | загрузить 102 | обработать 103 | проверить 104 | сохранить 105 | читать 106 | писать 107 | начать 108 | остановить 109 | запустить 110 | протестировать 111 | проверка 112 | логика 113 | структура 114 | оптимизация 115 | 116 | hello 117 | world 118 | programming 119 | code 120 | ##ing 121 | developer 122 | development 123 | ##ment 124 | artificial 125 | intelligence 126 | ##ence 127 | machine 128 | learning 129 | ##ing 130 | data 131 | algorithm 132 | ##m 133 | deep 134 | ##p 135 | neural 136 | network 137 | analysis 138 | language 139 | english 140 | russian 141 | word 142 | sentence 143 | model 144 | text 145 | system 146 | project 147 | encode 148 | ##ode 149 | team 150 | result 151 | interpreter 152 | mobile 153 | application 154 | server 155 | client 156 | cloud 157 | database 158 | file 159 | load 160 | process 161 | check 162 | save 163 | read 164 | write 165 | start 166 | stop 167 | run 168 | test 169 | verify 170 | logic 171 | structure 172 | optimization 173 | 174 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jjewuz/JustNotes/de1110d45184f47b24c6fd06549abc9ae48f4977/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Activities/AddCategory.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Activities 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import android.os.Bundle 6 | import android.view.ViewGroup 7 | import androidx.activity.enableEdgeToEdge 8 | import androidx.appcompat.app.AppCompatActivity 9 | import androidx.core.view.ViewCompat 10 | import androidx.core.view.WindowInsetsCompat 11 | import androidx.recyclerview.widget.LinearLayoutManager 12 | import com.google.android.material.dialog.MaterialAlertDialogBuilder 13 | import com.google.android.material.textfield.TextInputEditText 14 | import com.jjewuz.justnotes.Category.Category 15 | import com.jjewuz.justnotes.Category.CategoryAdapter 16 | import com.jjewuz.justnotes.Category.CategoryViewModel 17 | import com.jjewuz.justnotes.R 18 | import com.jjewuz.justnotes.databinding.ActivityAddCategoryBinding 19 | 20 | class AddCategory : AppCompatActivity() { 21 | 22 | private lateinit var categoryViewModel: CategoryViewModel 23 | private lateinit var categoryAdapter: CategoryAdapter 24 | 25 | private lateinit var binding: ActivityAddCategoryBinding 26 | private lateinit var sharedPref: SharedPreferences 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | sharedPref = this.getSharedPreferences("prefs", Context.MODE_PRIVATE) 31 | val enabledFont = sharedPref.getBoolean("enabledFont", false) 32 | val theme = sharedPref.getString("theme", "standart") 33 | if (enabledFont and (theme=="monet")) { 34 | setTheme(R.style.AppTheme) 35 | } else if (!enabledFont and (theme=="monet")) { 36 | setTheme(R.style.FontMonet) 37 | } else if (!enabledFont and (theme=="standart")) { 38 | setTheme(R.style.Font) 39 | } else if (enabledFont and (theme=="standart")) { 40 | setTheme(R.style.Nothing) 41 | } else if (!enabledFont and (theme=="ice")){ 42 | setTheme(R.style.BlackIceFont) 43 | } else if (enabledFont and (theme=="ice")){ 44 | setTheme(R.style.BlackIce) 45 | } 46 | binding = ActivityAddCategoryBinding.inflate(layoutInflater) 47 | setContentView(binding.root) 48 | enableEdgeToEdge() 49 | 50 | setSupportActionBar(binding.topAppBar) 51 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 52 | 53 | categoryViewModel = CategoryViewModel(application) 54 | 55 | categoryAdapter = CategoryAdapter { category -> 56 | deleteCategory(category) 57 | } 58 | 59 | 60 | binding.recyclerView.adapter = categoryAdapter 61 | binding.recyclerView.layoutManager = LinearLayoutManager(this) 62 | 63 | categoryViewModel.allCategories.observe(this) { categories -> 64 | categories?.let { 65 | categoryAdapter.setCategories(it) 66 | } 67 | } 68 | 69 | binding.addCategory.setOnClickListener { 70 | addDialog() 71 | } 72 | 73 | ViewCompat.setOnApplyWindowInsetsListener(binding.addCategory) { vi, windowInsets -> 74 | val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) 75 | val params = vi.layoutParams as ViewGroup.MarginLayoutParams 76 | params.bottomMargin = insets.bottom + 20 77 | params.rightMargin = insets.right + 40 78 | vi.layoutParams = params 79 | WindowInsetsCompat.CONSUMED 80 | } 81 | } 82 | 83 | override fun onSupportNavigateUp(): Boolean { 84 | this.finish() 85 | return false 86 | } 87 | 88 | private fun addDialog(){ 89 | val view = layoutInflater.inflate(R.layout.add_category_dialog, null) 90 | val editText = view.findViewById(R.id.edit_text) 91 | MaterialAlertDialogBuilder(this) 92 | .setTitle(resources.getString(R.string.label)) 93 | .setView(view) 94 | .setNegativeButton(resources.getString(R.string.back)) { _, _ -> 95 | } 96 | .setPositiveButton(resources.getString(R.string.add)) { _, _ -> 97 | val categoryName = editText.text.toString().trim() 98 | 99 | if (categoryName.isNotEmpty()) { 100 | val category = Category(name = categoryName) 101 | categoryViewModel.insert(category) 102 | categoryAdapter.addCategory(category) 103 | } 104 | } 105 | .show() 106 | } 107 | 108 | private fun deleteCategory(category: Category) { 109 | categoryAdapter.removeCategory(category) 110 | categoryViewModel.delete(category) 111 | } 112 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Category/Category.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Category 2 | 3 | import androidx.room.Entity 4 | import androidx.room.PrimaryKey 5 | 6 | @Entity(tableName = "categories") 7 | data class Category( 8 | @PrimaryKey(autoGenerate = true) val id: Int = 0, 9 | val name: String, 10 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Category/CategoryAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Category 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.Button 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.jjewuz.justnotes.R 10 | 11 | class CategoryAdapter( 12 | private val onDeleteClick: (Category) -> Unit 13 | ) : RecyclerView.Adapter() { 14 | 15 | private var categories: MutableList = mutableListOf() 16 | 17 | inner class CategoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 18 | private val nameTextView: TextView = itemView.findViewById(R.id.nameTextView) 19 | private val deleteButton: Button = itemView.findViewById(R.id.deleteButton) 20 | 21 | fun bind(category: Category) { 22 | nameTextView.text = category.name 23 | deleteButton.setOnClickListener { 24 | onDeleteClick(category) 25 | } 26 | if (category.id == 1){ 27 | deleteButton.visibility = View.GONE 28 | } 29 | } 30 | } 31 | 32 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder { 33 | val view = LayoutInflater.from(parent.context).inflate(R.layout.item_category, parent, false) 34 | return CategoryViewHolder(view) 35 | } 36 | 37 | override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) { 38 | holder.bind(categories[position]) 39 | } 40 | 41 | override fun getItemCount() = categories.size 42 | 43 | fun setCategories(categories: List) { 44 | this.categories.clear() 45 | this.categories.addAll(categories) 46 | notifyDataSetChanged() 47 | } 48 | 49 | fun addCategory(category: Category) { 50 | categories.add(category) 51 | notifyItemInserted(categories.size - 1) 52 | } 53 | 54 | fun removeCategory(category: Category) { 55 | val index = categories.indexOf(category) 56 | if (index != 1) { 57 | categories.removeAt(index) 58 | notifyItemRemoved(index) 59 | } 60 | } 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Category/CategoryDao.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Category 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.Dao 5 | import androidx.room.Insert 6 | import androidx.room.Query 7 | 8 | @Dao 9 | interface CategoryDao { 10 | @Insert 11 | suspend fun insert(category: Category) 12 | 13 | @Query("SELECT * FROM categories") 14 | fun getAllCategories(): LiveData> 15 | 16 | @Query("DELETE FROM categories WHERE id = :categoryId") 17 | suspend fun delete(categoryId: Int) { 18 | if (categoryId != 1) { 19 | deleteCategoryByIdInternal(categoryId) 20 | } 21 | } 22 | 23 | @Query("SELECT name FROM categories WHERE id = :categoryId") 24 | suspend fun getCategoryNameById(categoryId: Int): String? 25 | 26 | @Query("DELETE FROM categories WHERE id = :categoryId") 27 | suspend fun deleteCategoryByIdInternal(categoryId: Int) 28 | 29 | @Query("UPDATE categories SET name = :name WHERE id = :categoryId") 30 | suspend fun updateCategoryName(categoryId: Long, name: String) 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Category/CategoryRepository.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Category 2 | 3 | import androidx.lifecycle.LiveData 4 | import kotlinx.coroutines.Dispatchers 5 | import kotlinx.coroutines.withContext 6 | 7 | class CategoryRepo(private val categoryDao: CategoryDao) { 8 | 9 | fun getAllCategories(): LiveData> { 10 | return categoryDao.getAllCategories() 11 | } 12 | 13 | suspend fun insert(category: Category) { 14 | withContext(Dispatchers.IO) { 15 | categoryDao.insert(category) 16 | } 17 | } 18 | 19 | suspend fun delete(category: Category){ 20 | withContext(Dispatchers.IO){ 21 | categoryDao.delete(category.id) 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Category/CategoryViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Category 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.viewModelScope 7 | import com.jjewuz.justnotes.Notes.NoteDatabase 8 | import kotlinx.coroutines.launch 9 | 10 | class CategoryViewModel(application: Application) : AndroidViewModel(application) { 11 | private val repository: CategoryRepo 12 | val allCategories: LiveData> 13 | 14 | init { 15 | val categoryDao = NoteDatabase.getDatabase(application).getCategoryDao() 16 | repository = CategoryRepo(categoryDao) 17 | allCategories = repository.getAllCategories() 18 | } 19 | 20 | fun insert(category: Category) = viewModelScope.launch { 21 | repository.insert(category) 22 | } 23 | 24 | fun delete(category: Category) = viewModelScope.launch { 25 | repository.delete(category) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/FirebaseMessagingService.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes 2 | 3 | import android.util.Log 4 | import com.google.android.gms.tasks.OnCompleteListener 5 | import com.google.firebase.messaging.FirebaseMessaging 6 | import com.google.firebase.messaging.FirebaseMessagingService 7 | import com.google.firebase.messaging.RemoteMessage 8 | 9 | class MyFirebaseMessagingService : FirebaseMessagingService() { 10 | 11 | override fun onMessageReceived(remoteMessage: RemoteMessage) { 12 | super.onMessageReceived(remoteMessage) 13 | 14 | if (remoteMessage.data.isNotEmpty()) { 15 | // Handle the data message here. 16 | } 17 | 18 | remoteMessage.notification?.let { 19 | // Handle the notification message here. 20 | } 21 | 22 | FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -> 23 | if (!task.isSuccessful) { 24 | Log.w("TOKEN", "Fetching FCM registration token failed", task.exception) 25 | return@OnCompleteListener 26 | } 27 | 28 | val token = task.result 29 | Log.d("TOKEN", token) 30 | }) 31 | } 32 | 33 | override fun onNewToken(token: String) { 34 | Log.d("TOKEN", "Refreshed token: $token") 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/Note.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity( 8 | tableName = "notesTable", 9 | ) 10 | 11 | class Note ( 12 | @ColumnInfo(name = "Title") val noteTitle: String, 13 | @ColumnInfo(name = "description") val noteDescription: String, 14 | @ColumnInfo(name = "timestamp") val timeStamp: String, 15 | @ColumnInfo(name = "security", defaultValue = "") val security: String, 16 | @ColumnInfo(name = "label", defaultValue = "") val label: String, 17 | @ColumnInfo(name = "categoryId") val categoryId: Int? = null, 18 | @ColumnInfo(name = "bgId", defaultValue = "-1") val bgId: Int 19 | ){ 20 | @PrimaryKey(autoGenerate = true) 21 | var id = 0 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/NoteDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import android.content.Context 4 | import androidx.room.AutoMigration 5 | import androidx.room.Database 6 | import androidx.room.Room 7 | import androidx.room.RoomDatabase 8 | import androidx.room.migration.Migration 9 | import androidx.sqlite.db.SupportSQLiteDatabase 10 | import com.jjewuz.justnotes.Category.Category 11 | import com.jjewuz.justnotes.Category.CategoryDao 12 | import com.jjewuz.justnotes.R 13 | import kotlinx.coroutines.CoroutineScope 14 | import kotlinx.coroutines.Dispatchers 15 | import kotlinx.coroutines.launch 16 | 17 | @Database(entities = [Note::class, Category::class], version = 5, exportSchema = true, autoMigrations = [ AutoMigration (1,2), AutoMigration(2,3), AutoMigration(4,5)]) 18 | abstract class NoteDatabase : RoomDatabase() { 19 | 20 | abstract fun getNotesDao(): NotesDao 21 | abstract fun getCategoryDao(): CategoryDao 22 | 23 | companion object { 24 | @Volatile 25 | private var INSTANCE: NoteDatabase? = null 26 | 27 | fun getDatabase(context: Context): NoteDatabase { 28 | 29 | return INSTANCE ?: synchronized(this) { 30 | val instance = Room.databaseBuilder( 31 | context.applicationContext, 32 | NoteDatabase::class.java, 33 | "note_database" 34 | ).addCallback(object : Callback() { 35 | override fun onCreate(db: SupportSQLiteDatabase) { 36 | super.onCreate(db) 37 | CoroutineScope(Dispatchers.IO).launch { 38 | getDatabase(context).getCategoryDao().insert(Category(name = "Unlabeled")) 39 | } 40 | } 41 | override fun onOpen(db: SupportSQLiteDatabase) { 42 | super.onOpen(db) 43 | CoroutineScope(Dispatchers.IO).launch { 44 | val defaultCategoryName = context.getString(R.string.no_label) 45 | val dao = getDatabase(context).getCategoryDao() 46 | dao.updateCategoryName(1, defaultCategoryName) 47 | } 48 | } 49 | }) 50 | .addMigrations(MIGRATION_3_4) 51 | .build() 52 | INSTANCE = instance 53 | instance 54 | } 55 | 56 | } 57 | 58 | private val MIGRATION_3_4 = object : Migration(3, 4) { 59 | override fun migrate(db: SupportSQLiteDatabase) { 60 | // Выполняем миграцию для добавления categoryId 61 | db.execSQL(""" 62 | CREATE TABLE IF NOT EXISTS `categories` ( 63 | `id` INTEGER PRIMARY KEY NOT NULL, 64 | `name` TEXT NOT NULL 65 | ) 66 | """.trimIndent()) 67 | 68 | // Добавление столбца categoryId в таблицу notes 69 | db.execSQL("ALTER TABLE notesTable ADD COLUMN categoryId INTEGER") 70 | 71 | db.execSQL(""" 72 | CREATE TABLE IF NOT EXISTS `notesTable` ( 73 | `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 74 | `Title` TEXT NOT NULL, 75 | `description` TEXT NOT NULL, 76 | `timestamp` TEXT NOT NULL, 77 | `security` TEXT DEFAULT '', 78 | `label` TEXT DEFAULT '', 79 | `categoryId` INTEGER 80 | ) 81 | """.trimIndent()) 82 | 83 | db.execSQL(""" 84 | INSERT INTO categories (id, name) VALUES (1, 'Unlabeled') 85 | """.trimIndent()) 86 | } 87 | } 88 | 89 | } 90 | 91 | } 92 | 93 | fun getSelectedNoteID(context: Context): Int{ 94 | val sharedPref = context.getSharedPreferences("widget_prefs", Context.MODE_PRIVATE) 95 | return sharedPref.getInt("note_id", -1) 96 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/NoteRVAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import android.content.res.Configuration 6 | import android.content.res.Resources 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.TextView 11 | import androidx.core.content.ContextCompat 12 | import androidx.core.graphics.toColorInt 13 | import androidx.recyclerview.widget.RecyclerView 14 | import com.google.android.material.card.MaterialCardView 15 | import com.google.android.material.chip.Chip 16 | import com.jjewuz.justnotes.Category.CategoryDao 17 | import com.jjewuz.justnotes.R 18 | import com.jjewuz.justnotes.Utils.Utils 19 | import kotlinx.coroutines.CoroutineScope 20 | import kotlinx.coroutines.Dispatchers 21 | import kotlinx.coroutines.launch 22 | import kotlinx.coroutines.withContext 23 | import java.text.SimpleDateFormat 24 | import java.util.Locale 25 | 26 | class NoteRVAdapter( 27 | val context: Context, 28 | private val noteClickInterface: NoteClickInterface, 29 | private val noteLongClickInterface: NoteLongClickInterface, 30 | private val categoryDao: CategoryDao 31 | ) : 32 | RecyclerView.Adapter() { 33 | 34 | private lateinit var sharedPref: SharedPreferences 35 | 36 | private val allNotes = ArrayList() 37 | 38 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 39 | val noteTV: TextView = itemView.findViewById(R.id.idTVNote) 40 | val descTV: TextView = itemView.findViewById(R.id.notedesc) 41 | val dateTV: TextView = itemView.findViewById(R.id.idTVDate) 42 | val textContainer: MaterialCardView = itemView.findViewById(R.id.textcontainer) 43 | val categoryChip: MaterialCardView = itemView.findViewById(R.id.category) 44 | val categoryText: TextView = itemView.findViewById(R.id.category_tv) 45 | } 46 | 47 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 48 | val itemView = LayoutInflater.from(parent.context).inflate( 49 | R.layout.note_rv_item, 50 | parent, false 51 | ) 52 | 53 | return ViewHolder(itemView) 54 | } 55 | 56 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 57 | sharedPref = holder.descTV.context.getSharedPreferences("prefs", Context.MODE_PRIVATE) 58 | val isPreview = sharedPref.getBoolean("enabledPreview", false) 59 | 60 | holder.noteTV.text = allNotes[position].noteTitle 61 | val sdf = SimpleDateFormat("dd.MM.yyyy - HH:mm", Locale.getDefault()) 62 | val date = allNotes[position].timeStamp 63 | val currentDateAndTime: String = try { 64 | sdf.format(date.toLong()) 65 | } catch (e: Exception){ 66 | date 67 | } 68 | 69 | holder.dateTV.text = context.getString(R.string.lastedit) + currentDateAndTime 70 | val currCategory = allNotes[position].categoryId 71 | holder.categoryChip.visibility = View.GONE 72 | if(currCategory != 1) { 73 | CoroutineScope(Dispatchers.Main).launch { 74 | val categoryName = currCategory?.let { getCategoryName(it) } 75 | holder.categoryChip.visibility = View.VISIBLE 76 | holder.categoryText.text = categoryName 77 | } 78 | 79 | } 80 | 81 | val desc = Utils.fromHtml(allNotes[position].noteDescription.take(400)) 82 | 83 | 84 | if (isPreview){ 85 | holder.descTV.text = desc 86 | } else{ 87 | holder.descTV.visibility = View.GONE 88 | holder.textContainer.visibility = View.GONE 89 | } 90 | 91 | if (desc.isEmpty()) 92 | holder.textContainer.visibility = View.GONE 93 | 94 | holder.itemView.setOnClickListener { 95 | noteClickInterface.onNoteClick(allNotes[position], position) 96 | } 97 | 98 | holder.itemView.setOnLongClickListener { 99 | noteLongClickInterface.onNoteLongClick(allNotes[position]) 100 | return@setOnLongClickListener true 101 | } 102 | } 103 | 104 | private suspend fun getCategoryName(categoryId: Int): String? { 105 | return withContext(Dispatchers.IO) { 106 | categoryDao.getCategoryNameById(categoryId) 107 | } 108 | } 109 | 110 | override fun getItemCount(): Int { 111 | return allNotes.size 112 | } 113 | 114 | fun updateList(newList: List) { 115 | 116 | allNotes.clear() 117 | allNotes.addAll(newList) 118 | notifyDataSetChanged() 119 | } 120 | } 121 | 122 | 123 | interface NoteClickInterface { 124 | fun onNoteClick(note: Note, num: Int) 125 | } 126 | 127 | interface NoteLongClickInterface { 128 | fun onNoteLongClick(note: Note) 129 | } 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/NoteRepository.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import androidx.lifecycle.LiveData 4 | 5 | class NoteRepository(private val notesDao: NotesDao) { 6 | 7 | val allNotes: LiveData> = notesDao.getAllSortedByTime() 8 | 9 | fun getLabeled(id: Int): LiveData> { 10 | return notesDao.getNotesByCategory(id) 11 | } 12 | 13 | fun getQuery(query: String): LiveData> { 14 | return notesDao.showSearch(query) 15 | } 16 | 17 | fun getNotes(): LiveData> { 18 | return notesDao.getAllSortedByTime() 19 | } 20 | 21 | suspend fun insert(note: Note) { 22 | notesDao.insert(note) 23 | } 24 | 25 | suspend fun delete(note: Note){ 26 | notesDao.delete(note) 27 | } 28 | 29 | suspend fun update(note: Note){ 30 | notesDao.update(note) 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/NoteViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.viewModelScope 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.launch 9 | 10 | class NoteViewModal (application: Application) :AndroidViewModel(application) { 11 | 12 | private val allNotes : LiveData> 13 | private val repository : NoteRepository 14 | 15 | init { 16 | val dao = NoteDatabase.getDatabase(application).getNotesDao() 17 | repository = NoteRepository(dao) 18 | allNotes = repository.allNotes 19 | } 20 | 21 | fun getLabel(categoryId: Int): LiveData> { 22 | return repository.getLabeled(categoryId) 23 | } 24 | 25 | fun getQuery(query: String): LiveData>{ 26 | return repository.getQuery(query) 27 | } 28 | 29 | fun getNotes(): LiveData>{ 30 | return repository.getNotes() 31 | } 32 | 33 | fun deleteNote (note: Note) = viewModelScope.launch(Dispatchers.IO) { 34 | repository.delete(note) 35 | } 36 | 37 | fun updateNote(note: Note) = viewModelScope.launch(Dispatchers.IO) { 38 | repository.update(note) 39 | } 40 | 41 | fun addNote(note: Note) = viewModelScope.launch(Dispatchers.IO) { 42 | repository.insert(note) 43 | } 44 | 45 | 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/NoteWidget.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import android.app.PendingIntent 4 | import android.appwidget.AppWidgetManager 5 | import android.appwidget.AppWidgetProvider 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.widget.RemoteViews 9 | import androidx.room.Room 10 | import com.jjewuz.justnotes.Activities.MainActivity 11 | import com.jjewuz.justnotes.R 12 | import com.jjewuz.justnotes.Utils.Utils 13 | import kotlinx.coroutines.Dispatchers 14 | import kotlinx.coroutines.GlobalScope 15 | import kotlinx.coroutines.launch 16 | 17 | class NoteWidget : AppWidgetProvider() { 18 | 19 | override fun onUpdate( 20 | context: Context, 21 | appWidgetManager: AppWidgetManager, 22 | appWidgetIds: IntArray 23 | ) { 24 | val noteId = getSelectedNoteID(context) 25 | for (appWidgetId in appWidgetIds) { 26 | getNoteTextFromDB(context, noteId) { textNote, noteDesc -> updateAppWidget(context, appWidgetManager, appWidgetId, textNote, noteDesc) } 27 | } 28 | } 29 | 30 | override fun onEnabled(context: Context) { 31 | // Enter relevant functionality for when the first widget is created 32 | } 33 | 34 | override fun onDisabled(context: Context) { 35 | // Enter relevant functionality for when the last widget is disabled 36 | } 37 | 38 | private fun getNoteTextFromDB(context: Context, noteId: Int, callback: (String, String) -> Unit){ 39 | val database = Room.databaseBuilder(context, NoteDatabase::class.java, "note_database").build() 40 | val empty = context.resources.getString(R.string.empty) 41 | val desc = context.resources.getString(R.string.no_note) 42 | 43 | GlobalScope.launch(Dispatchers.IO) { 44 | val note = database.getNotesDao().getNoteById(noteId) 45 | val noteText = note?.noteTitle ?: empty 46 | val noteDesc = note?.noteDescription ?: desc 47 | 48 | callback(noteText, noteDesc) 49 | } 50 | } 51 | } 52 | 53 | internal fun updateAppWidget( 54 | context: Context, 55 | appWidgetManager: AppWidgetManager, 56 | appWidgetId: Int, 57 | noteText: String, 58 | noteDesc: String, 59 | ) { 60 | val views = RemoteViews(context.packageName, R.layout.note_widget) 61 | views.setTextViewText(R.id.appwidget_text, noteText) 62 | views.setTextViewText(R.id.widgetdesc_text, Utils.fromHtml(noteDesc)) 63 | 64 | val intent = Intent(context, MainActivity::class.java) 65 | val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) 66 | views.setOnClickPendingIntent(R.id.background, pendingIntent) 67 | 68 | appWidgetManager.updateAppWidget(appWidgetId, views) 69 | } -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notes/NotesDao.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notes 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.Dao 5 | import androidx.room.Delete 6 | import androidx.room.Insert 7 | import androidx.room.OnConflictStrategy 8 | import androidx.room.Query 9 | import androidx.room.Update 10 | 11 | 12 | @Dao 13 | interface NotesDao { 14 | @Insert(onConflict = OnConflictStrategy.IGNORE) 15 | suspend fun insert(note : Note) 16 | 17 | @Delete 18 | suspend fun delete(note: Note) 19 | 20 | @Query("Select * from notesTable order by id ASC") 21 | fun getAllNotes(): LiveData> 22 | 23 | @Query("Select * from notesTable where id = :noteId") 24 | fun getNoteById(noteId: Int): Note? 25 | 26 | @Query("Select * from notesTable Order By timeStamp DESC " ) 27 | fun getAllSortedByTime(): LiveData> 28 | 29 | @Query("SELECT * FROM notesTable WHERE categoryId = :categoryId") 30 | fun getNotesByCategory(categoryId: Int): LiveData> 31 | 32 | @Query("Select * from notesTable where Title like '%' || :search || '%'") 33 | fun showSearch(search: String?): LiveData> 34 | 35 | @Update 36 | suspend fun update(note: Note) 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notifications/NotificationHelper.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notifications 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.AlarmManager 5 | import android.app.PendingIntent 6 | import android.content.Context 7 | import android.content.Intent 8 | import java.time.LocalDateTime 9 | import java.util.Calendar 10 | 11 | class NotificationHelper(private val context: Context) { 12 | 13 | @SuppressLint("ScheduleExactAlarm") 14 | fun createNotification(title: String, message: String, id: Int, dateTime: LocalDateTime) { 15 | 16 | val intent = Intent(context, NotificationReceiver::class.java) 17 | intent.putExtra("title", title) 18 | intent.putExtra("message", message) 19 | intent.putExtra("id", id) 20 | 21 | val pendingIntent = PendingIntent.getBroadcast( 22 | context, 23 | id, 24 | intent, 25 | PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT 26 | ) 27 | 28 | val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager 29 | 30 | val calendar = Calendar.getInstance().apply { 31 | set(Calendar.YEAR, dateTime.year) 32 | set(Calendar.MONTH, dateTime.monthValue - 1) 33 | set(Calendar.DAY_OF_MONTH, dateTime.dayOfMonth) 34 | set(Calendar.HOUR_OF_DAY, dateTime.hour) 35 | set(Calendar.MINUTE, dateTime.minute) 36 | set(Calendar.SECOND, 0) 37 | } 38 | val alarmTime = calendar.timeInMillis 39 | 40 | alarmManager.setExact(AlarmManager.RTC_WAKEUP, alarmTime, pendingIntent) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Notifications/NotificationReciever.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Notifications 2 | 3 | import android.app.NotificationManager 4 | import android.content.BroadcastReceiver 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.util.Log 8 | import androidx.core.content.ContextCompat 9 | import com.jjewuz.justnotes.Utils.Utils 10 | 11 | 12 | class NotificationReceiver : BroadcastReceiver() { 13 | override fun onReceive(context: Context, intent: Intent) { 14 | when (intent.action) { 15 | "com.jjewuz.NOTIFICATION_DELETED" -> { 16 | val serviceIntent = Intent(context, Utils.PersistentService::class.java).apply { 17 | putExtra("noteId", intent.getIntExtra("noteId", -1)) 18 | putExtra("noteTitle", intent.getStringExtra("noteTitle")) 19 | putExtra("noteDescription", intent.getStringExtra("noteDescription")) 20 | } 21 | Log.d("NotesService", "Open note with ${intent.getStringExtra("noteTitle")}") 22 | ContextCompat.startForegroundService(context, serviceIntent) 23 | } 24 | 25 | "com.jjewuz.NOTIFICATION_HIDE" -> { 26 | val noteId = intent.getIntExtra("noteId", -1) 27 | val notificationManager = 28 | context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 29 | notificationManager.cancel(noteId) 30 | Log.d("NotesService", "Closed note with $noteId") 31 | } 32 | } 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Todos/Todo.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Todos 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "todos") 8 | data class Todo( 9 | @ColumnInfo(name = "text") val text: String, 10 | @ColumnInfo(name = "is_completed") var isCompleted: Boolean, 11 | @ColumnInfo(name = "time", defaultValue = "") var setTime: String 12 | ) { 13 | @PrimaryKey(autoGenerate = true) 14 | @ColumnInfo(name = "id") 15 | var id: Int = 0 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Todos/TodoAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Todos 2 | 3 | import android.graphics.Paint 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.CheckBox 8 | import android.widget.TextView 9 | import androidx.lifecycle.ViewModelProvider 10 | import androidx.lifecycle.ViewModelStoreOwner 11 | import androidx.recyclerview.widget.RecyclerView 12 | import com.jjewuz.justnotes.R 13 | 14 | class TodoAdapter(private val viewModelStoreOwner: ViewModelStoreOwner, var todos: List, val todoClickInterface: TodoClickInterface, private val todoLongClickInterface: TodoLongClickInterface) : RecyclerView.Adapter() { 15 | class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 16 | val titleTextView: TextView = itemView.findViewById(R.id.titleTextView) 17 | val completedCheckBox: CheckBox = itemView.findViewById(R.id.completedCheckBox) 18 | val timeText: TextView = itemView.findViewById(R.id.time_set) 19 | } 20 | 21 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 22 | val view = LayoutInflater.from(parent.context).inflate(R.layout.todo_item, parent, false) 23 | return ViewHolder(view) 24 | } 25 | 26 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 27 | val todo = todos[position] 28 | holder.titleTextView.text = todo.text 29 | holder.completedCheckBox.isChecked = todo.isCompleted 30 | 31 | holder.timeText.text = todo.setTime 32 | 33 | if (todo.isCompleted){ 34 | holder.titleTextView.paintFlags = Paint.STRIKE_THRU_TEXT_FLAG 35 | } else { 36 | holder.titleTextView.paintFlags = 0 37 | } 38 | 39 | 40 | val todoViewModel = ViewModelProvider(viewModelStoreOwner)[TodoViewModel::class.java] 41 | 42 | holder.completedCheckBox.setOnCheckedChangeListener { _, isChecked -> 43 | todo.isCompleted = isChecked 44 | todoViewModel.update(todo) 45 | } 46 | 47 | holder.itemView.setOnClickListener{ 48 | todo.isCompleted = !todo.isCompleted 49 | todoViewModel.update(todo) 50 | } 51 | 52 | holder.itemView.setOnLongClickListener { 53 | todoLongClickInterface.onTodoLongClick(todo) 54 | return@setOnLongClickListener true 55 | } 56 | } 57 | 58 | override fun getItemCount(): Int { 59 | return todos.size 60 | } 61 | } 62 | 63 | interface TodoClickInterface { 64 | fun onTodoClick(todo: Todo) 65 | } 66 | 67 | interface TodoLongClickInterface { 68 | fun onTodoLongClick(todo: Todo) 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Todos/TodoDao.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Todos 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.Dao 5 | import androidx.room.Delete 6 | import androidx.room.Insert 7 | import androidx.room.Query 8 | import androidx.room.Update 9 | 10 | @Dao 11 | interface TodoDao { 12 | @Query("SELECT * FROM todos ORDER BY id") 13 | fun getAllTodos(): LiveData> 14 | 15 | @Insert 16 | suspend fun insert(todo: Todo) 17 | 18 | @Update 19 | suspend fun update(todo: Todo) 20 | 21 | @Delete 22 | suspend fun delete(todo: Todo) 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Todos/TodoDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Todos 2 | 3 | import android.content.Context 4 | import androidx.room.AutoMigration 5 | import androidx.room.Database 6 | import androidx.room.Room 7 | import androidx.room.RoomDatabase 8 | 9 | @Database(entities = [Todo::class], version = 4, exportSchema = true, autoMigrations = [ AutoMigration (3,4)]) 10 | abstract class TodoDatabase : RoomDatabase() { 11 | abstract fun todoDao(): TodoDao 12 | 13 | companion object { 14 | @Volatile 15 | private var INSTANCE: TodoDatabase? = null 16 | 17 | fun getDatabase(context: Context): TodoDatabase { 18 | val tempInstance = INSTANCE 19 | if (tempInstance != null) { 20 | return tempInstance 21 | } 22 | synchronized(this) { 23 | val instance = Room.databaseBuilder( 24 | context.applicationContext, 25 | TodoDatabase::class.java, 26 | "todo_database" 27 | ) .fallbackToDestructiveMigration() 28 | .build() 29 | INSTANCE = instance 30 | return instance 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Todos/TodoViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Todos 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.AndroidViewModel 5 | import androidx.lifecycle.LiveData 6 | import androidx.lifecycle.viewModelScope 7 | import com.jjewuz.justnotes.Notes.NoteDatabase 8 | import com.jjewuz.justnotes.Notes.NoteRepository 9 | import kotlinx.coroutines.Dispatchers 10 | import kotlinx.coroutines.launch 11 | 12 | class TodoViewModel(application: Application) : AndroidViewModel(application) { 13 | private val todoDao = TodoDatabase.getDatabase(application).todoDao() 14 | val allTodos: LiveData> 15 | 16 | init { 17 | val dao = TodoDatabase.getDatabase(application).todoDao() 18 | allTodos = dao.getAllTodos() 19 | } 20 | 21 | fun insert(todo: Todo) { 22 | viewModelScope.launch(Dispatchers.IO) { 23 | todoDao.insert(todo) 24 | } 25 | } 26 | 27 | fun update(todo: Todo) { 28 | viewModelScope.launch(Dispatchers.IO) { 29 | todoDao.update(todo) 30 | } 31 | } 32 | 33 | fun delete(todo: Todo) { 34 | viewModelScope.launch(Dispatchers.IO) { 35 | todoDao.delete(todo) 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/jjewuz/justnotes/Utils/GridAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.jjewuz.justnotes.Utils 2 | 3 | import android.content.Context 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.recyclerview.widget.RecyclerView 10 | import com.google.android.material.card.MaterialCardView 11 | import com.jjewuz.justnotes.R 12 | 13 | data class Option(val icon: Int, val title: String, val action: String) 14 | 15 | 16 | class GridAdapter( 17 | private val context: Context, 18 | private val options: List