├── .gitignore ├── .idea ├── .gitignore ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── deploymentTargetSelector.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.thatsmanmeet.taskyapp.room.TodoDatabase │ │ ├── 2.json │ │ ├── 3.json │ │ └── 4.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── thatsmanmeet │ │ └── tasky │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── ic_logo-playstore.png │ ├── java │ │ └── com │ │ │ └── thatsmanmeet │ │ │ └── taskyapp │ │ │ ├── MainActivity.kt │ │ │ ├── components │ │ │ ├── ActionDialogBox.kt │ │ │ ├── AddTodoDialog.kt │ │ │ ├── DateHeader.kt │ │ │ ├── DatePicker.kt │ │ │ ├── DeletedTodoItem.kt │ │ │ ├── MaterialDatePicker.kt │ │ │ ├── MaterialTimePicker.kt │ │ │ ├── MyTopAppBar.kt │ │ │ ├── NavigationDrawer.kt │ │ │ ├── NotesHeader.kt │ │ │ ├── NotesItem.kt │ │ │ ├── OpenEditTodoDialog.kt │ │ │ ├── SearchBarTop.kt │ │ │ ├── SettingsComponent.kt │ │ │ ├── SettingsHeader.kt │ │ │ ├── SettingsToggleItem.kt │ │ │ ├── TaskCompleteAnimations.kt │ │ │ ├── TaskList.kt │ │ │ ├── ThemeDialog.kt │ │ │ ├── TimePicker.kt │ │ │ └── TodoItemCard.kt │ │ │ ├── constants │ │ │ └── Constants.kt │ │ │ ├── datastore │ │ │ └── SettingsStore.kt │ │ │ ├── misc │ │ │ ├── AppTheme.kt │ │ │ └── Recursor.kt │ │ │ ├── notification │ │ │ └── Notification.kt │ │ │ ├── receiver │ │ │ ├── BootCompletedReceiver.kt │ │ │ └── RepeatingTasksReceiver.kt │ │ │ ├── room │ │ │ ├── Todo.kt │ │ │ ├── TodoDao.kt │ │ │ ├── TodoDatabase.kt │ │ │ ├── TodoRepository.kt │ │ │ ├── TodoViewModel.kt │ │ │ ├── deletedtodo │ │ │ │ ├── DeletedTodo.kt │ │ │ │ ├── DeletedTodoDao.kt │ │ │ │ ├── DeletedTodoDatabase.kt │ │ │ │ ├── DeletedTodoRepository.kt │ │ │ │ └── DeletedTodoViewModel.kt │ │ │ └── notes │ │ │ │ ├── Note.kt │ │ │ │ ├── NoteDao.kt │ │ │ │ ├── NoteDatabase.kt │ │ │ │ ├── NoteRepository.kt │ │ │ │ └── NoteViewModel.kt │ │ │ ├── screens │ │ │ ├── AboutScreen.kt │ │ │ ├── AddNoteScreen.kt │ │ │ ├── App.kt │ │ │ ├── DeletedTodoScreen.kt │ │ │ ├── EditNoteScreen.kt │ │ │ ├── NavGraph.kt │ │ │ ├── NotesScreen.kt │ │ │ ├── PermissionRequestScreen.kt │ │ │ ├── Screen.kt │ │ │ └── SettingsScreen.kt │ │ │ ├── ui │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── viewmodels │ │ │ └── MainViewModel.kt │ └── res │ │ ├── drawable │ │ ├── cross_icon.png │ │ ├── ic_add_task.xml │ │ ├── ic_battery.xml │ │ ├── ic_check.xml │ │ ├── ic_code.xml │ │ ├── ic_dark.xml │ │ ├── ic_history.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_light.xml │ │ ├── ic_logo_foreground.xml │ │ ├── ic_notes.xml │ │ ├── ic_phone.xml │ │ ├── ic_refresh.xml │ │ ├── ic_security.xml │ │ ├── icon.xml │ │ ├── search_icon32.png │ │ └── search_icon64.png │ │ ├── font │ │ ├── poppins_bold.ttf │ │ ├── poppins_light.ttf │ │ ├── poppins_medium.ttf │ │ ├── poppins_regular.ttf │ │ └── poppins_thin.ttf │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_logo.xml │ │ └── ic_logo_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_logo.png │ │ └── ic_logo_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_logo.png │ │ └── ic_logo_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_logo.png │ │ └── ic_logo_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_logo.png │ │ └── ic_logo_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_logo.png │ │ └── ic_logo_round.png │ │ ├── raw │ │ ├── completed.mp3 │ │ ├── confetti.json │ │ ├── deleted.mp3 │ │ ├── notification.mp3 │ │ ├── notifications.mp3 │ │ └── side.json │ │ ├── values-cs │ │ └── strings.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-sk │ │ └── strings.xml │ │ ├── values-tr │ │ └── strings.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_logo_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── thatsmanmeet │ └── tasky │ └── ExampleUnitTest.kt ├── build.gradle ├── fastlane └── metadata │ └── android │ ├── de │ ├── full_description.txt │ └── short_description.txt │ ├── en-US │ ├── changelogs │ │ ├── 20.txt │ │ └── 6.txt │ ├── full_description.txt │ ├── images │ │ ├── featureGraphic.jpg │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── tasky1.jpg │ │ │ ├── tasky2.jpg │ │ │ ├── tasky3.jpg │ │ │ ├── tasky4.jpg │ │ │ ├── tasky5.jpg │ │ │ └── tasky6.jpg │ └── short_description.txt │ └── fr-FR │ ├── Courte_description.txt │ └── Longue_description.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images └── banner.png ├── license ├── screenshots ├── tasky1.png ├── tasky2.png ├── tasky3.png └── tasky4.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Tasky -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 119 | 120 | 122 | 123 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 68 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ⚠️ Development Update : EOL 3 | 4 | Tasky is now EOL and hence I'm dropping all the issues & pull requests on github as not planned. 5 | 6 | ![Banner](./images/banner.png) 7 | # Tasky 8 | Tasky is a highly intuitive and easy-to-use todo list application designed for those who seek a powerful yet minimalistic way to manage their daily tasks. 9 | 10 | ## Features ⭐️ 11 | 12 | 1. Modern and minimal design 13 | 2. Notifications for tasks 14 | 3. Notes feature 15 | 3. Material You Dynamic theming 16 | 4. Repeating tasks everyday. 17 | 5. Cool task completion animation 18 | 6. Add and Delete Sounds 19 | 7. Completely offline making it privacy first 20 | 8. Free and Open Source 21 | 9. Offline Backups 22 | 23 | ## Screenshot 🌠 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 | ## Download ⬇️ 32 | 33 | Get it on Google Play 34 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | android { 8 | namespace 'com.thatsmanmeet.taskyapp' 9 | compileSdk 35 10 | 11 | defaultConfig { 12 | applicationId "com.thatsmanmeet.taskyapp" 13 | minSdk 26 14 | targetSdk 35 15 | versionCode 28 16 | versionName "3.0.1" 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | vectorDrawables { 19 | useSupportLibrary true 20 | } 21 | 22 | kapt { 23 | arguments{ 24 | arg("room.schemaLocation","$projectDir/schemas") 25 | } 26 | } 27 | 28 | } 29 | 30 | buildTypes { 31 | release { 32 | minifyEnabled true 33 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 34 | } 35 | debug { 36 | minifyEnabled true 37 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 38 | } 39 | } 40 | compileOptions { 41 | sourceCompatibility "17" 42 | targetCompatibility JavaVersion.VERSION_17 43 | } 44 | kotlinOptions { 45 | jvmTarget = '17' 46 | } 47 | buildFeatures { 48 | compose true 49 | } 50 | composeOptions { 51 | kotlinCompilerExtensionVersion '1.5.15' 52 | } 53 | packagingOptions { 54 | resources { 55 | excludes += '/META-INF/{AL2.0,LGPL2.1}' 56 | } 57 | } 58 | } 59 | 60 | dependencies { 61 | implementation 'androidx.core:core-ktx:1.15.0' 62 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7' 63 | implementation 'androidx.activity:activity-compose:1.10.1' 64 | implementation "androidx.compose.ui:ui:$compose_version" 65 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" 66 | implementation 'androidx.compose.material3:material3:1.3.1' 67 | implementation 'androidx.media3:media3-common:1.5.1' 68 | testImplementation 'junit:junit:4.13.2' 69 | androidTestImplementation 'androidx.test.ext:junit:1.2.1' 70 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' 71 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" 72 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" 73 | debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" 74 | def room_version = "2.6.1" 75 | implementation "androidx.room:room-runtime:$room_version" 76 | implementation "androidx.room:room-ktx:$room_version" 77 | implementation "androidx.compose.runtime:runtime-livedata:1.7.8" 78 | annotationProcessor "androidx.room:room-compiler:$room_version" 79 | kapt "androidx.room:room-compiler:$room_version" 80 | implementation 'androidx.core:core-splashscreen:1.0.1' 81 | implementation "androidx.navigation:navigation-compose:2.8.9" 82 | implementation "androidx.datastore:datastore-preferences:1.1.3" 83 | def lottieVersion = "6.0.0" 84 | implementation "com.airbnb.android:lottie-compose:$lottieVersion" 85 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -dontwarn okio.** 23 | -dontwarn okhttp3.** 24 | -dontwarn com.squareup.okhttp3.** 25 | -keep class com.squareup.okhttp3.** { *; } 26 | -keep interface com.squareup.okhttp3.** { *; } 27 | -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.thatsmanmeet.taskyapp", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 27, 15 | "versionName": "3.0.0", 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": 26 37 | } -------------------------------------------------------------------------------- /app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 2, 5 | "identityHash": "4380ff6fe7dfa67dd0521d82dce95af4", 6 | "entities": [ 7 | { 8 | "tableName": "todo_table", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `completed` INTEGER NOT NULL, `date` TEXT, `time` TEXT)", 10 | "fields": [ 11 | { 12 | "fieldPath": "ID", 13 | "columnName": "ID", 14 | "affinity": "INTEGER", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "title", 19 | "columnName": "title", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "isCompleted", 25 | "columnName": "completed", 26 | "affinity": "INTEGER", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "TEXT", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "time", 37 | "columnName": "time", 38 | "affinity": "TEXT", 39 | "notNull": false 40 | } 41 | ], 42 | "primaryKey": { 43 | "autoGenerate": true, 44 | "columnNames": [ 45 | "ID" 46 | ] 47 | }, 48 | "indices": [], 49 | "foreignKeys": [] 50 | } 51 | ], 52 | "views": [], 53 | "setupQueries": [ 54 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 55 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4380ff6fe7dfa67dd0521d82dce95af4')" 56 | ] 57 | } 58 | } -------------------------------------------------------------------------------- /app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/3.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 3, 5 | "identityHash": "0fc0df9706e8458703d801ec28679abb", 6 | "entities": [ 7 | { 8 | "tableName": "todo_table", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `completed` INTEGER NOT NULL, `date` TEXT, `time` TEXT, `notificationID` INTEGER NOT NULL DEFAULT 0, `is_Recurring` INTEGER NOT NULL DEFAULT false)", 10 | "fields": [ 11 | { 12 | "fieldPath": "ID", 13 | "columnName": "ID", 14 | "affinity": "INTEGER", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "title", 19 | "columnName": "title", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "isCompleted", 25 | "columnName": "completed", 26 | "affinity": "INTEGER", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "TEXT", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "time", 37 | "columnName": "time", 38 | "affinity": "TEXT", 39 | "notNull": false 40 | }, 41 | { 42 | "fieldPath": "notificationID", 43 | "columnName": "notificationID", 44 | "affinity": "INTEGER", 45 | "notNull": true, 46 | "defaultValue": "0" 47 | }, 48 | { 49 | "fieldPath": "isRecurring", 50 | "columnName": "is_Recurring", 51 | "affinity": "INTEGER", 52 | "notNull": true, 53 | "defaultValue": "false" 54 | } 55 | ], 56 | "primaryKey": { 57 | "autoGenerate": true, 58 | "columnNames": [ 59 | "ID" 60 | ] 61 | }, 62 | "indices": [], 63 | "foreignKeys": [] 64 | } 65 | ], 66 | "views": [], 67 | "setupQueries": [ 68 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 69 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0fc0df9706e8458703d801ec28679abb')" 70 | ] 71 | } 72 | } -------------------------------------------------------------------------------- /app/schemas/com.thatsmanmeet.taskyapp.room.TodoDatabase/4.json: -------------------------------------------------------------------------------- 1 | { 2 | "formatVersion": 1, 3 | "database": { 4 | "version": 4, 5 | "identityHash": "ba7463236f237b93f55f7180888e06e3", 6 | "entities": [ 7 | { 8 | "tableName": "todo_table", 9 | "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`ID` INTEGER PRIMARY KEY AUTOINCREMENT, `title` TEXT, `completed` INTEGER NOT NULL, `date` TEXT, `time` TEXT, `notificationID` INTEGER NOT NULL DEFAULT 0, `is_Recurring` INTEGER NOT NULL DEFAULT false, `description` TEXT DEFAULT '')", 10 | "fields": [ 11 | { 12 | "fieldPath": "ID", 13 | "columnName": "ID", 14 | "affinity": "INTEGER", 15 | "notNull": false 16 | }, 17 | { 18 | "fieldPath": "title", 19 | "columnName": "title", 20 | "affinity": "TEXT", 21 | "notNull": false 22 | }, 23 | { 24 | "fieldPath": "isCompleted", 25 | "columnName": "completed", 26 | "affinity": "INTEGER", 27 | "notNull": true 28 | }, 29 | { 30 | "fieldPath": "date", 31 | "columnName": "date", 32 | "affinity": "TEXT", 33 | "notNull": false 34 | }, 35 | { 36 | "fieldPath": "time", 37 | "columnName": "time", 38 | "affinity": "TEXT", 39 | "notNull": false 40 | }, 41 | { 42 | "fieldPath": "notificationID", 43 | "columnName": "notificationID", 44 | "affinity": "INTEGER", 45 | "notNull": true, 46 | "defaultValue": "0" 47 | }, 48 | { 49 | "fieldPath": "isRecurring", 50 | "columnName": "is_Recurring", 51 | "affinity": "INTEGER", 52 | "notNull": true, 53 | "defaultValue": "false" 54 | }, 55 | { 56 | "fieldPath": "todoDescription", 57 | "columnName": "description", 58 | "affinity": "TEXT", 59 | "notNull": false, 60 | "defaultValue": "''" 61 | } 62 | ], 63 | "primaryKey": { 64 | "autoGenerate": true, 65 | "columnNames": [ 66 | "ID" 67 | ] 68 | }, 69 | "indices": [], 70 | "foreignKeys": [] 71 | } 72 | ], 73 | "views": [], 74 | "setupQueries": [ 75 | "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", 76 | "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ba7463236f237b93f55f7180888e06e3')" 77 | ] 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/thatsmanmeet/tasky/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.tasky 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.thatsmanmeet.tasky", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/ic_logo-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/ic_logo-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/ActionDialogBox.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.material3.AlertDialog 4 | import androidx.compose.material3.Button 5 | import androidx.compose.material3.ButtonColors 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.MutableState 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.graphics.Color 12 | 13 | @Composable 14 | fun ActionDialogBox( 15 | modifier: Modifier = Modifier, 16 | isDialogShowing: MutableState, 17 | title: String, 18 | message:String, 19 | confirmButtonText:String, 20 | dismissButtonText:String, 21 | onConfirmClick : () -> Unit, 22 | onDismissClick : () -> Unit, 23 | confirmButtonColor:Color = MaterialTheme.colorScheme.primary, 24 | confirmButtonContentColor:Color = MaterialTheme.colorScheme.onPrimary 25 | ) { 26 | if(isDialogShowing.value){ 27 | AlertDialog( 28 | onDismissRequest = { 29 | isDialogShowing.value = false 30 | }, 31 | confirmButton = { 32 | Button(onClick = { 33 | onConfirmClick() 34 | isDialogShowing.value = false 35 | }, colors = ButtonColors(containerColor = confirmButtonColor, contentColor = confirmButtonContentColor, disabledContainerColor = Color.Gray, disabledContentColor = Color.White)) { 36 | Text(text = confirmButtonText) 37 | } 38 | }, 39 | dismissButton = { 40 | Button(onClick = { 41 | onDismissClick() 42 | isDialogShowing.value = false 43 | }) { 44 | Text(text = dismissButtonText) 45 | } 46 | }, 47 | title = { 48 | Text(text = title) 49 | }, 50 | text = { 51 | Text(text = message) 52 | } 53 | ) 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/DateHeader.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.* 5 | import androidx.compose.material.icons.Icons 6 | import androidx.compose.material.icons.filled.DateRange 7 | import androidx.compose.material3.Icon 8 | import androidx.compose.material3.MaterialTheme 9 | import androidx.compose.material3.Text 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.unit.dp 14 | import androidx.compose.ui.unit.sp 15 | 16 | @Composable 17 | fun DateHeader( 18 | modifier: Modifier = Modifier, 19 | date:String 20 | ) { 21 | Row( 22 | modifier = modifier 23 | .background(MaterialTheme.colorScheme.surface) 24 | .padding(bottom = 10.dp, top = 0.dp) 25 | .fillMaxWidth(), 26 | verticalAlignment = Alignment.CenterVertically 27 | ) { 28 | Icon( 29 | imageVector = Icons.Default.DateRange, 30 | contentDescription = null, 31 | tint = MaterialTheme.colorScheme.primary 32 | ) 33 | Spacer(modifier = modifier.width(8.dp)) 34 | Text( 35 | text = date, 36 | color = MaterialTheme.colorScheme.primary, 37 | fontSize = 20.sp 38 | ) 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/DatePicker.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import android.app.DatePickerDialog 4 | import android.content.Context 5 | import android.widget.DatePicker 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.runtime.MutableState 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.compose.runtime.remember 10 | import java.util.* 11 | 12 | @Composable 13 | fun showDatePicker( 14 | context: Context, 15 | isShowing : MutableState 16 | ) : String{ 17 | val year: Int 18 | val month: Int 19 | val day: Int 20 | val calendar = Calendar.getInstance() 21 | year = calendar.get(Calendar.YEAR) 22 | month = calendar.get(Calendar.MONTH) 23 | day = calendar.get(Calendar.DAY_OF_MONTH) 24 | calendar.time = Date() 25 | val date = remember { mutableStateOf("") } 26 | val datePickerDialog = DatePickerDialog( 27 | context, 28 | { _: DatePicker, currentYear: Int, currentMonth: Int, dayOfMonth: Int -> 29 | date.value = "$dayOfMonth/${currentMonth+1}/$currentYear" 30 | isShowing.value = false 31 | }, year, month, day 32 | ) 33 | datePickerDialog.datePicker.minDate = calendar.timeInMillis 34 | if(isShowing.value){ 35 | datePickerDialog.show() 36 | } 37 | return date.value 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/DeletedTodoItem.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import android.content.Context 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.clickable 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.fillMaxWidth 9 | import androidx.compose.foundation.layout.heightIn 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.shape.RoundedCornerShape 12 | import androidx.compose.material3.MaterialTheme 13 | import androidx.compose.material3.Text 14 | import androidx.compose.runtime.Composable 15 | import androidx.compose.runtime.mutableStateOf 16 | import androidx.compose.runtime.remember 17 | import androidx.compose.ui.Alignment 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.draw.clip 20 | import androidx.compose.ui.platform.LocalContext 21 | import androidx.compose.ui.text.style.TextDecoration 22 | import androidx.compose.ui.unit.dp 23 | import androidx.compose.ui.unit.sp 24 | import com.thatsmanmeet.taskyapp.room.Todo 25 | import com.thatsmanmeet.taskyapp.room.TodoViewModel 26 | import com.thatsmanmeet.taskyapp.room.deletedtodo.DeletedTodo 27 | import com.thatsmanmeet.taskyapp.room.deletedtodo.DeletedTodoViewModel 28 | import com.thatsmanmeet.taskyapp.screens.currentDateTimeComparator 29 | import com.thatsmanmeet.taskyapp.screens.scheduleNotification 30 | 31 | 32 | @Composable 33 | fun DeletedTodoItem( 34 | deletedTodo: DeletedTodo, 35 | todoViewModel: TodoViewModel, 36 | deletedTodoViewModel: DeletedTodoViewModel, 37 | context:Context = LocalContext.current, 38 | modifier: Modifier = Modifier 39 | ) { 40 | val isShowing = remember { 41 | mutableStateOf(false) 42 | } 43 | Row( 44 | modifier = modifier 45 | .fillMaxWidth() 46 | .padding(10.dp) 47 | .heightIn(60.dp) 48 | .clip(RoundedCornerShape(10.dp)) 49 | .clickable { 50 | isShowing.value = true 51 | } 52 | .background(MaterialTheme.colorScheme.inverseOnSurface), 53 | verticalAlignment = Alignment.CenterVertically 54 | ) { 55 | Box(modifier = modifier 56 | .fillMaxWidth() 57 | .padding(10.dp)){ 58 | Text( 59 | text = deletedTodo.title!!, 60 | fontSize = 20.sp, 61 | textDecoration = if(deletedTodo.isCompleted) TextDecoration.LineThrough else TextDecoration.None 62 | ) 63 | } 64 | } 65 | if(isShowing.value){ 66 | ActionDialogBox( 67 | isDialogShowing = isShowing, 68 | title = "Choose Action", 69 | message = "Do you want to restore or delete this task?", 70 | confirmButtonText = "Restore" , 71 | dismissButtonText = "Delete", 72 | onConfirmClick = { 73 | val newTodo = Todo( 74 | ID = deletedTodo.ID, 75 | title = deletedTodo.title, 76 | todoDescription = deletedTodo.todoDescription, 77 | isCompleted = deletedTodo.isCompleted, 78 | date = deletedTodo.date, 79 | time = deletedTodo.time, 80 | notificationID = deletedTodo.notificationID, 81 | isRecurring = deletedTodo.isRecurring 82 | ) 83 | todoViewModel.insertTodo(newTodo) 84 | if(newTodo.time!!.isNotEmpty()){ 85 | currentDateTimeComparator(inputDate = newTodo.date!!, inputTime = newTodo.time!!) { 86 | scheduleNotification( 87 | context = context, 88 | titleText = newTodo.title!!, 89 | messageText = newTodo.todoDescription!!, 90 | time = "${newTodo.date} ${newTodo.time}", 91 | todo = newTodo 92 | ) 93 | } 94 | } 95 | deletedTodoViewModel.deleteDeletedTodo(deletedTodo) 96 | }, 97 | onDismissClick = { 98 | deletedTodoViewModel.deleteDeletedTodo(deletedTodo) 99 | }, 100 | 101 | ) 102 | } 103 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/MaterialDatePicker.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.material3.DatePicker 4 | import androidx.compose.material3.DatePickerDialog 5 | import androidx.compose.material3.ExperimentalMaterial3Api 6 | import androidx.compose.material3.Text 7 | import androidx.compose.material3.TextButton 8 | import androidx.compose.material3.rememberDatePickerState 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.MutableState 11 | import androidx.compose.runtime.derivedStateOf 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.ui.Modifier 14 | import java.text.SimpleDateFormat 15 | import java.util.* 16 | 17 | @OptIn(ExperimentalMaterial3Api::class) 18 | @Composable 19 | fun MaterialDatePicker( 20 | isShowing: MutableState, 21 | modifier: Modifier = Modifier, 22 | onDateSelected: (String) -> Unit 23 | ) { 24 | val currentDate = Calendar.getInstance().timeInMillis 25 | val datePickerState = rememberDatePickerState(initialSelectedDateMillis = currentDate) 26 | val confirmEnabled = remember { 27 | derivedStateOf { datePickerState.selectedDateMillis != null } 28 | } 29 | val dateFormat = remember { SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) } 30 | 31 | if (isShowing.value) { 32 | DatePickerDialog( 33 | onDismissRequest = { 34 | isShowing.value = false 35 | }, 36 | confirmButton = { 37 | TextButton( 38 | onClick = { 39 | isShowing.value = false 40 | val dateResult = if (datePickerState.selectedDateMillis != null) { 41 | val date = Date(datePickerState.selectedDateMillis!!) 42 | dateFormat.format(date) 43 | } else { 44 | "No Selection" 45 | } 46 | onDateSelected(dateResult) 47 | 48 | }, 49 | enabled = confirmEnabled.value 50 | ) { 51 | Text(text = "Select") 52 | } 53 | }, 54 | dismissButton = { 55 | TextButton(onClick = { isShowing.value = false }) { 56 | Text(text = "Cancel") 57 | } 58 | } 59 | ) { 60 | DatePicker(state = datePickerState) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/MaterialTimePicker.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import android.content.Context 4 | import androidx.compose.material3.AlertDialog 5 | import androidx.compose.material3.ExperimentalMaterial3Api 6 | import androidx.compose.material3.Text 7 | import androidx.compose.material3.TextButton 8 | import androidx.compose.material3.TimePicker 9 | import androidx.compose.material3.rememberTimePickerState 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.runtime.MutableState 12 | import androidx.compose.ui.Modifier 13 | import java.time.LocalTime 14 | import java.time.format.DateTimeFormatter 15 | 16 | @OptIn(ExperimentalMaterial3Api::class) 17 | @Composable 18 | fun MaterialTimePicker( 19 | context: Context, 20 | isShowing: MutableState, 21 | modifier: Modifier = Modifier, 22 | is24HourField:Boolean, 23 | onTimeSelected: (String) -> Unit 24 | ) { 25 | val currentTime = LocalTime.now() 26 | val timePickerState = rememberTimePickerState( 27 | is24Hour = is24HourField , 28 | initialHour = currentTime.hour, 29 | initialMinute = currentTime.minute 30 | ) 31 | 32 | 33 | val timeFormatter = DateTimeFormatter.ofPattern("HH:mm") 34 | 35 | if (isShowing.value) { 36 | AlertDialog( 37 | onDismissRequest = { 38 | isShowing.value = false 39 | }, 40 | title = { Text(text = "Select Time") }, 41 | text = { 42 | TimePicker( 43 | state = timePickerState, 44 | modifier = modifier 45 | ) 46 | }, 47 | confirmButton = { 48 | TextButton( 49 | onClick = { 50 | isShowing.value = false 51 | val selectedHour = timePickerState.hour 52 | val selectedMinute = timePickerState.minute 53 | val time = LocalTime.of(selectedHour, selectedMinute) 54 | val formattedTime = time.format(timeFormatter) 55 | onTimeSelected(formattedTime) 56 | }, 57 | ) { 58 | Text(text = "Select") 59 | } 60 | }, 61 | dismissButton = { 62 | TextButton(onClick = { isShowing.value = false }) { 63 | Text(text = "Cancel") 64 | } 65 | } 66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/MyTopAppBar.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.material.icons.Icons 7 | import androidx.compose.material.icons.filled.Menu 8 | import androidx.compose.material3.DrawerState 9 | import androidx.compose.material3.ExperimentalMaterial3Api 10 | import androidx.compose.material3.Icon 11 | import androidx.compose.material3.IconButton 12 | import androidx.compose.material3.MaterialTheme 13 | import androidx.compose.material3.Text 14 | import androidx.compose.material3.TopAppBar 15 | import androidx.compose.material3.TopAppBarDefaults 16 | import androidx.compose.runtime.Composable 17 | import androidx.compose.runtime.MutableState 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.unit.sp 21 | import kotlinx.coroutines.CoroutineScope 22 | import kotlinx.coroutines.launch 23 | 24 | @Composable 25 | @OptIn(ExperimentalMaterial3Api::class) 26 | fun MyTopAppBar( 27 | title:String = "Tasky", 28 | coroutineScope: CoroutineScope, 29 | drawerState: DrawerState, 30 | modifier: Modifier, 31 | searchText: MutableState, 32 | topAppBarColors: TopAppBarDefaults 33 | ) { 34 | TopAppBar( 35 | navigationIcon = { 36 | IconButton(onClick = { 37 | coroutineScope.launch { 38 | drawerState.apply { 39 | if (isClosed) open() else close() 40 | } 41 | } 42 | }) { 43 | Icon( 44 | imageVector = Icons.Default.Menu, 45 | contentDescription = null, 46 | tint = MaterialTheme.colorScheme.onPrimary 47 | ) 48 | } 49 | }, 50 | title = { 51 | Row( 52 | modifier = modifier.fillMaxWidth(), 53 | verticalAlignment = Alignment.CenterVertically, 54 | horizontalArrangement = Arrangement.SpaceBetween 55 | ) { 56 | Text( 57 | text = title, 58 | fontSize = 25.sp 59 | ) 60 | SearchBarTop(searchText.value) { searchText.value = it } 61 | } 62 | }, 63 | colors = topAppBarColors.topAppBarColors( 64 | containerColor = MaterialTheme.colorScheme.primary, 65 | titleContentColor = MaterialTheme.colorScheme.onPrimary 66 | ) 67 | ) 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/NavigationDrawer.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.foundation.layout.size 8 | import androidx.compose.material.icons.Icons 9 | import androidx.compose.material.icons.filled.Delete 10 | import androidx.compose.material.icons.filled.Info 11 | import androidx.compose.material.icons.filled.Settings 12 | import androidx.compose.material3.DrawerState 13 | import androidx.compose.material3.HorizontalDivider 14 | import androidx.compose.material3.Icon 15 | import androidx.compose.material3.ModalDrawerSheet 16 | import androidx.compose.material3.ModalNavigationDrawer 17 | import androidx.compose.material3.NavigationDrawerItem 18 | import androidx.compose.material3.Text 19 | import androidx.compose.runtime.Composable 20 | import androidx.compose.ui.Alignment 21 | import androidx.compose.ui.Modifier 22 | import androidx.compose.ui.res.painterResource 23 | import androidx.compose.ui.unit.dp 24 | import androidx.navigation.NavHostController 25 | import com.thatsmanmeet.taskyapp.BuildConfig 26 | import com.thatsmanmeet.taskyapp.R 27 | import com.thatsmanmeet.taskyapp.screens.Screen 28 | import kotlinx.coroutines.CoroutineScope 29 | import kotlinx.coroutines.launch 30 | 31 | @Composable 32 | fun NavigationDrawer( 33 | modifier: Modifier = Modifier, 34 | title:String = "Tasky", 35 | navHostController: NavHostController, 36 | coroutineScope: CoroutineScope, 37 | drawerState: DrawerState, 38 | content : @Composable () -> Unit 39 | ) { 40 | ModalNavigationDrawer( 41 | drawerState = drawerState, 42 | gesturesEnabled = true, 43 | drawerContent = { 44 | ModalDrawerSheet { 45 | Row( 46 | modifier = modifier 47 | .fillMaxWidth() 48 | .padding(16.dp), 49 | horizontalArrangement = Arrangement.SpaceBetween, 50 | verticalAlignment = Alignment.CenterVertically 51 | ) { 52 | Text(title) 53 | Text(text = "V${BuildConfig.VERSION_NAME}") 54 | } 55 | HorizontalDivider() 56 | NavigationDrawerItem( 57 | modifier = modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp), 58 | icon = { Icon(painter = painterResource(id = R.drawable.ic_check),modifier = modifier.size(20.dp), contentDescription = null) }, 59 | label = { Text(text = "Tasks", modifier = modifier.padding(start = 4.dp)) }, 60 | selected = false, 61 | onClick = { 62 | coroutineScope.launch { 63 | drawerState.close() 64 | } 65 | navHostController.navigate(route = Screen.MyApp.route) { 66 | this.popUpTo(Screen.MyApp.route) { 67 | inclusive = true 68 | } 69 | } 70 | } 71 | ) 72 | NavigationDrawerItem( 73 | modifier = modifier.padding(start = 16.dp, end = 16.dp), 74 | icon = { Icon(painter = painterResource(id = R.drawable.ic_notes),modifier = modifier.size(20.dp), contentDescription = null) }, 75 | label = { 76 | Row { 77 | Text(text = "Notes (Beta)", modifier = modifier.padding(start = 4.dp)) 78 | } 79 | }, 80 | selected = false, 81 | onClick = { 82 | coroutineScope.launch { 83 | drawerState.close() 84 | } 85 | navHostController.navigate(route = Screen.NotesScreen.route) 86 | } 87 | ) 88 | NavigationDrawerItem( 89 | modifier = modifier.padding(start = 16.dp, end = 16.dp), 90 | icon = { Icon(imageVector = Icons.Default.Delete, contentDescription = null) }, 91 | label = { Text(text = "Recycle Bin") }, 92 | selected = false, 93 | onClick = { 94 | coroutineScope.launch { 95 | drawerState.close() 96 | } 97 | navHostController.navigate(route = Screen.DeletedTodosScreen.route) 98 | } 99 | ) 100 | NavigationDrawerItem( 101 | modifier = modifier.padding(start = 16.dp, end = 16.dp), 102 | icon = { Icon(imageVector = Icons.Default.Info, contentDescription = null) }, 103 | label = { Text(text = "About") }, 104 | selected = false, 105 | onClick = { 106 | coroutineScope.launch { 107 | drawerState.close() 108 | } 109 | navHostController.navigate(route = Screen.AboutScreen.route) 110 | } 111 | ) 112 | NavigationDrawerItem( 113 | modifier = modifier.padding(bottom = 16.dp,start = 16.dp, end = 16.dp), 114 | icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null) }, 115 | label = { Text(text = "Settings") }, 116 | selected = false, 117 | onClick = { 118 | coroutineScope.launch { 119 | drawerState.close() 120 | } 121 | navHostController.navigate(route = Screen.SettingsScreen.route) 122 | } 123 | ) 124 | } 125 | } 126 | ){ 127 | content() 128 | } 129 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/NotesHeader.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.foundation.layout.width 9 | import androidx.compose.material3.MaterialTheme 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.unit.sp 16 | 17 | @Composable 18 | fun NotesHeader( 19 | modifier: Modifier = Modifier, 20 | text:String, 21 | iconContent: @Composable () -> Unit 22 | ) { 23 | Row( 24 | modifier = modifier 25 | .background(MaterialTheme.colorScheme.surface) 26 | .padding(bottom = 10.dp, top = 0.dp) 27 | .fillMaxWidth(), 28 | verticalAlignment = Alignment.CenterVertically 29 | ) { 30 | iconContent() 31 | Spacer(modifier = modifier.width(8.dp)) 32 | Text( 33 | text = text, 34 | color = MaterialTheme.colorScheme.primary, 35 | fontSize = 20.sp 36 | ) 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/NotesItem.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.height 9 | import androidx.compose.foundation.layout.heightIn 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.shape.RoundedCornerShape 12 | import androidx.compose.material3.Card 13 | import androidx.compose.material3.MaterialTheme 14 | import androidx.compose.material3.Text 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.draw.clip 18 | import androidx.compose.ui.text.font.FontWeight 19 | import androidx.compose.ui.unit.dp 20 | import androidx.compose.ui.unit.sp 21 | import androidx.navigation.NavHostController 22 | import com.thatsmanmeet.taskyapp.room.notes.Note 23 | 24 | 25 | @Composable 26 | fun NotesItem(modifier: Modifier = Modifier, note: Note,navHostController:NavHostController) { 27 | Card ( 28 | modifier = modifier 29 | .fillMaxWidth() 30 | .clickable { 31 | navHostController.navigate(route = "edit_notes_screen/" + note.ID) 32 | } 33 | .clip(RoundedCornerShape(10.dp)) 34 | .background(MaterialTheme.colorScheme.inverseOnSurface) 35 | .heightIn(min = 60.dp), 36 | ) { 37 | Column( 38 | modifier = modifier.padding(10.dp) 39 | ) { 40 | Text( 41 | text = if (note.title.isNullOrEmpty()) "Empty Title" else note.title!!, 42 | fontSize = 16.sp, 43 | fontWeight = FontWeight.Bold, 44 | maxLines = 1 45 | ) 46 | Spacer(modifier = modifier.height(8.dp)) 47 | Text( 48 | text = if (note.body.isNullOrEmpty()) { 49 | "Empty Body" 50 | } else if (note.body!!.length < 50) { 51 | note.body!! 52 | } else { 53 | note.body!!.slice(0..49) + "..." 54 | }, fontSize = 12.sp) 55 | Spacer(modifier = modifier.height(8.dp)) 56 | Text(text = "Last updated: ${note.date!!}", fontSize = 10.sp) 57 | } 58 | } 59 | Spacer(modifier = modifier.height(10.dp)) 60 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/SearchBarTop.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.width 10 | import androidx.compose.foundation.shape.RoundedCornerShape 11 | import androidx.compose.material.icons.Icons 12 | import androidx.compose.material.icons.filled.Clear 13 | import androidx.compose.material.icons.filled.Search 14 | import androidx.compose.material3.Icon 15 | import androidx.compose.material3.IconButton 16 | import androidx.compose.material3.Text 17 | import androidx.compose.material3.TextField 18 | import androidx.compose.runtime.Composable 19 | import androidx.compose.runtime.LaunchedEffect 20 | import androidx.compose.runtime.getValue 21 | import androidx.compose.runtime.mutableStateOf 22 | import androidx.compose.runtime.remember 23 | import androidx.compose.runtime.saveable.rememberSaveable 24 | import androidx.compose.runtime.setValue 25 | import androidx.compose.runtime.snapshotFlow 26 | import androidx.compose.ui.Alignment 27 | import androidx.compose.ui.Modifier 28 | import androidx.compose.ui.focus.FocusRequester 29 | import androidx.compose.ui.focus.focusRequester 30 | import androidx.compose.ui.platform.LocalWindowInfo 31 | import androidx.compose.ui.text.TextStyle 32 | import androidx.compose.ui.text.font.FontFamily 33 | import androidx.compose.ui.text.style.TextMotion 34 | import androidx.compose.ui.tooling.preview.Preview 35 | import androidx.compose.ui.unit.dp 36 | import androidx.compose.ui.unit.sp 37 | 38 | @Composable 39 | fun SearchBarTop(searchText: String, onValueChange: (String)->Unit) { 40 | var searchBarVisible by rememberSaveable { 41 | mutableStateOf(false) 42 | } 43 | //Auto keyboard focus 44 | val windowInfo = LocalWindowInfo.current 45 | val focusRequester = remember { FocusRequester() } 46 | 47 | Row( 48 | horizontalArrangement = Arrangement.End, 49 | modifier = Modifier.padding(horizontal = 10.dp) 50 | .fillMaxWidth(0.8f) 51 | ) { 52 | if(!searchBarVisible){ 53 | IconButton( 54 | onClick = { 55 | searchBarVisible = true 56 | } 57 | ) { 58 | Icon(Icons.Default.Search, contentDescription = "Search") 59 | } 60 | } 61 | else{ 62 | 63 | TextField( 64 | value = searchText,//searchText , 65 | onValueChange = onValueChange, 66 | modifier = Modifier 67 | .fillMaxWidth(1f) 68 | .align(Alignment.CenterVertically) 69 | .padding(top = 5.dp, bottom = 5.dp) 70 | .focusRequester(focusRequester), 71 | textStyle = TextStyle( 72 | textMotion = TextMotion.Animated, 73 | fontFamily = FontFamily.Monospace, 74 | fontSize = 18.sp 75 | ), 76 | shape = RoundedCornerShape(30.dp), 77 | leadingIcon = { 78 | Icon( 79 | Icons.Default.Search, 80 | contentDescription = "Search", 81 | modifier = Modifier.height(20.dp).width(20.dp) 82 | ) 83 | }, 84 | trailingIcon = { 85 | IconButton(onClick = { 86 | if (searchText.isNotEmpty()) { 87 | onValueChange("") 88 | } else { 89 | searchBarVisible = false 90 | } 91 | }) { 92 | Icon( 93 | Icons.Default.Clear, 94 | contentDescription = "Clear", 95 | modifier = Modifier.height(20.dp).width(20.dp) 96 | ) 97 | } 98 | }, 99 | placeholder = { Text(text = "Search") }, 100 | singleLine = true 101 | ) 102 | LaunchedEffect(windowInfo) { 103 | snapshotFlow { windowInfo.isWindowFocused }.collect { isWindowFocused -> 104 | if (isWindowFocused) { 105 | focusRequester.requestFocus() 106 | } 107 | } 108 | } 109 | } 110 | } 111 | } 112 | @Preview(showBackground = true) 113 | @Composable 114 | fun SearchBarTopPreview() { 115 | SearchBarTop(" ") {} 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/SettingsComponent.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.Spacer 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.height 11 | import androidx.compose.foundation.layout.heightIn 12 | import androidx.compose.foundation.layout.padding 13 | import androidx.compose.foundation.layout.size 14 | import androidx.compose.foundation.shape.RoundedCornerShape 15 | import androidx.compose.material3.Card 16 | import androidx.compose.material3.CardDefaults 17 | import androidx.compose.material3.Icon 18 | import androidx.compose.material3.MaterialTheme 19 | import androidx.compose.material3.Text 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.draw.clip 24 | import androidx.compose.ui.res.painterResource 25 | import androidx.compose.ui.unit.dp 26 | import androidx.compose.ui.unit.sp 27 | 28 | 29 | @Composable 30 | fun SettingsComponent( 31 | modifier: Modifier = Modifier, 32 | settingHeaderText: String, 33 | settingText:String, 34 | painterResourceID: Int, 35 | clickable: ()-> Unit 36 | ) { 37 | 38 | Spacer(modifier = modifier.height(12.dp)) 39 | Card( 40 | modifier = modifier 41 | .heightIn(56.dp) 42 | .clip(RoundedCornerShape(15.dp)) 43 | .fillMaxWidth() 44 | .background(MaterialTheme.colorScheme.inverseOnSurface) 45 | .clickable { 46 | clickable() 47 | }, 48 | elevation = CardDefaults.cardElevation(0.dp) 49 | ) { 50 | Row( 51 | modifier = modifier 52 | .padding(10.dp) 53 | .fillMaxWidth(), 54 | verticalAlignment = Alignment.CenterVertically, 55 | horizontalArrangement = Arrangement.SpaceBetween 56 | ) { 57 | Column ( 58 | verticalArrangement = Arrangement.Center, 59 | horizontalAlignment = Alignment.Start, 60 | modifier = modifier 61 | .weight(1f) 62 | .padding(10.dp) 63 | ){ 64 | Text( 65 | text = settingHeaderText, 66 | fontSize = 18.sp, 67 | 68 | ) 69 | 70 | Text( 71 | text = settingText, 72 | fontSize = 10.sp, 73 | ) 74 | } 75 | Icon( 76 | painter = painterResource(painterResourceID), 77 | contentDescription = "code icon", 78 | modifier = modifier 79 | .size(35.dp) 80 | .weight(0.1f), 81 | tint = MaterialTheme.colorScheme.primary 82 | ) 83 | } 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/SettingsHeader.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.foundation.layout.width 9 | import androidx.compose.material3.MaterialTheme 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.unit.dp 15 | import androidx.compose.ui.unit.sp 16 | 17 | @Composable 18 | fun SettingsHeader( 19 | modifier: Modifier = Modifier, 20 | text:String, 21 | iconContent: @Composable () -> Unit 22 | ) { 23 | Row( 24 | modifier = modifier 25 | .background(MaterialTheme.colorScheme.surface) 26 | .padding(bottom = 5.dp, top = 5.dp) 27 | .fillMaxWidth(), 28 | verticalAlignment = Alignment.CenterVertically 29 | ) { 30 | iconContent() 31 | Spacer(modifier = modifier.width(8.dp)) 32 | Text( 33 | text = text, 34 | color = MaterialTheme.colorScheme.primary, 35 | fontSize = 20.sp 36 | ) 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/SettingsToggleItem.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.height 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.shape.RoundedCornerShape 11 | import androidx.compose.material3.Card 12 | import androidx.compose.material3.CardDefaults 13 | import androidx.compose.material3.MaterialTheme 14 | import androidx.compose.material3.Switch 15 | import androidx.compose.material3.Text 16 | import androidx.compose.runtime.Composable 17 | import androidx.compose.runtime.MutableState 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.draw.clip 21 | import androidx.compose.ui.unit.dp 22 | import androidx.compose.ui.unit.sp 23 | import kotlinx.coroutines.CoroutineScope 24 | import kotlinx.coroutines.launch 25 | 26 | @Composable 27 | fun SettingsToggleItem( 28 | modifier: Modifier = Modifier, 29 | cardText:String = "", 30 | cardToggleState:MutableState, 31 | onToggleAction: (isToggleChecked:Boolean) -> Unit, 32 | ) { 33 | Card( 34 | modifier = modifier 35 | .clip(RoundedCornerShape(15.dp)) 36 | .fillMaxWidth() 37 | .background(MaterialTheme.colorScheme.inverseOnSurface), 38 | elevation = CardDefaults.cardElevation(0.dp) 39 | ) { 40 | Row( 41 | modifier = modifier 42 | .padding(10.dp) 43 | .fillMaxWidth(), 44 | verticalAlignment = Alignment.CenterVertically, 45 | horizontalArrangement = Arrangement.SpaceBetween 46 | ) { 47 | Text( 48 | text = cardText, 49 | fontSize = 18.sp, 50 | modifier = modifier.weight(1f) 51 | ) 52 | cardToggleState.value?.let { it1 -> 53 | Switch( 54 | modifier = modifier.weight(0.2f), 55 | checked = it1, 56 | onCheckedChange = { isToggleChecked-> 57 | cardToggleState.value = isToggleChecked 58 | onToggleAction(isToggleChecked) 59 | }) 60 | } 61 | } 62 | } 63 | Spacer(modifier = modifier.height(12.dp)) 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/TaskCompleteAnimations.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import androidx.compose.foundation.layout.fillMaxSize 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.MutableState 6 | import androidx.compose.runtime.getValue 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.layout.ContentScale 9 | import com.airbnb.lottie.compose.LottieAnimation 10 | import com.airbnb.lottie.compose.LottieCompositionSpec 11 | import com.airbnb.lottie.compose.animateLottieCompositionAsState 12 | import com.airbnb.lottie.compose.rememberLottieComposition 13 | import com.thatsmanmeet.taskyapp.R 14 | 15 | 16 | @Composable 17 | fun TaskCompleteAnimations( 18 | isLottiePlaying: MutableState, 19 | modifier: Modifier = Modifier 20 | ) { 21 | val lottieComposition by rememberLottieComposition(spec = LottieCompositionSpec.RawRes(R.raw.side)) 22 | val progressAnimation by animateLottieCompositionAsState(composition = lottieComposition, isPlaying = isLottiePlaying.value, speed = 1.4f) 23 | LottieAnimation( 24 | composition = lottieComposition, 25 | progress = { 26 | progressAnimation 27 | }, 28 | modifier = modifier.fillMaxSize(), 29 | contentScale = ContentScale.Crop 30 | ) 31 | 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/ThemeDialog.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.Spacer 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.width 12 | import androidx.compose.foundation.lazy.LazyColumn 13 | import androidx.compose.foundation.lazy.items 14 | import androidx.compose.material.icons.Icons 15 | import androidx.compose.material.icons.filled.Check 16 | import androidx.compose.material3.AlertDialog 17 | import androidx.compose.material3.Button 18 | import androidx.compose.material3.ButtonColors 19 | import androidx.compose.material3.Icon 20 | import androidx.compose.material3.Text 21 | import androidx.compose.runtime.Composable 22 | import androidx.compose.runtime.MutableState 23 | import androidx.compose.runtime.mutableStateOf 24 | import androidx.compose.runtime.remember 25 | import androidx.compose.ui.Alignment 26 | import androidx.compose.ui.Modifier 27 | import androidx.compose.ui.graphics.Color 28 | import androidx.compose.ui.res.painterResource 29 | import androidx.compose.ui.res.stringResource 30 | import androidx.compose.ui.unit.dp 31 | import com.thatsmanmeet.taskyapp.R 32 | import com.thatsmanmeet.taskyapp.misc.AppTheme 33 | 34 | 35 | @Composable 36 | fun ThemeChangerDialog( 37 | modifier: Modifier = Modifier, 38 | selectedItem:MutableState, 39 | isShowing:MutableState, 40 | onClick : (String) -> Unit 41 | ) { 42 | if(isShowing.value){ 43 | AlertDialog( 44 | onDismissRequest = { 45 | isShowing.value = false 46 | }, 47 | title = { 48 | Text(text = stringResource(R.string.select_app_theme_text)) 49 | }, 50 | text = { 51 | Column( 52 | horizontalAlignment = Alignment.Start, 53 | verticalArrangement = Arrangement.Center 54 | ) { 55 | val list = mutableListOf( 56 | AppTheme("0","System Default",false, R.drawable.ic_phone), 57 | AppTheme("1","Light",false,R.drawable.ic_light), 58 | AppTheme("2","Dark",false,R.drawable.ic_dark) 59 | ) 60 | LazyColumn(modifier = modifier.padding(2.dp)){ 61 | items(list){theme-> 62 | ThemeItem(appTheme = theme, selectedThemeId = selectedItem) { selectedThemeId -> 63 | selectedItem.value = selectedThemeId 64 | } 65 | } 66 | } 67 | } 68 | }, 69 | confirmButton = { 70 | Button( 71 | onClick = { 72 | onClick(selectedItem.value) 73 | isShowing.value = false 74 | }, 75 | colors = ButtonColors(containerColor = Color(0xFF229E28), contentColor = Color.White, disabledContentColor = Color.White, disabledContainerColor = Color.Gray) 76 | ) { 77 | Text(text = "OK") 78 | } 79 | }, 80 | dismissButton = { 81 | Button( 82 | onClick = { 83 | isShowing.value = false 84 | }, 85 | colors = ButtonColors(containerColor = Color(0xFFDB4C41), contentColor = Color.White, disabledContentColor = Color.White, disabledContainerColor = Color.Gray) 86 | ) { 87 | Text(text = "Cancel") 88 | } 89 | } 90 | ) 91 | } 92 | } 93 | 94 | @Composable 95 | fun ThemeItem( 96 | modifier: Modifier = Modifier, 97 | appTheme: AppTheme, 98 | selectedThemeId: MutableState, 99 | onThemeItemSelected: (String) -> Unit 100 | ) { 101 | val isSelected = remember(appTheme.id == selectedThemeId.value) { 102 | mutableStateOf(appTheme.id == selectedThemeId.value) 103 | } 104 | 105 | Row( 106 | modifier = modifier.padding(10.dp).fillMaxWidth().clickable { 107 | onThemeItemSelected(appTheme.id) 108 | }, 109 | horizontalArrangement = Arrangement.SpaceBetween, 110 | verticalAlignment = Alignment.CenterVertically 111 | ) { 112 | Row(verticalAlignment = Alignment.CenterVertically) { 113 | Icon(painter = painterResource(id = appTheme.icon), contentDescription = null) 114 | Spacer(modifier = modifier.width(10.dp)) 115 | Text(text = appTheme.mode) 116 | } 117 | if (isSelected.value) { 118 | Icon( 119 | imageVector = Icons.Default.Check, 120 | contentDescription = null, 121 | tint = Color(0xFF009688) 122 | ) 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/TimePicker.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import android.app.TimePickerDialog 4 | import android.content.Context 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.MutableState 7 | import androidx.compose.runtime.collectAsState 8 | import androidx.compose.runtime.mutableStateOf 9 | import androidx.compose.runtime.remember 10 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 11 | import java.util.* 12 | 13 | @Composable 14 | fun showTimePickerDialog( 15 | context:Context, 16 | isShowing: MutableState 17 | ):String{ 18 | val settingsStore = SettingsStore(context) 19 | val calendar = Calendar.getInstance() 20 | val hour = calendar[Calendar.HOUR_OF_DAY] 21 | val minute = calendar[Calendar.MINUTE] 22 | 23 | val time = remember { mutableStateOf("") } 24 | val timePickerDialog = TimePickerDialog( 25 | context, 26 | {_, currentHour : Int, currentMinute: Int -> 27 | time.value = "$currentHour:$currentMinute" 28 | }, hour, minute, settingsStore.getClockKey.collectAsState(initial = false).value!! 29 | ) 30 | if(isShowing.value){ 31 | timePickerDialog.show() 32 | } 33 | return time.value 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/components/TodoItemCard.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.components 2 | 3 | import android.annotation.SuppressLint 4 | import androidx.compose.foundation.background 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material.icons.Icons 8 | import androidx.compose.material.icons.filled.Notifications 9 | import androidx.compose.material3.* 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.runtime.collectAsState 12 | import androidx.compose.runtime.mutableStateOf 13 | import androidx.compose.runtime.remember 14 | import androidx.compose.ui.Alignment 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.draw.clip 17 | import androidx.compose.ui.platform.LocalContext 18 | import androidx.compose.ui.res.painterResource 19 | import androidx.compose.ui.text.style.TextDecoration 20 | import androidx.compose.ui.unit.dp 21 | import androidx.compose.ui.unit.sp 22 | import com.thatsmanmeet.taskyapp.R 23 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 24 | import com.thatsmanmeet.taskyapp.room.Todo 25 | import com.thatsmanmeet.taskyapp.room.TodoViewModel 26 | import com.thatsmanmeet.taskyapp.screens.cancelNotification 27 | import com.thatsmanmeet.taskyapp.screens.scheduleNotification 28 | import java.text.SimpleDateFormat 29 | import java.util.Calendar 30 | import java.util.Locale 31 | 32 | @SuppressLint("SuspiciousIndentation") 33 | @Composable 34 | fun TodoItemCard( 35 | todo: Todo, 36 | viewModel: TodoViewModel, 37 | modifier: Modifier = Modifier 38 | ){ 39 | val currentCheckBoxState = remember { 40 | mutableStateOf(todo.isCompleted) 41 | } 42 | val context = LocalContext.current 43 | val currentDate = Calendar.getInstance().apply { 44 | set(Calendar.HOUR_OF_DAY, 0) 45 | set(Calendar.MINUTE, 0) 46 | set(Calendar.SECOND, 0) 47 | } 48 | val settingsStore = SettingsStore(context = context) 49 | val savedSoundKey = settingsStore.getSoundKey.collectAsState(initial = true) 50 | Row( 51 | modifier = modifier 52 | .fillMaxWidth() 53 | .clip(RoundedCornerShape(10.dp)) 54 | .background(MaterialTheme.colorScheme.inverseOnSurface) 55 | .heightIn(min = 60.dp), 56 | verticalAlignment = Alignment.CenterVertically, 57 | horizontalArrangement = Arrangement.SpaceBetween 58 | ) { 59 | Row ( 60 | modifier = modifier.padding(6.dp), 61 | verticalAlignment = Alignment.CenterVertically, 62 | horizontalArrangement = Arrangement.Start 63 | ) { 64 | Checkbox( 65 | checked = currentCheckBoxState.value, 66 | onCheckedChange = { 67 | currentCheckBoxState.value = it 68 | val currentTodo = Todo( 69 | todo.ID, 70 | todo.title, 71 | currentCheckBoxState.value, 72 | todo.date, 73 | todo.time, 74 | notificationID = todo.notificationID, 75 | isRecurring = todo.isRecurring, 76 | todoDescription = todo.todoDescription 77 | ) 78 | viewModel.updateTodo( 79 | currentTodo 80 | ) 81 | if(currentCheckBoxState.value){ 82 | if(savedSoundKey.value == true){ 83 | viewModel.playCompletedSound(context) 84 | } 85 | viewModel.isAnimationPlayingState.value = true 86 | cancelNotification( 87 | context = context, 88 | todo = currentTodo 89 | ) 90 | }else{ 91 | if (currentTodo.date!!.isNotEmpty() && currentTodo.time!!.isNotEmpty()){ 92 | val format = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) 93 | val parsedDate = format.parse(currentTodo.date!!) 94 | val calendar = Calendar.getInstance().apply { 95 | time = parsedDate!! 96 | set(Calendar.HOUR_OF_DAY, currentTodo.time!!.substringBefore(":").toInt()) 97 | set(Calendar.MINUTE, currentTodo.time!!.substringAfter(":").toInt()) 98 | set(Calendar.SECOND, 0) 99 | } 100 | val currentTime = Calendar.getInstance().timeInMillis 101 | if(calendar >= currentDate && calendar.timeInMillis >= currentTime){ 102 | scheduleNotification( 103 | context = context, 104 | titleText = currentTodo.title, 105 | messageText = currentTodo.todoDescription, 106 | time = "${currentTodo.date} ${currentTodo.time}", 107 | todo = currentTodo 108 | ) 109 | } 110 | } 111 | } 112 | }) 113 | Spacer(modifier = modifier.width(3.dp)) 114 | Text( 115 | modifier = modifier.fillMaxWidth(0.9f), 116 | text = todo.title!!, 117 | textDecoration = if (currentCheckBoxState.value) TextDecoration.LineThrough else TextDecoration.None, 118 | fontSize = 16.sp 119 | ) 120 | } 121 | if(todo.time!!.isNotEmpty()){ 122 | if(todo.isRecurring){ 123 | Icon( 124 | modifier = modifier 125 | .padding(end = 5.dp) 126 | .size(24.dp), 127 | painter = painterResource(id = R.drawable.ic_refresh), 128 | contentDescription = null, 129 | tint = MaterialTheme.colorScheme.primary 130 | ) 131 | }else if(!todo.isCompleted){ 132 | Icon( 133 | modifier = modifier 134 | .padding(end = 5.dp) 135 | .size(24.dp), 136 | imageVector = Icons.Default.Notifications, 137 | contentDescription = null, 138 | tint = MaterialTheme.colorScheme.primary 139 | ) 140 | } 141 | } 142 | } 143 | Spacer(modifier = modifier.height(12.dp)) 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/constants/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.constants 2 | 3 | object Constants { 4 | const val BROADCAST_ID = 3553 5 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/datastore/SettingsStore.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.datastore 2 | 3 | import android.content.Context 4 | import androidx.datastore.core.DataStore 5 | import androidx.datastore.preferences.core.Preferences 6 | import androidx.datastore.preferences.core.booleanPreferencesKey 7 | import androidx.datastore.preferences.core.edit 8 | import androidx.datastore.preferences.core.stringPreferencesKey 9 | import androidx.datastore.preferences.preferencesDataStore 10 | import kotlinx.coroutines.flow.Flow 11 | import kotlinx.coroutines.flow.map 12 | 13 | class SettingsStore( 14 | private val context: Context 15 | ) { 16 | companion object{ 17 | private val Context.dataStore:DataStore by preferencesDataStore("settings") 18 | val TASK_LIST_KEY = booleanPreferencesKey("task_list_preference") 19 | val ANIMATION_SHOW_KEY = booleanPreferencesKey("animation_list_preference") 20 | val SHOW_24_HOUR_CLOCK_KEY = booleanPreferencesKey("show_24_hour_clock_preference") 21 | val TASK_COMPLETION_SOUNDS = booleanPreferencesKey("sound_list_preference") 22 | val THEME_MODE_KEY = stringPreferencesKey("theme_mode_preference") 23 | val USE_SYSTEM_FONT = booleanPreferencesKey("use_system_font_preference") 24 | val USE_LEGACY_DATE_TIME_PICKERS = booleanPreferencesKey("use_legacy_date_time_pickers") 25 | } 26 | 27 | val getTaskListKey : Flow = context.dataStore.data.map {preference-> 28 | preference[TASK_LIST_KEY] ?: true 29 | } 30 | 31 | val getAnimationKey : Flow = context.dataStore.data.map {preference-> 32 | preference[ANIMATION_SHOW_KEY] ?: true 33 | } 34 | 35 | val getClockKey : Flow = context.dataStore.data.map {preference-> 36 | preference[SHOW_24_HOUR_CLOCK_KEY] ?: true 37 | } 38 | val getSoundKey : Flow = context.dataStore.data.map{preference-> 39 | preference[TASK_COMPLETION_SOUNDS] ?: true 40 | } 41 | 42 | val getThemeModeKey : Flow = context.dataStore.data.map {preference -> 43 | preference[THEME_MODE_KEY] ?: "" 44 | } 45 | 46 | val getUseSystemFontKey : Flow = context.dataStore.data.map {preference -> 47 | preference[USE_SYSTEM_FONT] ?: false 48 | } 49 | 50 | val getUseLegacyDateTimePickers: Flow = context.dataStore.data.map {preference -> 51 | preference[USE_LEGACY_DATE_TIME_PICKERS] ?: false 52 | } 53 | 54 | 55 | suspend fun saveUseLegacyDateTimePickers(isEnabled: Boolean){ 56 | context.dataStore.edit {preferences-> 57 | preferences[USE_LEGACY_DATE_TIME_PICKERS] = isEnabled 58 | } 59 | } 60 | 61 | suspend fun saveTaskListKey(isEnabled:Boolean) { 62 | context.dataStore.edit {preferences-> 63 | preferences[TASK_LIST_KEY] = isEnabled 64 | } 65 | } 66 | suspend fun saveAnimationShowKey(isEnabled:Boolean) { 67 | context.dataStore.edit {preferences-> 68 | preferences[ANIMATION_SHOW_KEY] = isEnabled 69 | } 70 | } 71 | 72 | suspend fun saveSoundPlayKey(isEnabled: Boolean){ 73 | context.dataStore.edit {preferences-> 74 | preferences[TASK_COMPLETION_SOUNDS] = isEnabled 75 | } 76 | } 77 | 78 | suspend fun saveSystemFontsKey(isEnabled: Boolean){ 79 | context.dataStore.edit {preferences-> 80 | preferences[USE_SYSTEM_FONT] = isEnabled 81 | } 82 | } 83 | 84 | suspend fun saveClockKey(isEnabled: Boolean){ 85 | context.dataStore.edit { preference-> 86 | preference[SHOW_24_HOUR_CLOCK_KEY] = isEnabled 87 | } 88 | } 89 | 90 | suspend fun saveThemeModeKey(mode:String){ 91 | context.dataStore.edit { preference-> 92 | preference[THEME_MODE_KEY] = mode 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/misc/AppTheme.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.misc 2 | 3 | data class AppTheme( 4 | val id:String, 5 | val mode:String, 6 | var isSelected:Boolean, 7 | val icon:Int 8 | ) 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/misc/Recursor.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.misc 2 | 3 | import android.content.Context 4 | import androidx.room.Room 5 | import com.thatsmanmeet.taskyapp.room.TodoDatabase 6 | import com.thatsmanmeet.taskyapp.screens.scheduleNotification 7 | import com.thatsmanmeet.taskyapp.screens.setRepeatingAlarm 8 | import java.text.SimpleDateFormat 9 | import java.util.Calendar 10 | import java.util.Locale 11 | 12 | class Recursor { 13 | 14 | fun rescheduleTasks(context: Context){ 15 | val todoDatabase = Room.databaseBuilder( 16 | context.applicationContext, 17 | TodoDatabase::class.java, 18 | "todo_database" 19 | ).build() 20 | 21 | val todoList = todoDatabase.todoDao().getAllTodos() 22 | 23 | todoList.observeForever {todos-> 24 | val currentDate = Calendar.getInstance().apply { 25 | set(Calendar.HOUR_OF_DAY, 0) 26 | set(Calendar.MINUTE, 0) 27 | set(Calendar.SECOND, 0) 28 | } 29 | todos.forEach {todo-> 30 | if(!todo.isCompleted && (todo.date!!.isNotEmpty() && todo.time!!.isNotEmpty())){ 31 | val format = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) 32 | val parsedDate = format.parse(todo.date!!) 33 | val calendar = Calendar.getInstance().apply { 34 | time = parsedDate!! 35 | set(Calendar.HOUR_OF_DAY, todo.time!!.substringBefore(":").toInt()) 36 | set(Calendar.MINUTE, todo.time!!.substringAfter(":").toInt()) 37 | set(Calendar.SECOND, 0) 38 | } 39 | val timeFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault()) 40 | val parsedTime = timeFormat.parse("${todo.date} ${todo.time}") 41 | val notificationTime = Calendar.getInstance().apply { 42 | time = parsedTime!! 43 | } 44 | val currentTime = Calendar.getInstance().timeInMillis 45 | if(calendar >= currentDate && notificationTime.timeInMillis >= currentTime){ 46 | scheduleNotification( 47 | context = context, 48 | titleText = todo.title, 49 | messageText = todo.todoDescription, 50 | time = "${todo.date} ${todo.time}", 51 | todo = todo 52 | ) 53 | println("Todo ${todo.title} set!") 54 | } 55 | } 56 | } 57 | } 58 | setRepeatingAlarm(context = context) 59 | todoList.removeObserver{} 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/notification/Notification.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.notification 2 | 3 | import android.app.NotificationManager 4 | import android.app.PendingIntent 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.content.Intent 8 | import androidx.core.app.NotificationCompat 9 | import com.thatsmanmeet.taskyapp.MainActivity 10 | import com.thatsmanmeet.taskyapp.R 11 | import kotlin.random.Random 12 | 13 | const val notificationID = 1 14 | const val channelID = "Task Reminder Channel v1" 15 | const val titleExtra = "titleExtra" 16 | const val messageExtra = "messageExtra" 17 | 18 | class Notification: BroadcastReceiver(){ 19 | override fun onReceive(context: Context, intent: Intent?) { 20 | val launchIntent = Intent(context, MainActivity::class.java) 21 | intent!!.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK 22 | val pendingIntent = PendingIntent.getActivity( context.applicationContext, 0, launchIntent, PendingIntent.FLAG_IMMUTABLE) 23 | 24 | val notification = NotificationCompat.Builder(context, channelID) 25 | .setSmallIcon(R.drawable.ic_check) 26 | .setContentTitle(intent.getStringExtra(titleExtra)) 27 | .setContentText(intent.getStringExtra(messageExtra)) 28 | .setContentIntent(pendingIntent) 29 | .setAutoCancel(true) 30 | .build() 31 | val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 32 | manager.notify(Random.nextInt(0,1000) - Random.nextInt(25,75),notification) 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/receiver/BootCompletedReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.receiver 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.Intent 6 | import androidx.room.Room 7 | import com.thatsmanmeet.taskyapp.room.TodoDatabase 8 | import com.thatsmanmeet.taskyapp.screens.scheduleNotification 9 | import com.thatsmanmeet.taskyapp.screens.setRepeatingAlarm 10 | import java.text.SimpleDateFormat 11 | import java.util.Calendar 12 | import java.util.Locale 13 | 14 | class BootCompletedReceiver : BroadcastReceiver() { 15 | override fun onReceive(context: Context?, intent: Intent?) { 16 | if(intent?.action == Intent.ACTION_BOOT_COMPLETED){ 17 | val todoDatabase = Room.databaseBuilder( 18 | context!!.applicationContext, 19 | TodoDatabase::class.java, 20 | "todo_database" 21 | ).build() 22 | 23 | val todoList = todoDatabase.todoDao().getAllTodos() 24 | 25 | todoList.observeForever {todos-> 26 | val currentDate = Calendar.getInstance().apply { 27 | set(Calendar.HOUR_OF_DAY, 0) 28 | set(Calendar.MINUTE, 0) 29 | set(Calendar.SECOND, 0) 30 | } 31 | todos.forEach {todo-> 32 | if(!todo.isCompleted && (todo.date!!.isNotEmpty() && todo.time!!.isNotEmpty())){ 33 | val format = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) 34 | val parsedDate = format.parse(todo.date!!) 35 | val calendar = Calendar.getInstance().apply { 36 | time = parsedDate!! 37 | set(Calendar.HOUR_OF_DAY, todo.time!!.substringBefore(":").toInt()) 38 | set(Calendar.MINUTE, todo.time!!.substringAfter(":").toInt()) 39 | set(Calendar.SECOND, 0) 40 | } 41 | val timeFormat = SimpleDateFormat("dd/MM/yyyy HH:mm", Locale.getDefault()) 42 | val parsedTime = timeFormat.parse("${todo.date} ${todo.time}") 43 | val notificationTime = Calendar.getInstance().apply { 44 | time = parsedTime!! 45 | } 46 | val currentTime = Calendar.getInstance().timeInMillis 47 | if(calendar >= currentDate && notificationTime.timeInMillis >= currentTime){ 48 | scheduleNotification( 49 | context = context, 50 | titleText = todo.title, 51 | messageText = todo.todoDescription, 52 | time = "${todo.date} ${todo.time}", 53 | todo = todo 54 | ) 55 | println("Todo ${todo.title} set!") 56 | } 57 | } 58 | } 59 | } 60 | setRepeatingAlarm(context = context) 61 | todoList.removeObserver{} 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/receiver/RepeatingTasksReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.receiver 2 | 3 | 4 | import android.app.AlarmManager 5 | import android.app.PendingIntent 6 | import android.content.BroadcastReceiver 7 | import android.content.Context 8 | import android.content.Intent 9 | import androidx.room.Room 10 | import com.thatsmanmeet.taskyapp.constants.Constants 11 | import com.thatsmanmeet.taskyapp.room.Todo 12 | import com.thatsmanmeet.taskyapp.room.TodoDatabase 13 | import com.thatsmanmeet.taskyapp.room.deletedtodo.DeletedTodoDatabase 14 | import com.thatsmanmeet.taskyapp.screens.scheduleNotification 15 | import kotlinx.coroutines.CoroutineScope 16 | import kotlinx.coroutines.Dispatchers 17 | import kotlinx.coroutines.launch 18 | import kotlinx.coroutines.withTimeout 19 | import java.text.SimpleDateFormat 20 | import java.util.Date 21 | import java.util.Locale 22 | 23 | class RepeatingTasksReceiver : BroadcastReceiver() { 24 | private val coroutineScope = CoroutineScope(Dispatchers.Main) 25 | override fun onReceive(context: Context?, intent: Intent?) { 26 | if(intent!!.action == Intent.ACTION_DATE_CHANGED || intent.action == "repeating_tasks") { 27 | val todoDatabase = Room.databaseBuilder( 28 | context!!.applicationContext, 29 | TodoDatabase::class.java, 30 | "todo_database" 31 | ).build() 32 | val deletedTodoDatabase = Room.databaseBuilder( 33 | context!!.applicationContext, 34 | DeletedTodoDatabase::class.java, 35 | "deleted_todo_database" 36 | ).build() 37 | coroutineScope.launch { 38 | withTimeout(10000) { 39 | todoDatabase.todoDao().getAllTodosFlow().collect{todos-> 40 | for(todo in todos){ 41 | if ((todo.date!!.isNotEmpty() && todo.time!!.isNotEmpty()) && todo.isRecurring) { 42 | val currentDate = 43 | SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format( 44 | Date() 45 | ) 46 | val newTodo = Todo( 47 | ID = todo.ID, 48 | title = todo.title, 49 | isCompleted = false, 50 | date = currentDate, 51 | time = todo.time, 52 | notificationID = todo.notificationID, 53 | isRecurring = todo.isRecurring, 54 | todoDescription = todo.todoDescription 55 | ) 56 | scheduleNotification( 57 | context, 58 | newTodo.title, 59 | newTodo.todoDescription, 60 | time = "${newTodo.date} ${newTodo.time}", 61 | todo = newTodo 62 | ) 63 | todoDatabase.todoDao().updateTodo(newTodo) 64 | } 65 | } 66 | } 67 | } 68 | } 69 | coroutineScope.launch { 70 | withTimeout(10000){ 71 | deletedTodoDatabase.deletedTodoDao().getAllTodosFlow().collect{deletedTodos-> 72 | val currentDate = 73 | SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format( 74 | Date() 75 | ) 76 | for(todo in deletedTodos){ 77 | if(currentDate == todo.todoDeletionDate){ 78 | deletedTodoDatabase.deletedTodoDao().deleteTodo(todo) 79 | } 80 | } 81 | } 82 | } 83 | } 84 | val nextAlarmIntent = Intent(context.applicationContext,RepeatingTasksReceiver::class.java).also { 85 | it.action = "repeating_tasks" 86 | } 87 | val currentAlarmTime = System.currentTimeMillis() 88 | val nextAlarmTime = currentAlarmTime + (24 * 60 * 60 * 1000) // Add 24 hours 89 | val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager 90 | alarmManager.setExactAndAllowWhileIdle( 91 | AlarmManager.RTC_WAKEUP, 92 | nextAlarmTime, 93 | PendingIntent.getBroadcast(context.applicationContext, 94 | Constants.BROADCAST_ID,nextAlarmIntent, 95 | PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) 96 | 97 | ) 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/Todo.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "todo_table") 8 | data class Todo( 9 | @PrimaryKey(autoGenerate = true) var ID:Long? = null, 10 | @ColumnInfo(name = "title") var title:String? = "", 11 | @ColumnInfo(name = "completed") var isCompleted:Boolean = false, 12 | @ColumnInfo(name = "date") var date:String? = "", 13 | @ColumnInfo(name = "time") var time:String? = "", 14 | @ColumnInfo(name = "notificationID", defaultValue = "0") var notificationID:Int = 0, 15 | @ColumnInfo(name = "is_Recurring", defaultValue = "false") var isRecurring : Boolean = false, 16 | @ColumnInfo(name = "description", defaultValue = "") var todoDescription : String? = "" 17 | ) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoDao.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.room.* 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | @Dao 8 | interface TodoDao { 9 | 10 | @Insert(onConflict = OnConflictStrategy.REPLACE) 11 | suspend fun addTodo(todo: Todo) 12 | 13 | @Update(onConflict = OnConflictStrategy.REPLACE) 14 | suspend fun updateTodo(todo: Todo) 15 | 16 | @Delete 17 | suspend fun deleteTodo(todo: Todo) 18 | 19 | @Query("SELECT * FROM todo_table ORDER BY ID ASC") 20 | fun getAllTodos() : LiveData> 21 | 22 | @Query("SELECT * FROM todo_table ORDER BY ID ASC") 23 | fun getAllTodosFlow() : Flow> 24 | 25 | @Query("SELECT * FROM todo_table WHERE ID = :id") 26 | fun getOneTodo(id:Long) : Todo 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room 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( 10 | entities = [Todo::class], 11 | version = 4, 12 | exportSchema = true, 13 | autoMigrations = [ 14 | AutoMigration(from = 2, to = 3), 15 | AutoMigration(from = 3, to = 4) 16 | ] 17 | ) 18 | abstract class TodoDatabase : RoomDatabase() { 19 | 20 | abstract fun todoDao() : TodoDao 21 | 22 | companion object{ 23 | @Volatile 24 | private var INSTANCE: TodoDatabase? = null 25 | 26 | fun getInstance(context:Context) : TodoDatabase { 27 | synchronized(this){ 28 | var instance = INSTANCE 29 | if(instance == null){ 30 | instance = Room.databaseBuilder( 31 | context.applicationContext, 32 | TodoDatabase::class.java, 33 | "todo_database" 34 | ) 35 | .allowMainThreadQueries() 36 | .build() 37 | INSTANCE = instance 38 | } 39 | return instance 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoRepository.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | 6 | class TodoRepository(private val todoDao: TodoDao) { 7 | 8 | suspend fun insertTodo(todo: Todo){ 9 | todoDao.addTodo(todo) 10 | } 11 | 12 | suspend fun updateTodo(todo: Todo){ 13 | todoDao.updateTodo(todo) 14 | } 15 | 16 | suspend fun deleteTodo(todo: Todo){ 17 | todoDao.deleteTodo(todo) 18 | } 19 | 20 | fun getOneTodo(id:Long) : Todo = todoDao.getOneTodo(id) 21 | 22 | fun getAllTodosFlow() : Flow> = todoDao.getAllTodosFlow() 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/TodoViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room 2 | 3 | 4 | import android.app.Application 5 | import android.content.Context 6 | import android.media.MediaPlayer 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.lifecycle.ViewModel 9 | import androidx.lifecycle.viewModelScope 10 | import com.thatsmanmeet.taskyapp.R 11 | import kotlinx.coroutines.Dispatchers 12 | import kotlinx.coroutines.flow.Flow 13 | import kotlinx.coroutines.launch 14 | 15 | class TodoViewModel(application: Application) : ViewModel() { 16 | 17 | private var repository: TodoRepository 18 | val getAllTodosFlow : Flow> 19 | val isAnimationPlayingState = mutableStateOf(false) 20 | private var mediaPlayer : MediaPlayer? = null 21 | 22 | init { 23 | val dao = TodoDatabase.getInstance(application).todoDao() 24 | repository = TodoRepository(dao) 25 | getAllTodosFlow = repository.getAllTodosFlow() 26 | } 27 | 28 | fun insertTodo(todo: Todo){ 29 | viewModelScope.launch(Dispatchers.IO) { 30 | repository.insertTodo(todo) 31 | } 32 | } 33 | fun updateTodo(todo: Todo){ 34 | viewModelScope.launch(Dispatchers.IO) { 35 | repository.updateTodo(todo) 36 | } 37 | } 38 | fun deleteTodo(todo: Todo){ 39 | viewModelScope.launch(Dispatchers.IO) { 40 | repository.deleteTodo(todo) 41 | } 42 | } 43 | 44 | fun getOneTodo(id:Long) : Todo{ 45 | return repository.getOneTodo(id) 46 | } 47 | 48 | fun playCompletedSound(context: Context){ 49 | mediaPlayer?.release() 50 | mediaPlayer = MediaPlayer.create(context,R.raw.completed) 51 | mediaPlayer?.start() 52 | mediaPlayer?.setOnCompletionListener { 53 | mediaPlayer?.release() 54 | mediaPlayer = null 55 | } 56 | } 57 | 58 | private var lastDeletedTodo = Todo() 59 | 60 | fun setLastDeletedTodo(todo: Todo){ 61 | lastDeletedTodo = todo 62 | } 63 | 64 | fun getLastDeletedTodo():Todo{ 65 | return lastDeletedTodo 66 | } 67 | 68 | fun resetLastDeletedTodo(){ 69 | lastDeletedTodo = Todo() 70 | } 71 | 72 | fun playDeletedSound(context: Context) { 73 | mediaPlayer?.release() 74 | mediaPlayer = MediaPlayer.create(context, R.raw.deleted) 75 | mediaPlayer?.start() 76 | mediaPlayer?.setOnCompletionListener { 77 | mediaPlayer?.release() 78 | mediaPlayer = null 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/deletedtodo/DeletedTodo.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.deletedtodo 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "deleted_todo_table") 8 | data class DeletedTodo( 9 | @PrimaryKey(autoGenerate = true) var ID:Long? = null, 10 | @ColumnInfo(name = "title") var title:String? = "", 11 | @ColumnInfo(name = "completed") var isCompleted:Boolean = false, 12 | @ColumnInfo(name = "date") var date:String? = "", 13 | @ColumnInfo(name = "time") var time:String? = "", 14 | @ColumnInfo(name = "notificationID", defaultValue = "0") var notificationID:Int = 0, 15 | @ColumnInfo(name = "is_Recurring", defaultValue = "false") var isRecurring : Boolean = false, 16 | @ColumnInfo(name = "description", defaultValue = "") var todoDescription : String? = "", 17 | @ColumnInfo(name = "deletionDate", defaultValue = "") var todoDeletionDate: String ? = "" 18 | ) 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/deletedtodo/DeletedTodoDao.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.deletedtodo 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 | import kotlinx.coroutines.flow.Flow 11 | 12 | @Dao 13 | interface DeletedTodoDao { 14 | 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | suspend fun addTodo(todo: DeletedTodo) 17 | 18 | @Update(onConflict = OnConflictStrategy.REPLACE) 19 | suspend fun updateTodo(todo: DeletedTodo) 20 | 21 | @Delete 22 | suspend fun deleteTodo(todo: DeletedTodo) 23 | 24 | @Query("SELECT * FROM deleted_todo_table ORDER BY ID ASC") 25 | fun getAllTodos() : LiveData> 26 | 27 | @Query("SELECT * FROM deleted_todo_table ORDER BY ID ASC") 28 | fun getAllTodosFlow() : Flow> 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/deletedtodo/DeletedTodoDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.deletedtodo 2 | 3 | import android.content.Context 4 | import androidx.room.Database 5 | import androidx.room.Room 6 | import androidx.room.RoomDatabase 7 | 8 | @Database( 9 | entities = [DeletedTodo::class], 10 | version = 1 11 | ) 12 | abstract class DeletedTodoDatabase : RoomDatabase() { 13 | 14 | abstract fun deletedTodoDao(): DeletedTodoDao 15 | 16 | companion object{ 17 | @Volatile 18 | private var INSTANCE : DeletedTodoDatabase? = null 19 | 20 | fun getInstance(context: Context): DeletedTodoDatabase{ 21 | synchronized(this){ 22 | var instance = INSTANCE 23 | if(instance == null){ 24 | instance = Room.databaseBuilder( 25 | context.applicationContext, 26 | DeletedTodoDatabase::class.java, 27 | "deleted_todo_database" 28 | ).allowMainThreadQueries().build() 29 | INSTANCE = instance 30 | } 31 | return instance 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/deletedtodo/DeletedTodoRepository.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.deletedtodo 2 | 3 | 4 | import kotlinx.coroutines.flow.Flow 5 | 6 | class DeletedTodoRepository(private val deletedTodoDao: DeletedTodoDao) { 7 | suspend fun insertTodo(todo: DeletedTodo){ 8 | deletedTodoDao.addTodo(todo) 9 | } 10 | 11 | suspend fun updateTodo(todo: DeletedTodo){ 12 | deletedTodoDao.updateTodo(todo) 13 | } 14 | 15 | suspend fun deleteTodo(todo: DeletedTodo){ 16 | deletedTodoDao.deleteTodo(todo) 17 | } 18 | 19 | fun getAllTodosFlow() : Flow> = deletedTodoDao.getAllTodosFlow() 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/deletedtodo/DeletedTodoViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.deletedtodo 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import kotlinx.coroutines.flow.Flow 7 | import kotlinx.coroutines.launch 8 | 9 | class DeletedTodoViewModel(application: Application) : ViewModel() { 10 | 11 | private var repository:DeletedTodoRepository 12 | val getAllDeletedTodos: Flow> 13 | 14 | init { 15 | val dao = DeletedTodoDatabase.getInstance(application).deletedTodoDao() 16 | repository = DeletedTodoRepository(dao) 17 | getAllDeletedTodos = repository.getAllTodosFlow() 18 | } 19 | 20 | fun insertDeletedTodo(deletedTodo: DeletedTodo){ 21 | viewModelScope.launch { 22 | repository.insertTodo(deletedTodo) 23 | } 24 | } 25 | fun deleteDeletedTodo(deletedTodo: DeletedTodo){ 26 | viewModelScope.launch { 27 | repository.deleteTodo(deletedTodo) 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/notes/Note.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.notes 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "notes_table") 8 | data class Note ( 9 | @PrimaryKey(autoGenerate = true) var ID:Long? = null, 10 | @ColumnInfo(name = "title") var title:String? = "", 11 | @ColumnInfo(name = "body") var body : String? = "", 12 | @ColumnInfo(name = "date") var date:String? = "", 13 | @ColumnInfo(name = "favourite", defaultValue = "") var isFavourite : Boolean? = false, 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/notes/NoteDao.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.notes 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Delete 5 | import androidx.room.Query 6 | import androidx.room.Upsert 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | @Dao 10 | interface NoteDao { 11 | @Upsert 12 | suspend fun upsertNote(note: Note) 13 | 14 | @Delete 15 | suspend fun deleteNote(note: Note) 16 | 17 | @Query("SELECT * FROM notes_table ORDER BY date DESC") 18 | fun getAllNotesFlow(): Flow> 19 | 20 | @Query("SELECT * FROM notes_table WHERE ID = :id") 21 | fun getOneNote(id:Long): Note 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/notes/NoteDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.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 | 9 | @Database( 10 | entities = [Note::class], 11 | version = 1, 12 | exportSchema = true, 13 | ) 14 | abstract class NoteDatabase: RoomDatabase() { 15 | abstract fun noteDao(): NoteDao 16 | 17 | companion object{ 18 | @Volatile 19 | private var INSTANCE:NoteDatabase? = null 20 | 21 | fun getInstance(context: Context) : NoteDatabase{ 22 | synchronized(this){ 23 | var instance = INSTANCE 24 | if(instance == null){ 25 | instance = Room.databaseBuilder( 26 | context, 27 | NoteDatabase::class.java, 28 | "notes_database" 29 | ).allowMainThreadQueries().build() 30 | INSTANCE = instance 31 | } 32 | return instance 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/notes/NoteRepository.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.notes 2 | 3 | import kotlinx.coroutines.flow.Flow 4 | 5 | class NoteRepository(private val noteDao: NoteDao) { 6 | fun getAllNotes(): Flow> = noteDao.getAllNotesFlow() 7 | 8 | fun getOneNote(id:Long): Note = noteDao.getOneNote(id) 9 | 10 | suspend fun upsertNote(note: Note) = noteDao.upsertNote(note) 11 | 12 | suspend fun deleteNote(note: Note) = noteDao.deleteNote(note) 13 | 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/room/notes/NoteViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.room.notes 2 | 3 | import android.app.Application 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import kotlinx.coroutines.Dispatchers 7 | import kotlinx.coroutines.flow.Flow 8 | import kotlinx.coroutines.launch 9 | 10 | class NoteViewModel(application: Application) : ViewModel() { 11 | private var repository:NoteRepository 12 | val getAllNotesFlow: Flow> 13 | 14 | init{ 15 | val dao = NoteDatabase.getInstance(application).noteDao() 16 | repository = NoteRepository(dao) 17 | getAllNotesFlow = repository.getAllNotes() 18 | } 19 | 20 | fun getOneNote(id:Long) : Note{ 21 | return repository.getOneNote(id) 22 | } 23 | 24 | fun upsertNote(note: Note){ 25 | viewModelScope.launch(Dispatchers.IO) { 26 | repository.upsertNote(note) 27 | } 28 | } 29 | 30 | fun deleteNote(note: Note){ 31 | viewModelScope.launch(Dispatchers.IO) { 32 | repository.deleteNote(note) 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/screens/AboutScreen.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.screens 2 | 3 | import androidx.compose.foundation.clickable 4 | import androidx.compose.foundation.isSystemInDarkTheme 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxSize 8 | import androidx.compose.foundation.layout.height 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.rememberScrollState 11 | import androidx.compose.foundation.verticalScroll 12 | import androidx.compose.material.icons.Icons 13 | import androidx.compose.material.icons.automirrored.filled.ArrowBack 14 | import androidx.compose.material.icons.filled.ArrowBack 15 | import androidx.compose.material3.ExperimentalMaterial3Api 16 | import androidx.compose.material3.Icon 17 | import androidx.compose.material3.IconButton 18 | import androidx.compose.material3.MaterialTheme 19 | import androidx.compose.material3.Scaffold 20 | import androidx.compose.material3.Text 21 | import androidx.compose.material3.TopAppBar 22 | import androidx.compose.material3.TopAppBarDefaults 23 | import androidx.compose.runtime.Composable 24 | import androidx.compose.runtime.collectAsState 25 | import androidx.compose.ui.Modifier 26 | import androidx.compose.ui.platform.LocalContext 27 | import androidx.compose.ui.text.style.TextAlign 28 | import androidx.compose.ui.unit.dp 29 | import androidx.compose.ui.unit.sp 30 | import androidx.navigation.NavHostController 31 | import com.thatsmanmeet.taskyapp.BuildConfig 32 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 33 | import com.thatsmanmeet.taskyapp.ui.theme.TaskyTheme 34 | import com.thatsmanmeet.taskyapp.viewmodels.MainViewModel 35 | 36 | @OptIn(ExperimentalMaterial3Api::class) 37 | @Composable 38 | fun AboutScreen( 39 | navHostController: NavHostController, 40 | modifier: Modifier = Modifier 41 | ) { 42 | val context = LocalContext.current 43 | val settingStore = SettingsStore(context) 44 | val savedThemeKey = settingStore.getThemeModeKey.collectAsState(initial = "") 45 | val savedFontKey = settingStore.getUseSystemFontKey.collectAsState(initial = false) 46 | val mainViewModel = MainViewModel() 47 | val height = 12.dp 48 | TaskyTheme( 49 | darkTheme = when (savedThemeKey.value) { 50 | "0" -> { 51 | isSystemInDarkTheme() 52 | } 53 | "1" -> {false} 54 | else -> {true} 55 | }, 56 | useSystemFont = savedFontKey.value!! 57 | ) { 58 | Scaffold( 59 | topBar = { 60 | TopAppBar( 61 | title = { Text(text = "About Tasky") }, 62 | navigationIcon = { 63 | IconButton(onClick = { 64 | navHostController.navigate(route = Screen.MyApp.route){ 65 | popUpTo(route = Screen.MyApp.route){ 66 | inclusive = true 67 | } 68 | } 69 | }) { 70 | Icon(imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) 71 | } 72 | }, 73 | colors = TopAppBarDefaults.topAppBarColors( 74 | containerColor = MaterialTheme.colorScheme.primary, 75 | titleContentColor = MaterialTheme.colorScheme.onPrimary, 76 | navigationIconContentColor = MaterialTheme.colorScheme.onPrimary 77 | ) 78 | ) 79 | } 80 | ) {paddingValues -> 81 | Column( 82 | modifier = modifier 83 | .verticalScroll(rememberScrollState()) 84 | .fillMaxSize() 85 | .padding(paddingValues) 86 | .padding(16.dp) 87 | ) { 88 | Text( 89 | text = "Welcome to Tasky, a free and open source, minimal and aesthetic task management app. This guide will provide you with everything you can do in this app.", 90 | fontSize = 16.sp, 91 | textAlign = TextAlign.Justify 92 | ) 93 | Spacer(modifier = modifier.height(height)) 94 | Text(text = "License: GPL-V3") 95 | Spacer(modifier = modifier.height(height)) 96 | Text(text = "APP Version: V${BuildConfig.VERSION_NAME}") 97 | Spacer(modifier = modifier.height(height)) 98 | Text(text = "APP Version Code: ${BuildConfig.VERSION_CODE}") 99 | Spacer(modifier = modifier.height(height)) 100 | Text(text = "Author: @thatsmanmeet") 101 | Spacer(modifier = modifier.height(height)) 102 | Text(text = "Click here to view Source Code", modifier = modifier.clickable { 103 | mainViewModel.openGithubPage(context) 104 | }, color = MaterialTheme.colorScheme.onPrimaryContainer) 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/screens/AddNoteScreen.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.screens 2 | 3 | import android.app.Activity 4 | import androidx.compose.foundation.isSystemInDarkTheme 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.material.icons.Icons 7 | import androidx.compose.material.icons.filled.Done 8 | import androidx.compose.material.icons.filled.Favorite 9 | import androidx.compose.material.icons.outlined.FavoriteBorder 10 | import androidx.compose.material3.* 11 | import androidx.compose.runtime.* 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.unit.dp 16 | import androidx.navigation.NavHostController 17 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 18 | import com.thatsmanmeet.taskyapp.room.notes.Note 19 | import com.thatsmanmeet.taskyapp.room.notes.NoteViewModel 20 | import com.thatsmanmeet.taskyapp.ui.theme.TaskyTheme 21 | import java.time.ZoneId 22 | import java.time.ZonedDateTime 23 | 24 | @OptIn(ExperimentalMaterial3Api::class) 25 | @Composable 26 | fun AddNoteScreen( 27 | navHostController: NavHostController, 28 | modifier: Modifier = Modifier 29 | ) { 30 | val context = LocalContext.current 31 | val activity = context as Activity 32 | val notesViewModel = NoteViewModel(activity.application) 33 | val settingsStore = SettingsStore(context) 34 | val savedThemeKey = settingsStore.getThemeModeKey.collectAsState(initial = "0") 35 | val savedFontKey = settingsStore.getUseSystemFontKey.collectAsState(initial = false) 36 | 37 | val titleText = remember { mutableStateOf("") } 38 | val bodyText = remember { mutableStateOf("") } 39 | val isPinned = remember { mutableStateOf(false) } 40 | val isSavedByClick = remember { mutableStateOf(false) } 41 | 42 | fun getCurrentDate(): String { 43 | val zoneId = ZoneId.systemDefault() 44 | val zoneDateTime = ZonedDateTime.now(zoneId) 45 | return zoneDateTime.toLocalDate().toString() 46 | } 47 | 48 | TaskyTheme( 49 | darkTheme = when (savedThemeKey.value.toString()) { 50 | "null", "0" -> isSystemInDarkTheme() 51 | "1" -> false 52 | else -> true 53 | }, 54 | useSystemFont = savedFontKey.value!! 55 | ) { 56 | Scaffold( 57 | topBar = { 58 | TopAppBar( 59 | title = { Text(text = "New Note") }, 60 | actions = { 61 | IconButton(onClick = { isPinned.value = !isPinned.value }) { 62 | if (!isPinned.value) { 63 | Icon(imageVector = Icons.Outlined.FavoriteBorder, contentDescription = "", tint = MaterialTheme.colorScheme.onPrimary) 64 | } else { 65 | Icon(imageVector = Icons.Filled.Favorite, contentDescription = "", tint = Color.Red) 66 | } 67 | } 68 | if(titleText.value != "" || bodyText.value != ""){ 69 | IconButton(onClick = { 70 | isSavedByClick.value = true 71 | notesViewModel.upsertNote( 72 | Note( 73 | title = titleText.value, 74 | body = bodyText.value, 75 | isFavourite = isPinned.value, 76 | date = getCurrentDate() 77 | ) 78 | ).also { 79 | navHostController.navigate(Screen.NotesScreen.route) { 80 | popUpTo(Screen.NotesScreen.route) { inclusive = true } 81 | } 82 | } 83 | }) { 84 | Icon(imageVector = Icons.Default.Done, contentDescription = "",tint = MaterialTheme.colorScheme.onPrimary) 85 | } 86 | } 87 | } 88 | , 89 | colors = TopAppBarDefaults.topAppBarColors( 90 | containerColor = MaterialTheme.colorScheme.primary, 91 | titleContentColor = MaterialTheme.colorScheme.onPrimary 92 | ) 93 | ) 94 | } 95 | ) { paddingValues -> 96 | Surface( 97 | modifier = modifier 98 | .fillMaxSize() 99 | .padding(paddingValues) 100 | .padding(10.dp), 101 | color = MaterialTheme.colorScheme.background 102 | ) { 103 | Column(modifier = modifier.fillMaxSize()) { 104 | TextField( 105 | modifier = modifier.fillMaxWidth(), 106 | value = titleText.value, 107 | onValueChange = { titleText.value = it }, 108 | placeholder = { Text(text = "Title") }, 109 | colors = TextFieldDefaults.colors( 110 | unfocusedContainerColor = MaterialTheme.colorScheme.surface, 111 | focusedContainerColor = MaterialTheme.colorScheme.surface, 112 | disabledIndicatorColor = Color.Transparent, 113 | focusedIndicatorColor = Color.Transparent, 114 | unfocusedIndicatorColor = Color.Transparent, 115 | errorIndicatorColor = Color.Transparent 116 | ) 117 | ) 118 | Spacer(modifier = modifier.height(5.dp)) 119 | TextField( 120 | modifier = modifier.fillMaxSize(), 121 | value = bodyText.value, 122 | onValueChange = { bodyText.value = it }, 123 | placeholder = { Text(text = "Write Something...") }, 124 | colors = TextFieldDefaults.colors( 125 | unfocusedContainerColor = MaterialTheme.colorScheme.surface, 126 | focusedContainerColor = MaterialTheme.colorScheme.surface, 127 | disabledIndicatorColor = Color.Transparent, 128 | focusedIndicatorColor = Color.Transparent, 129 | unfocusedIndicatorColor = Color.Transparent, 130 | errorIndicatorColor = Color.Transparent 131 | ) 132 | ) 133 | } 134 | } 135 | } 136 | } 137 | 138 | DisposableEffect(Unit) { 139 | onDispose { 140 | if (!isSavedByClick.value && (titleText.value != "")) { 141 | notesViewModel.upsertNote( 142 | Note( 143 | title = titleText.value, 144 | body = bodyText.value, 145 | isFavourite = isPinned.value, 146 | date = getCurrentDate() 147 | ) 148 | ) 149 | } 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/screens/DeletedTodoScreen.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.screens 2 | 3 | import android.app.Activity 4 | import androidx.compose.foundation.isSystemInDarkTheme 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.Spacer 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.height 11 | import androidx.compose.foundation.layout.padding 12 | import androidx.compose.foundation.lazy.LazyColumn 13 | import androidx.compose.foundation.lazy.items 14 | import androidx.compose.material.icons.Icons 15 | import androidx.compose.material.icons.filled.ArrowBack 16 | import androidx.compose.material3.ExperimentalMaterial3Api 17 | import androidx.compose.material3.Icon 18 | import androidx.compose.material3.IconButton 19 | import androidx.compose.material3.MaterialTheme 20 | import androidx.compose.material3.Scaffold 21 | import androidx.compose.material3.Text 22 | import androidx.compose.material3.TopAppBar 23 | import androidx.compose.material3.TopAppBarDefaults.topAppBarColors 24 | import androidx.compose.runtime.Composable 25 | import androidx.compose.runtime.collectAsState 26 | import androidx.compose.ui.Alignment 27 | import androidx.compose.ui.Modifier 28 | import androidx.compose.ui.platform.LocalContext 29 | import androidx.compose.ui.tooling.preview.Preview 30 | import androidx.compose.ui.unit.dp 31 | import androidx.compose.ui.unit.sp 32 | import androidx.navigation.NavHostController 33 | import androidx.navigation.compose.rememberNavController 34 | import com.thatsmanmeet.taskyapp.components.DeletedTodoItem 35 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 36 | import com.thatsmanmeet.taskyapp.room.TodoViewModel 37 | import com.thatsmanmeet.taskyapp.room.deletedtodo.DeletedTodoViewModel 38 | import com.thatsmanmeet.taskyapp.ui.theme.TaskyTheme 39 | 40 | @OptIn(ExperimentalMaterial3Api::class) 41 | @Composable 42 | fun DeletedTodoScreen( 43 | navHostController: NavHostController, 44 | modifier: Modifier = Modifier 45 | ) { 46 | val context = LocalContext.current 47 | val activity = context as Activity 48 | val settingStore = SettingsStore(context) 49 | val savedThemeKey = settingStore.getThemeModeKey.collectAsState(initial = "") 50 | val deletedTodoViewModel = DeletedTodoViewModel(activity.application) 51 | val todoViewModel = TodoViewModel(activity.application) 52 | val deletedTodoList = deletedTodoViewModel.getAllDeletedTodos.collectAsState(initial = emptyList()) 53 | val savedFontKey = settingStore.getUseSystemFontKey.collectAsState(initial = false) 54 | TaskyTheme(darkTheme = when (savedThemeKey.value) { 55 | "0" -> { 56 | isSystemInDarkTheme() 57 | } 58 | "1" -> {false} 59 | else -> {true} 60 | }, 61 | useSystemFont = savedFontKey.value!!) { 62 | Scaffold( 63 | modifier = modifier.fillMaxSize(), 64 | topBar = { 65 | TopAppBar( 66 | title = { 67 | Text(text = "Deleted Tasks") 68 | }, navigationIcon = { 69 | IconButton(onClick = { 70 | navHostController.navigate(route = Screen.MyApp.route){ 71 | popUpTo(route = Screen.MyApp.route){ 72 | inclusive = true 73 | } 74 | } 75 | }) { 76 | Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Arrow back", tint = MaterialTheme.colorScheme.onPrimary) 77 | } 78 | }, 79 | colors = topAppBarColors( 80 | containerColor = MaterialTheme.colorScheme.primary, 81 | titleContentColor = MaterialTheme.colorScheme.onPrimary 82 | ) 83 | ) 84 | } 85 | ) {paddingValues -> 86 | Column( 87 | modifier = modifier.padding(paddingValues), 88 | horizontalAlignment = Alignment.CenterHorizontally 89 | ) { 90 | Box( 91 | modifier = modifier 92 | .fillMaxWidth() 93 | .height(100.dp) 94 | .padding(16.dp) 95 | ){ 96 | Text(text = "Deleted Tasks will be stay here as an archived storage. You can tap on a task to restore or delete it permanently.", fontSize = 15.sp) 97 | } 98 | LazyColumn{ 99 | items(deletedTodoList.value){deletedTodo-> 100 | DeletedTodoItem( 101 | deletedTodo = deletedTodo, 102 | todoViewModel = todoViewModel, 103 | deletedTodoViewModel = deletedTodoViewModel 104 | ) 105 | Spacer(modifier = modifier.height(5.dp)) 106 | } 107 | 108 | } 109 | } 110 | } 111 | } 112 | 113 | } 114 | 115 | @Preview 116 | @Composable 117 | fun PreviewDeletedTodoScreen() { 118 | DeletedTodoScreen(rememberNavController()) 119 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/screens/NavGraph.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.screens 2 | 3 | 4 | import android.transition.Fade 5 | import androidx.compose.animation.AnimatedContentTransitionScope 6 | import androidx.compose.animation.core.tween 7 | import androidx.compose.animation.fadeIn 8 | import androidx.compose.runtime.* 9 | import androidx.compose.ui.platform.LocalContext 10 | import androidx.navigation.NavHostController 11 | import androidx.navigation.NavType 12 | import androidx.navigation.compose.NavHost 13 | import androidx.navigation.compose.composable 14 | import androidx.navigation.navArgument 15 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 16 | 17 | 18 | @Composable 19 | fun SetupNavGraph( 20 | navController:NavHostController 21 | ) { 22 | val context = LocalContext.current 23 | val settingsStore = SettingsStore(context) 24 | val isEnabledState = settingsStore.getTaskListKey.collectAsState(initial = false) 25 | val isAnimationShowingState = settingsStore.getAnimationKey.collectAsState(initial = false) 26 | val is24HourClock = settingsStore.getClockKey.collectAsState(initial = false) 27 | val usSoundPlayingState = settingsStore.getSoundKey.collectAsState(initial = false) 28 | val useSystemFontState = settingsStore.getUseSystemFontKey.collectAsState(initial = false) 29 | val useLegacyDateTimePickersState = settingsStore.getUseLegacyDateTimePickers.collectAsState(initial = false) 30 | NavHost( 31 | navController = navController, 32 | startDestination = Screen.PermissionScreen.route 33 | ){ 34 | composable( 35 | route = Screen.PermissionScreen.route 36 | ){ 37 | PermissionRequestScreen(navController, requestOnClick = { 38 | 39 | }) 40 | } 41 | 42 | composable( 43 | route = Screen.SettingsScreen.route, 44 | enterTransition = { 45 | return@composable slideIntoContainer( 46 | AnimatedContentTransitionScope.SlideDirection.Up, tween(700) 47 | ) 48 | }, exitTransition = { 49 | return@composable slideOutOfContainer( 50 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 51 | ) 52 | }, popEnterTransition = { 53 | return@composable slideIntoContainer( 54 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 55 | ) 56 | } 57 | ){ 58 | SettingsScreen( 59 | navController, 60 | isChecked = isEnabledState, 61 | shouldShowAnimation = isAnimationShowingState, 62 | is24HourClockKey = is24HourClock, 63 | shouldPlaySound = usSoundPlayingState, 64 | useSystemFont = useSystemFontState, 65 | useLegacyDateTimePickers = useLegacyDateTimePickersState 66 | ) 67 | } 68 | composable( 69 | route = Screen.MyApp.route, 70 | enterTransition = { 71 | return@composable fadeIn(tween(700)) 72 | }, 73 | ){ 74 | MyApp(navHostController = navController) 75 | } 76 | 77 | composable( 78 | route = Screen.DeletedTodosScreen.route, 79 | enterTransition = { 80 | return@composable slideIntoContainer( 81 | AnimatedContentTransitionScope.SlideDirection.Up, tween(700) 82 | ) 83 | }, exitTransition = { 84 | return@composable slideOutOfContainer( 85 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 86 | ) 87 | }, popEnterTransition = { 88 | return@composable slideIntoContainer( 89 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 90 | ) 91 | } 92 | ){ 93 | DeletedTodoScreen(navHostController = navController) 94 | } 95 | 96 | composable( 97 | route = Screen.AboutScreen.route, 98 | enterTransition = { 99 | return@composable slideIntoContainer( 100 | AnimatedContentTransitionScope.SlideDirection.Up, tween(700) 101 | ) 102 | }, exitTransition = { 103 | return@composable slideOutOfContainer( 104 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 105 | ) 106 | }, popEnterTransition = { 107 | return@composable slideIntoContainer( 108 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 109 | ) 110 | } 111 | ){ 112 | AboutScreen(navHostController = navController) 113 | } 114 | 115 | composable(route = Screen.NotesScreen.route){ 116 | NotesScreen(navHostController = navController) 117 | } 118 | composable( 119 | route = Screen.AddNotesScreen.route, 120 | enterTransition = { 121 | return@composable slideIntoContainer( 122 | AnimatedContentTransitionScope.SlideDirection.Up, tween(700) 123 | ) 124 | }, exitTransition = { 125 | return@composable slideOutOfContainer( 126 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 127 | ) 128 | }, popEnterTransition = { 129 | return@composable slideIntoContainer( 130 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 131 | ) 132 | } 133 | ){ 134 | AddNoteScreen(navHostController = navController) 135 | } 136 | 137 | composable( 138 | route = Screen.EditNotesScreen.route, 139 | arguments = listOf(navArgument("noteID"){ 140 | type = NavType.LongType 141 | }), 142 | enterTransition = { 143 | return@composable slideIntoContainer( 144 | AnimatedContentTransitionScope.SlideDirection.Up, tween(700) 145 | ) 146 | }, exitTransition = { 147 | return@composable slideOutOfContainer( 148 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 149 | ) 150 | }, popEnterTransition = { 151 | return@composable slideIntoContainer( 152 | AnimatedContentTransitionScope.SlideDirection.Down, tween(700) 153 | ) 154 | } 155 | ){ 156 | val noteId = it.arguments?.getLong("noteID") 157 | EditNoteScreen(navHostController = navController, noteID = noteId) 158 | } 159 | 160 | } 161 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/screens/PermissionRequestScreen.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.screens 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.foundation.layout.* 5 | import androidx.compose.material.icons.Icons 6 | import androidx.compose.material.icons.filled.Notifications 7 | import androidx.compose.material3.Button 8 | import androidx.compose.material3.Icon 9 | import androidx.compose.material3.MaterialTheme 10 | import androidx.compose.material3.Surface 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.collectAsState 14 | import androidx.compose.ui.Alignment 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.platform.LocalContext 17 | import androidx.compose.ui.res.painterResource 18 | import androidx.compose.ui.res.stringResource 19 | import androidx.compose.ui.text.style.TextAlign 20 | import androidx.compose.ui.tooling.preview.Preview 21 | import androidx.compose.ui.unit.dp 22 | import androidx.compose.ui.unit.sp 23 | import androidx.navigation.NavHostController 24 | import androidx.navigation.compose.rememberNavController 25 | import com.thatsmanmeet.taskyapp.R 26 | import com.thatsmanmeet.taskyapp.datastore.SettingsStore 27 | import com.thatsmanmeet.taskyapp.ui.theme.TaskyTheme 28 | 29 | 30 | @Composable 31 | fun PermissionRequestScreen( 32 | navHostController: NavHostController, 33 | requestOnClick: () -> Unit, 34 | modifier: Modifier = Modifier 35 | ){ 36 | val context = LocalContext.current 37 | val settingsStore = SettingsStore(context) 38 | val savedThemeKey = settingsStore.getThemeModeKey.collectAsState(initial = "0") 39 | TaskyTheme( 40 | darkTheme = when (savedThemeKey.value.toString()) { 41 | "null" -> { 42 | isSystemInDarkTheme() 43 | } 44 | "0" -> { 45 | isSystemInDarkTheme() 46 | } 47 | "1" -> {false} 48 | else -> {true} 49 | } 50 | ) { 51 | Surface( 52 | modifier = modifier.fillMaxSize() 53 | ) { 54 | Column( 55 | modifier = modifier 56 | .fillMaxSize() 57 | .padding(16.dp), 58 | horizontalAlignment = Alignment.CenterHorizontally, 59 | verticalArrangement = Arrangement.Center 60 | ) { 61 | Icon( 62 | modifier = modifier.size(50.dp), 63 | painter = painterResource(id = R.drawable.ic_security), 64 | contentDescription = "Permission Required Icon", 65 | tint = MaterialTheme.colorScheme.primary 66 | ) 67 | Spacer(modifier = modifier.height(25.dp)) 68 | Text(text = stringResource(R.string.permission_screen_title), fontSize = 30.sp) 69 | Spacer(modifier = modifier.height(20.dp)) 70 | Column( 71 | horizontalAlignment = Alignment.CenterHorizontally, 72 | verticalArrangement = Arrangement.Center, 73 | modifier = modifier.padding(start = 20.dp, end = 20.dp) 74 | ){ 75 | Row( 76 | horizontalArrangement = Arrangement.SpaceBetween, 77 | verticalAlignment = Alignment.CenterVertically, 78 | ) { 79 | Icon( 80 | imageVector = Icons.Default.Notifications, 81 | contentDescription = "notification permission icon", 82 | modifier = modifier.padding(start = 5.dp), 83 | tint = MaterialTheme.colorScheme.primary 84 | ) 85 | Spacer(modifier = modifier.width(15.dp)) 86 | Text( 87 | modifier = modifier.padding(start = 10.dp), 88 | text = stringResource(R.string.permission_screen_information_text), 89 | fontSize = 14.sp, 90 | textAlign = TextAlign.Start 91 | ) 92 | } 93 | } 94 | Spacer(modifier = modifier.height(20.dp)) 95 | Button(onClick = { 96 | requestOnClick() 97 | }) { 98 | Text(text = stringResource(R.string.permission_screen_request_permission_button)) 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | @Preview(showBackground = true) 106 | @Composable 107 | fun Preview(){ 108 | PermissionRequestScreen( 109 | navHostController = rememberNavController(), 110 | requestOnClick = { 111 | 112 | } 113 | ) 114 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/screens/Screen.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.screens 2 | 3 | sealed class Screen(val route:String){ 4 | object MyApp: Screen(route = "myapp_screen") 5 | object PermissionScreen: Screen("permission_screen") 6 | object SettingsScreen : Screen("settings_screen") 7 | object SearchScreen: Screen("search_screen") 8 | object DeletedTodosScreen:Screen("deleted_todo_screen") 9 | object AboutScreen:Screen("about_screen") 10 | object NotesScreen:Screen("notes_screen") 11 | object AddNotesScreen:Screen("add_notes_screen") 12 | object EditNotesScreen:Screen("edit_notes_screen/{noteID}") 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | import androidx.compose.material3.Typography 18 | 19 | private val DarkColorScheme = darkColorScheme( 20 | primary = Purple80, 21 | secondary = PurpleGrey80, 22 | tertiary = Pink80 23 | ) 24 | 25 | private val LightColorScheme = lightColorScheme( 26 | primary = Purple40, 27 | secondary = PurpleGrey40, 28 | tertiary = Pink40 29 | ) 30 | 31 | val SystemTypography = Typography() 32 | 33 | @Composable 34 | fun TaskyTheme( 35 | darkTheme: Boolean = isSystemInDarkTheme(), 36 | // Dynamic color is available on Android 12+ 37 | dynamicColor: Boolean = true, 38 | useSystemFont: Boolean = true, 39 | content: @Composable () -> Unit 40 | ) { 41 | val colorScheme = when { 42 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 43 | val context = LocalContext.current 44 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 45 | } 46 | darkTheme -> DarkColorScheme 47 | else -> LightColorScheme 48 | } 49 | val view = LocalView.current 50 | if (!view.isInEditMode) { 51 | SideEffect { 52 | val window = (view.context as Activity).window 53 | (view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb() 54 | (view.context as Activity).window.navigationBarColor = colorScheme.surface.toArgb() 55 | WindowCompat.getInsetsController(window,view).isAppearanceLightStatusBars = darkTheme 56 | } 57 | } 58 | 59 | MaterialTheme( 60 | colorScheme = colorScheme, 61 | typography = if (!useSystemFont) Poppins else SystemTypography, 62 | content = content 63 | ) 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.Font 6 | import androidx.compose.ui.text.font.FontFamily 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.unit.sp 9 | import com.thatsmanmeet.taskyapp.R 10 | 11 | // Set of Material typography styles to start with 12 | val Typography = Typography( 13 | bodyLarge = TextStyle( 14 | fontFamily = FontFamily.Default, 15 | fontWeight = FontWeight.Normal, 16 | fontSize = 16.sp, 17 | lineHeight = 24.sp, 18 | letterSpacing = 0.5.sp 19 | ) 20 | 21 | ) 22 | 23 | val poppins = FontFamily( 24 | Font(R.font.poppins_thin,FontWeight.Thin), 25 | Font(R.font.poppins_light,FontWeight.Light), 26 | Font(R.font.poppins_regular,FontWeight.Normal), 27 | Font(R.font.poppins_bold,FontWeight.Bold), 28 | ) 29 | 30 | val Poppins = Typography( 31 | headlineLarge = TextStyle( 32 | fontFamily = poppins, 33 | fontWeight = FontWeight.Bold, 34 | fontSize = 30.sp 35 | ), 36 | headlineSmall = TextStyle( 37 | fontFamily = poppins, 38 | fontWeight = FontWeight.Normal, 39 | fontSize = 24.sp 40 | ), 41 | bodyMedium = TextStyle( 42 | fontFamily = poppins, 43 | fontWeight = FontWeight.Normal, 44 | fontSize = 16.sp 45 | ), 46 | bodySmall = TextStyle( 47 | fontFamily = poppins, 48 | fontWeight = FontWeight.Light, 49 | fontSize = 14.sp 50 | ), 51 | bodyLarge = TextStyle( 52 | fontFamily = poppins, 53 | fontWeight = FontWeight.Normal, 54 | fontSize = 18.sp 55 | ), 56 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/thatsmanmeet/taskyapp/viewmodels/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.taskyapp.viewmodels 2 | 3 | import android.content.ComponentName 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import android.os.Build 8 | import androidx.compose.runtime.mutableStateListOf 9 | import androidx.lifecycle.ViewModel 10 | class MainViewModel : ViewModel() { 11 | 12 | private val permissionDialogQueue = mutableStateListOf() 13 | 14 | 15 | 16 | fun onPermissionResult( 17 | permission:String, 18 | isGranted:Boolean 19 | ){ 20 | if(!isGranted){ 21 | permissionDialogQueue.add(0,permission) 22 | } 23 | } 24 | 25 | fun enableAutoStartIntent(context: Context){ 26 | try { 27 | val intent = Intent() 28 | val manufacturer = Build.MANUFACTURER 29 | when { 30 | "xiaomi".equals(manufacturer, ignoreCase = true) -> { 31 | intent.component = ComponentName( 32 | "com.miui.securitycenter", 33 | "com.miui.permcenter.autostart.AutoStartManagementActivity" 34 | ) 35 | } 36 | "oppo".equals(manufacturer, ignoreCase = true) -> { 37 | intent.component = ComponentName( 38 | "com.coloros.safecenter", 39 | "com.coloros.safecenter.permission.startup.StartupAppListActivity" 40 | ) 41 | } 42 | "vivo".equals(manufacturer, ignoreCase = true) -> { 43 | intent.component = ComponentName( 44 | "com.vivo.permissionmanager", 45 | "com.vivo.permissionmanager.activity.BgStartUpManagerActivity" 46 | ) 47 | } 48 | "letv".equals(manufacturer, ignoreCase = true) -> { 49 | intent.component = ComponentName( 50 | "com.letv.android.letvsafe", 51 | "com.letv.android.letvsafe.AutobootManageActivity" 52 | ) 53 | } 54 | "honor".equals(manufacturer, ignoreCase = true) -> { 55 | intent.component = ComponentName( 56 | "com.huawei.systemmanager", 57 | "com.huawei.systemmanager.optimize.process.ProtectActivity" 58 | ) 59 | } 60 | "asus".equals(manufacturer, ignoreCase = true) -> { 61 | intent.component = ComponentName( 62 | "com.asus.mobilemanager", 63 | "com.asus.mobilemanager.powersaver.PowerSaverSettings" 64 | ) 65 | } 66 | "nokia".equals(manufacturer, ignoreCase = true) -> { 67 | intent.component = ComponentName( 68 | "com.evenwell.powersaving.g3", 69 | "com.evenwell.powersaving.g3.exception.PowerSaverExceptionActivity" 70 | ) 71 | } 72 | "huawei".equals(manufacturer, ignoreCase = true) -> { 73 | intent.component = ComponentName( 74 | "com.huawei.systemmanager", 75 | "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity" 76 | ) 77 | } 78 | } 79 | context.startActivity(intent) 80 | }catch (e:Exception){ 81 | e.printStackTrace() 82 | } 83 | } 84 | 85 | fun openGithubPage(context: Context){ 86 | val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/thatsmanmeet/tasky")) 87 | context.startActivity(intent) 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/cross_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/drawable/cross_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_task.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_battery.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_code.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dark.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_history.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_light.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logo_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notes.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_phone.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_security.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/search_icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/drawable/search_icon32.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/search_icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/drawable/search_icon64.png -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/font/poppins_bold.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/font/poppins_light.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/font/poppins_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/font/poppins_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/poppins_thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/font/poppins_thin.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_logo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_logo_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-hdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-hdpi/ic_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-mdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-mdpi/ic_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-xhdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-xhdpi/ic_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-xxhdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-xxhdpi/ic_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-xxxhdpi/ic_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/mipmap-xxxhdpi/ic_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/raw/completed.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/raw/completed.mp3 -------------------------------------------------------------------------------- /app/src/main/res/raw/deleted.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/raw/deleted.mp3 -------------------------------------------------------------------------------- /app/src/main/res/raw/notification.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/raw/notification.mp3 -------------------------------------------------------------------------------- /app/src/main/res/raw/notifications.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/app/src/main/res/raw/notifications.mp3 -------------------------------------------------------------------------------- /app/src/main/res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tasky 4 | Přidat úkol 5 | Přidat úkol 6 | Co máte na mysli? 7 | Vyberte datum 8 | Vyberte čas 9 | Opakovat každý den 10 | Nastavit připomenutí (volitelné) 11 | Přidat 12 | Zrušit 13 | Upravit úkol 14 | Upravit připomenutí 15 | Uložit 16 | Smazat 17 | Splnili jste svůj dnešní úkol? 18 | Žádné úkoly 19 | Vyžaduje se povolení 20 | Od verze Android 13 a výše, vyžadují aplikace pro zobrazování upozornění povolení k upozornění. 21 | Požádat o povolení 22 | Nastavení 23 | Oddělte úkoly pomocí datumů 24 | Použít animaci dokončení úkolu 25 | Povolit AutoStart 26 | Na některých zařízeních nemusí být upozornění přijímána včas nebo po restartu nemusí fungovat. Klepnutím sem povolíte automatické spuštění. Užitečné pro Xiaomi, Oppo, vivo a zařízení se stock ROM 27 | Zobrazit zdrojový kód 28 | Tasky je aplikace s otevřeným zdrojovým kódem. Máte zpětnou vazbu nebo se chcete pustit do vývoje? navštivte Github! A nezapomeňte dát ⭐️ ;) 29 | Použít 24-hodinový čas 30 | Zálohovat a Obnovit 31 | Zálohujte nebo obnovte své úkoly z lokálního souboru zip. 32 | Použít zvuk dokončení úkolu 33 | Vyberte motiv aplikace 34 | Nastavit motiv aplikace 35 | Změňte aktuální motiv aplikace na světlý, tmavý nebo výchozí podle systému. 36 | vymazat 37 | smazat úkol? 38 | Chcete tento úkol smazat? 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tasky 4 | Aufgabe hinzufügen 5 | Aufgabe hinzufügen 6 | Was geht Ihnen im Kopf rum? 7 | Datum auswählen 8 | Zeitpunkt auswählen 9 | Täglich wiederholen 10 | Erinnerung festlegen (optional) 11 | Hinzufügen 12 | Abbrechen 13 | Aufgabe editieren 14 | Erinnerung editieren 15 | Speichern 16 | Löschen 17 | Haben Sie Ihre Aufgabe heute erledigt ? 18 | Keine Aufgaben 19 | Erlaubnis erforderlich 20 | Beginnend mit Android 13+, Anwendungen benötigen Benachrichtigungserlaubnis um Benachrichtigungen anzuzeigen. 21 | Erlaubnis erforderlich 22 | Einstellungen 23 | Aufgaben trennen durch Verwendung von Daten 24 | Verwendung von Animation bei erfüllter Aufgabe 25 | AutoStart aktivieren 26 | Bei einigen Geräten kommen die Benachrichtigungen nicht rechtzeitig oder funktionieren nicht nach einer Rebootbenachrichtigung. Klicken Sie hier um autostart zu aktivieren. Nützlich für Xiaomi, vivo, Oppo ähnliche Geräte die stock rom verwenden 27 | Quelle ansehen 28 | Tasky ist komplett quelloffen. Für Rückmeldungen oder Beteiligung an der Entwicklung ? besuchen Sie Github! Oh vergessen Sie nicht mir einen ⭐️ zu geben ;) 29 | 24-Stunden-Uhr verwenden 30 | Sicherung/Wiederherstellung 31 | Sicherung oder zu Wiederherstellung ihrer Aufgaben durch eine lokale Zip-Datei 32 | Verwende Töne bei erfüllter Aufgabe 33 | Wählen Sie App-Theme 34 | Wählen Sie App-Theme 35 | Ändern Sie das aktuelle Design der App in hell, dunkel oder Systemstandard. 36 | Löschen 37 | Aufgabe löschen? 38 | Möchten Sie diese Aufgabe löschen? 39 | -------------------------------------------------------------------------------- /app/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tasky 4 | Ajoutez une tâche 5 | Ajoutez une tâche 6 | Quest-ce qui préoccupe votre esprit ? 7 | Sélectionez la date 8 | Sélectionnez lheure 9 | Répétez tous les jours 10 | Définir un rappel (facultatif) 11 | Ajoutez 12 | Annulez 13 | Editez la tâche 14 | Editez le rappel 15 | Sauvegardez 16 | Supprimez 17 | Avez-vous terminé votre tâche aujourd hui ? 18 | Aucune tâche 19 | Autorisation requise 20 | À partir d Android 13+, les applications nécessitent une autorisation de notification pour afficher les notifications. 21 | Demander la permission. 22 | Paramètres 23 | Séparez les tâches en utilisant les dates. 24 | Utilisez complètement les animations. 25 | Activez le démarrage automatique. 26 | Pour certains appareils, les notifications peuvent ne pas être reçues à temps ou ne pas fonctionner après le redémarrage. Cliquez ici pour activer le démarrage automatique. Utile pour Xiaomi, vivo, Oppo comme les appareils exécutant une rom de stockage. 27 | Voir la Source. 28 | Tasky est complètement à source ouverte. Vous avez un retour d expérience ou souhaitez vous lancez dans le développement ? Visitez Github! Oh et n oubliez pas de donnez une ⭐️ ;). 29 | Utiliser l horloge 24 heures. 30 | Sauvegardez/Restaurez. 31 | Sauvegardez ou restaurez vos tâches à partir d un fichier zip local. 32 | Utiliser les sons de tâche. 33 | Sélectionnez le thème de la application. 34 | Définir le thème de la application. 35 | Changez le thème actuel de la application en clair, sombre ou par défaut du système. 36 | Supprimer 37 | Supprimer la tâche? 38 | Voulez-vous supprimer cette tâche? 39 | -------------------------------------------------------------------------------- /app/src/main/res/values-sk/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tasky 4 | Pridať úlohu 5 | Pridať úlohu 6 | Čo máte na mysli? 7 | Vyberte dátum 8 | Vyberte čas 9 | Opakovať každý deň 10 | Nastaviť pripomenutie (voliteľné) 11 | Pridať 12 | Zrušiť 13 | Upraviť úlohu 14 | Upraviť pripomenutie 15 | Uložiť 16 | Zmazať 17 | Splnili ste svoju dnešnú úlohu? 18 | Žiadne úlohy 19 | Vyžaduje sa povolenie 20 | Od verzie Android 13 a vyššie, vyžadujú aplikácie na zobrazovanie upozornení povolenie na upozornenia. 21 | Požiadať o povolenie 22 | Nastavenia 23 | Oddeľte úlohy pomocou dátumov 24 | Použiť animáciu dokončenia úlohy 25 | Povoliť AutoStart 26 | Na niektorých zariadeniach nemusia byť upozornenia prijímané včas alebo po reštarte nemusia fungovať. Kliknutím sem povolíte automatické spustenie. Užitočné pre Xiaomi, Oppo, vivo a zariadenia so stock ROM 27 | Zobraziť zdrojový kód 28 | Tasky je aplikácia s otvoreným zdrojovým kódom. Máte spätnú väzbu alebo sa chcete pustiť do vývoja? navštívte Github! A nezabudnite dať ⭐️ ;) 29 | Použiť 24-hodinový čas 30 | Zálohovať a Obnoviť 31 | Zálohujte alebo obnovte svoje úlohy z lokálneho súboru zip. 32 | Použiť zvuk dokončenia úlohy 33 | Vyberte motív aplikácie 34 | Nastaviť motív aplikácie 35 | Zmeňte aktuálny motív aplikácie na svetlý, tmavý alebo predvolený podľa systému. 36 | Odstrániť 37 | Odstrániť úlohu? 38 | Chcete odstrániť túto úlohu? 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tasky 4 | Görev Ekle 5 | Görev Ekle 6 | Aklında ne var? 7 | Tarih seç 8 | Zaman seç 9 | Her gün tekrarla 10 | Hatırlatıcı ayarla (isteğe bağlı) 11 | Ekle 12 | İptal 13 | Görevi Düzenle 14 | Hatırlatıcıyı Düzenle 15 | Kaydet 16 | Sil 17 | Bugün görevini tamamladın mı? 18 | Görev yok 19 | İzin Gerekli 20 | Android 13+\'dan itibaren uygulamalar bildirimleri göstermek için Bildirim İzni gerektirir. 21 | İzin Gerekli 22 | Ayarlar 23 | Görevleri tarihlerine göre ayır 24 | Görev tamamlama animasyonunu etkinleştir 25 | Otomatik Başlatmayı Etkinleştir 26 | Bazı cihazlarda bildirimler zamanında alınmayabilir veya yeniden başlatıldıktan sonra çalışmayabilir. Otomatik başlatmayı etkinleştirmek için buraya tıklayın. Stok rom çalıştıran Xiaomi, vivo, Oppo gibi cihazlar için kullanışlıdır 27 | Kaynak kodunu görüntüle 28 | Tasky tamamen açık kaynak kodludur. Bir geri bildiriminiz mi var veya geliştirme sürecine dahil olmak mı istiyorsunuz? Github\'ı ziyaret edin! Ve bir de ⭐️ vermeyi unutmayın ;) 29 | 24 Saat Formatı 30 | Yedekle/Geri Yükle 31 | Görevlerinizi yerel bir zip dosyasından yedekleyin veya geri yükleyin. 32 | Görev tamamlamama ses efektini etkinleştir 33 | Uygulama Teması Seç 34 | Uygulama temasını ayarla 35 | Change current theme of the app to light, dark or system default. 36 | Silmek 37 | Görev Silinsin mi? 38 | Bu görevi silmek istiyor musunuz? 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #284D77 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_logo_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #284D77 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Tasky 4 | Add Task 5 | Add Task 6 | What\'s on your mind? 7 | Select Date 8 | Select Time 9 | Repeat Everyday 10 | Set Reminder (optional) 11 | Add 12 | Cancel 13 | Edit Task 14 | Edit Reminder 15 | Save 16 | Delete 17 | Have you completed your task today? 18 | No Tasks 19 | Permission Required 20 | Starting from Android 13+, apps requires Notification Permission to show notifications. 21 | Request Permission 22 | Settings 23 | Separate tasks using dates 24 | Use task complete animations 25 | Enable AutoStart 26 | For some devices, notifications might not be received on time, or may not work after reboot. Click here to enable autostart. Useful for Xiaomi, Oppo, vivo and devices running stock ROM 27 | View Source 28 | Tasky is completely open source. Have a feedback or want to get into development? visit Github! Oh and don\'t forget to give a ⭐️ ;) 29 | Use 24 Hour Clock 30 | Backup/Restore 31 | Backup or Restore your tasks from a local zip file. 32 | Use task complete sounds 33 | Select App Theme 34 | Set app theme 35 | Change current theme of the app to light, dark or system default. 36 | Delete 37 | Delete Task? 38 | Do you want to delete this task? 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/thatsmanmeet/tasky/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.thatsmanmeet.tasky 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 | buildscript { 2 | ext { 3 | compose_version = '1.7.8' 4 | agp_version = '8.0.0' 5 | } 6 | }// Top-level build file where you can add configuration options common to all sub-projects/modules. 7 | plugins { 8 | id 'com.android.application' version '8.0.2' apply false 9 | id 'com.android.library' version '8.9.0' apply false 10 | id 'org.jetbrains.kotlin.android' version '1.9.25' apply false 11 | } -------------------------------------------------------------------------------- /fastlane/metadata/android/de/full_description.txt: -------------------------------------------------------------------------------- 1 |

Tasky ist ein einfaches Aufgabenmanagement-Tool – eine App zur Organisation von ToDo-Listen. Damit lässt sich die Arbeit verwalten und die Zeit richtig einteilen.

Features:

  • Modernes Design.
  • Material You Dynamic Theming.
  • Hinzufügen und Löschen von Sounds.
  • Minimales Design.
  • Vollständig Offline, der Datenschutz steht im Vordergrund.
-------------------------------------------------------------------------------- /fastlane/metadata/android/de/short_description.txt: -------------------------------------------------------------------------------- 1 | Aufgabenverwaltungstool -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/20.txt: -------------------------------------------------------------------------------- 1 | What's New: 2 | 1. Added a toggle to change app theme. 3 | 2. Delete task sound can be disabled from settings toggle. 4 | 3. Updated German Translations. 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/changelogs/6.txt: -------------------------------------------------------------------------------- 1 | What's New: 2 | 1. Added Material 3 themed Icon 3 | 2. General text optimizations 4 | 3. Moved package name from com.thatsmanmeet.tasky to com.thatsmanmeet.taskyapp* 5 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 |

Tasky is a highly intuitive and easy-to-use todo list application designed for those who seek a powerful yet minimalistic way to manage their daily tasks.

2 | 3 | Features ⭐️: 4 |
    5 |
  1. Modern and minimal design
  2. 6 |
  3. Notifications for tasks
  4. 7 |
  5. Material You Dynamic theming
  6. 8 |
  7. Repeating tasks
  8. 9 |
  9. Cool task completion animation
  10. 10 |
  11. Add and Delete Sounds
  12. 11 |
  13. Completely offline making it privacy first
  14. 12 |
  15. Free and Open Source
  16. 13 |
  17. Offline Backups
  18. 14 | 15 |
16 | 17 |

Tasky is a simple todo list app which offers tasks with reminders that helps you remember your tasks and eliminate the concern of forgetting your tasks.

18 | 19 |

Also with the help of repeating tasks or reminders, it makes it easy to form habits by getting notifications everyday about a specified task.

20 | 21 |

With it's minimalistic design and no extra bells and whistles, you can keep organizing your day distraction free.

22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/featureGraphic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/featureGraphic.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/icon.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/tasky1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/phoneScreenshots/tasky1.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/tasky2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/phoneScreenshots/tasky2.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/tasky3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/phoneScreenshots/tasky3.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/tasky4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/phoneScreenshots/tasky4.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/tasky5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/phoneScreenshots/tasky5.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/tasky6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/fastlane/metadata/android/en-US/images/phoneScreenshots/tasky6.jpg -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | Tasky is a highly intuitive and easy-to-use todo list app 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/fr-FR/Courte_description.txt: -------------------------------------------------------------------------------- 1 | Tasky est un simple outil de gestion de tâches pour organiser vos listes de choses à faire. 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/fr-FR/Longue_description.txt: -------------------------------------------------------------------------------- 1 |

Tasky est une application de liste de tâches très intuitive et facile à utiliser, conçue pour ceux qui recherchent un moyen puissant mais minimaliste de gérer leurs tâches quotidiennes.

2 | 3 | Features ⭐️: 4 |
    5 |
  • 1.Moderne et Minimale Design
  • 6 |
  • 2.Notifications pour tâches
  • 7 |
  • 3.Thème dynamique
  • 8 |
  • 4.Tâches répétitives
  • 9 |
  • 5.Cool animation d'achèvement de tâche
  • 10 |
  • 6.Ajoute ou Supprime les Sons
  • 11 |
  • 7.Complètement hors ligne, ce qui en fait la confidentialité d'abbord
  • 12 |
  • 8.Libre et Source Ouverte
  • 13 |
  • 9.Sauvegardes Hors Ligne
  • 14 |
15 |

Avec sont design minimaliste et sans effets de cloches ou sifflets supplémentaires, vous pouvez continuer à organiser votre journée sans distraction.

16 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.defaults.buildfeatures.buildconfig=true 25 | android.nonFinalResIds=false 26 | android.enableR8.fullMode=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 17 17:08:14 IST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/images/banner.png -------------------------------------------------------------------------------- /screenshots/tasky1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/screenshots/tasky1.png -------------------------------------------------------------------------------- /screenshots/tasky2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/screenshots/tasky2.png -------------------------------------------------------------------------------- /screenshots/tasky3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/screenshots/tasky3.png -------------------------------------------------------------------------------- /screenshots/tasky4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thatsmanmeet/Tasky/62be6ecc0466584ca1daee06df216446338193e6/screenshots/tasky4.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "Tasky" 16 | include ':app' 17 | --------------------------------------------------------------------------------