From 465b485120685f8de98040753b7896bb8f0c3187 Mon Sep 17 00:00:00 2001 From: bulkabuka Date: Sat, 1 Jun 2024 17:30:41 +0300 Subject: [PATCH] other: Bug fixes, UI fixes, architecture refinement, export now working properly --- README-en.md | 20 ++- README.md | 22 +++- app/build.gradle.kts | 6 +- app/src/main/AndroidManifest.xml | 8 +- .../classes/{Checks.kt => Verifications.kt} | 2 +- .../uptaskapp/components/TaskListRow.kt | 2 +- .../uptaskapp/components/TaskView.kt | 4 +- .../leftbrained/uptaskapp/dates/DateUtils.kt | 20 +++ .../org/leftbrained/uptaskapp/nav/NavHost.kt | 8 +- .../uptaskapp/{classes => other}/Logs.kt | 2 +- .../uptaskapp/{classes => other}/Stats.kt | 2 +- .../{classes => reminders}/AlarmReceiver.kt | 2 +- .../uptaskapp/reminders/ReminderUtils.kt | 45 +++++++ .../{ => ui}/dialogs/AddTaskDialog.kt | 87 ++++++------- .../{ => ui}/dialogs/AddTaskListDialog.kt | 6 +- .../{ => ui}/dialogs/FilterSortDialog.kt | 2 +- .../{ => ui}/dialogs/ModifyTaskDialog.kt | 99 +++++++-------- .../{ => ui}/dialogs/ModifyTaskListDialog.kt | 6 +- .../{ => ui}/dialogs/SettingsDialog.kt | 2 +- .../{ => ui/screens}/AuthActivity.kt | 20 +-- .../{ => ui/screens}/MainActivity.kt | 19 +-- .../{ => ui/screens}/RegisterActivity.kt | 13 +- .../{ => ui/screens}/StatsActivity.kt | 40 ++++-- .../{ => ui/screens}/TaskActivity.kt | 10 +- .../{ => ui/screens}/TaskListActivity.kt | 120 +++++++++++++----- .../{ => ui/screens}/UserActivity.kt | 94 ++++++++++++-- .../uptaskapp/viewmodel/TaskViewModel.kt | 2 + app/src/main/res/values-ru/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- banner.png => images/banner.png | Bin screens.png => images/screens.png | Bin 33 files changed, 442 insertions(+), 237 deletions(-) rename app/src/main/java/org/leftbrained/uptaskapp/classes/{Checks.kt => Verifications.kt} (99%) create mode 100644 app/src/main/java/org/leftbrained/uptaskapp/dates/DateUtils.kt rename app/src/main/java/org/leftbrained/uptaskapp/{classes => other}/Logs.kt (98%) rename app/src/main/java/org/leftbrained/uptaskapp/{classes => other}/Stats.kt (97%) rename app/src/main/java/org/leftbrained/uptaskapp/{classes => reminders}/AlarmReceiver.kt (96%) create mode 100644 app/src/main/java/org/leftbrained/uptaskapp/reminders/ReminderUtils.kt rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui}/dialogs/AddTaskDialog.kt (85%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui}/dialogs/AddTaskListDialog.kt (95%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui}/dialogs/FilterSortDialog.kt (99%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui}/dialogs/ModifyTaskDialog.kt (85%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui}/dialogs/ModifyTaskListDialog.kt (95%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui}/dialogs/SettingsDialog.kt (97%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/AuthActivity.kt (88%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/MainActivity.kt (93%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/RegisterActivity.kt (94%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/StatsActivity.kt (76%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/TaskActivity.kt (94%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/TaskListActivity.kt (57%) rename app/src/main/java/org/leftbrained/uptaskapp/{ => ui/screens}/UserActivity.kt (66%) rename banner.png => images/banner.png (100%) rename screens.png => images/screens.png (100%) diff --git a/README-en.md b/README-en.md index 6fc2d29..8da1135 100644 --- a/README-en.md +++ b/README-en.md @@ -1,14 +1,22 @@ -![Banner](img/banner.png) +![Banner](images/banner.png) -# ☑️ uptask. +# ☑️ uptask -Welcome to `uptask.`, intuitive **task management app** with dynamic Material You design! +Welcome to `uptask`, intuitive **task management app** with dynamic Material You design! ## Platforms uptask is an Android app, written on `Kotlin + Jetpack Compose`. Data organization implemented through the `H2` engine and a framework `Exposed`. +## Description + +Uptask is built from the ground up, and from the day one it was planned +as a private space for planning. +Therefore it doesn't have such convenient feature as synchronizing. +Nevertheless, uptask is completely safe and secure as well as absolutely free! (even if you are a company) +This way it provides the most independent experience. + ## Features * Local accounts system (registration and authorization) @@ -18,16 +26,16 @@ Data organization implemented through the `H2` engine and a framework `Exposed`. * Full-text search (description and name) * Various stats for the time period * Profile editing +* Extended reminders +* Immediate and versatile data export ## Screenshots -![Screenshots](img/screenshots.png) +![Screenshots](images/screens.png) ## Roadmap * Localization into more languages -* Reminders -* Optimization * Calendar integration [🇷🇺](README.md) diff --git a/README.md b/README.md index a0b6f3f..e8ca9e9 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,41 @@ -![Баннер](img/banner.png) +![Баннер](images/banner.png) -# ☑️ uptask. +# ☑️ uptask -Добро пожаловать в `uptask.`, интуитивный **планировщик задач** с Material You дизайном! +Добро пожаловать в `uptask`, интуитивный **планировщик задач** с Material You дизайном! ## Платформы uptask это Android приложение, написанное на `Kotlin + Jetpack Compose`. Организация данных выполнена при помощи базы данных `H2` и фреймворка `Exposed`. +## Описание + +Uptask построен с нуля как самостоятельная система, и с первого дня задумывался +как приватное пространство для организации своих планов. +Именно поэтому в нем нет привычной системы синхронизации задач. +Тем не менее, uptask является полностью +безопасным и бесплатным при любом использовании - вы не зависите ни от кого. + ## Функции * Локальная система аккаунтов (регистрация и авторизация) * Проекты или списки задач (название и эмодзи) * Комплексная структура задач (название, описание, теги и приоритет, дедлайны) * Гибкая сортировка (по имени, приоритету или дедлайну) -* Полный поиск (описание и название задачи) +* Глубокий поиск (описание и название задачи) * Статистика по задачам за временной период * Редактирование профиля и очистка БД +* Расширенные напоминания до дедлайна +* Моментальный экспорт данных ## Скриншоты -![Скриншоты](img/screenshots.png) +![Скриншоты](images/screens.png) ## Дорожная карта * Локализация на большее количество языков -* Напоминания -* Оптимизация * Интеграция с системным календарем [🇬🇧](README-en.md) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c2ba3c1..88b4979 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -10,7 +10,7 @@ android { defaultConfig { applicationId = "org.leftbrained.uptaskapp" - minSdk = 33 + minSdk = 32 targetSdk = 34 versionCode = 1 versionName = "1.0" @@ -61,14 +61,14 @@ dependencies { implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion") implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.1") implementation("androidx.activity:activity-compose:1.9.0") implementation(platform("androidx.compose:compose-bom:2024.05.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") - implementation("com.google.android.engage:engage-core:1.5.0") + implementation("com.google.android.engage:engage-core:1.5.1") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 77f85c0..03a2251 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,10 +3,7 @@ - - - - + diff --git a/app/src/main/java/org/leftbrained/uptaskapp/classes/Checks.kt b/app/src/main/java/org/leftbrained/uptaskapp/classes/Verifications.kt similarity index 99% rename from app/src/main/java/org/leftbrained/uptaskapp/classes/Checks.kt rename to app/src/main/java/org/leftbrained/uptaskapp/classes/Verifications.kt index bb0e7d9..46363be 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/classes/Checks.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/classes/Verifications.kt @@ -8,7 +8,7 @@ import org.leftbrained.uptaskapp.R import org.leftbrained.uptaskapp.db.Tag import org.leftbrained.uptaskapp.db.UptaskDb -object Checks { +object Verifications { fun emptyCheck( name: String, context: Context diff --git a/app/src/main/java/org/leftbrained/uptaskapp/components/TaskListRow.kt b/app/src/main/java/org/leftbrained/uptaskapp/components/TaskListRow.kt index 95cc970..3fd964a 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/components/TaskListRow.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/components/TaskListRow.kt @@ -25,7 +25,7 @@ import androidx.navigation.NavController import org.jetbrains.exposed.sql.transactions.transaction import org.leftbrained.uptaskapp.db.DatabaseStateViewmodel import org.leftbrained.uptaskapp.db.TaskList -import org.leftbrained.uptaskapp.dialogs.ModifyTaskListDialog +import org.leftbrained.uptaskapp.ui.dialogs.ModifyTaskListDialog @Composable fun TaskListRow(taskList: TaskList, navController: NavController, userId: Int, vm: DatabaseStateViewmodel = viewModel()) { diff --git a/app/src/main/java/org/leftbrained/uptaskapp/components/TaskView.kt b/app/src/main/java/org/leftbrained/uptaskapp/components/TaskView.kt index ead2440..439c81e 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/components/TaskView.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/components/TaskView.kt @@ -35,12 +35,12 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq import org.jetbrains.exposed.sql.transactions.transaction -import org.leftbrained.uptaskapp.classes.Logs +import org.leftbrained.uptaskapp.other.Logs import org.leftbrained.uptaskapp.db.DatabaseStateViewmodel import org.leftbrained.uptaskapp.db.Tag import org.leftbrained.uptaskapp.db.UptaskDb import org.leftbrained.uptaskapp.db.UserTask -import org.leftbrained.uptaskapp.dialogs.ModifyTaskDialog +import org.leftbrained.uptaskapp.ui.dialogs.ModifyTaskDialog import java.util.Locale @Composable diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dates/DateUtils.kt b/app/src/main/java/org/leftbrained/uptaskapp/dates/DateUtils.kt new file mode 100644 index 0000000..46871c9 --- /dev/null +++ b/app/src/main/java/org/leftbrained/uptaskapp/dates/DateUtils.kt @@ -0,0 +1,20 @@ +package org.leftbrained.uptaskapp.dates + +import kotlinx.datetime.Instant +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +import java.util.Locale + +object DateUtils { + fun instantToDate(dueDate: Instant?): String { + return dueDate!!.toLocalDateTime(TimeZone.UTC).let { + "${it.dayOfMonth} ${ + it.month.name.let { name -> + name.substring(0, 1) + .uppercase(Locale.ROOT) + name.substring(1) + .lowercase(Locale.ROOT) + } + }, ${it.year} ${it.hour}:${it.minute}" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/leftbrained/uptaskapp/nav/NavHost.kt b/app/src/main/java/org/leftbrained/uptaskapp/nav/NavHost.kt index c9497de..3bf0bcc 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/nav/NavHost.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/nav/NavHost.kt @@ -13,9 +13,15 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.transactions.transaction -import org.leftbrained.uptaskapp.* import org.leftbrained.uptaskapp.db.UptaskDb import org.leftbrained.uptaskapp.db.connectToDb +import org.leftbrained.uptaskapp.ui.screens.AuthActivity +import org.leftbrained.uptaskapp.ui.screens.RegisterActivity +import org.leftbrained.uptaskapp.ui.screens.StatsActivity +import org.leftbrained.uptaskapp.ui.screens.TaskActivity +import org.leftbrained.uptaskapp.ui.screens.TaskListActivity +import org.leftbrained.uptaskapp.ui.screens.UserActivity +import org.leftbrained.uptaskapp.ui.screens.WelcomeScreen @Composable fun GeneralNav() { diff --git a/app/src/main/java/org/leftbrained/uptaskapp/classes/Logs.kt b/app/src/main/java/org/leftbrained/uptaskapp/other/Logs.kt similarity index 98% rename from app/src/main/java/org/leftbrained/uptaskapp/classes/Logs.kt rename to app/src/main/java/org/leftbrained/uptaskapp/other/Logs.kt index b9be532..403fc36 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/classes/Logs.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/other/Logs.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.classes +package org.leftbrained.uptaskapp.other import android.content.SharedPreferences import kotlinx.datetime.Clock diff --git a/app/src/main/java/org/leftbrained/uptaskapp/classes/Stats.kt b/app/src/main/java/org/leftbrained/uptaskapp/other/Stats.kt similarity index 97% rename from app/src/main/java/org/leftbrained/uptaskapp/classes/Stats.kt rename to app/src/main/java/org/leftbrained/uptaskapp/other/Stats.kt index 77410e4..f4cf0a7 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/classes/Stats.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/other/Stats.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.classes +package org.leftbrained.uptaskapp.other import android.content.SharedPreferences import kotlinx.datetime.LocalDate diff --git a/app/src/main/java/org/leftbrained/uptaskapp/classes/AlarmReceiver.kt b/app/src/main/java/org/leftbrained/uptaskapp/reminders/AlarmReceiver.kt similarity index 96% rename from app/src/main/java/org/leftbrained/uptaskapp/classes/AlarmReceiver.kt rename to app/src/main/java/org/leftbrained/uptaskapp/reminders/AlarmReceiver.kt index b11804e..af5c843 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/classes/AlarmReceiver.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/reminders/AlarmReceiver.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.classes +package org.leftbrained.uptaskapp.reminders import android.app.NotificationManager import android.content.BroadcastReceiver diff --git a/app/src/main/java/org/leftbrained/uptaskapp/reminders/ReminderUtils.kt b/app/src/main/java/org/leftbrained/uptaskapp/reminders/ReminderUtils.kt new file mode 100644 index 0000000..a6e7c66 --- /dev/null +++ b/app/src/main/java/org/leftbrained/uptaskapp/reminders/ReminderUtils.kt @@ -0,0 +1,45 @@ +package org.leftbrained.uptaskapp.reminders + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import org.leftbrained.uptaskapp.dates.DateUtils +import org.leftbrained.uptaskapp.viewmodel.TaskViewModel +import kotlin.time.Duration + +object ReminderUtils { + fun setReminder( + context: Context, + name: String, + desc: String, + dueDate: Instant?, + selectedReminder: Pair + ): Boolean { + val alarmManager = + context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val intent = Intent(context, AlarmReceiver::class.java) + intent.putExtra("taskName", name) + intent.putExtra("taskDesc", desc) + intent.putExtra( + "taskDueDate", + DateUtils.instantToDate(dueDate) + ) + println(DateUtils.instantToDate(dueDate)) + val pendingIntent = PendingIntent.getBroadcast( + context, Clock.System.now().epochSeconds.toInt(), intent, + PendingIntent.FLAG_IMMUTABLE + ) + val reminderMillis = dueDate!! - selectedReminder.second + if (alarmManager.canScheduleExactAlarms()) { + alarmManager.setExact( + AlarmManager.RTC_WAKEUP, + reminderMillis.toEpochMilliseconds(), + pendingIntent + ) + return true + } else return false + } +} \ No newline at end of file diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/AddTaskDialog.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/AddTaskDialog.kt similarity index 85% rename from app/src/main/java/org/leftbrained/uptaskapp/dialogs/AddTaskDialog.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/AddTaskDialog.kt index 9e21750..012bbe3 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/AddTaskDialog.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/AddTaskDialog.kt @@ -1,9 +1,6 @@ -package org.leftbrained.uptaskapp.dialogs +package org.leftbrained.uptaskapp.ui.dialogs -import android.app.AlarmManager -import android.app.PendingIntent import android.content.Context -import android.content.Intent import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -62,19 +59,18 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import org.jetbrains.exposed.sql.transactions.transaction import org.leftbrained.uptaskapp.R -import org.leftbrained.uptaskapp.classes.AlarmReceiver -import org.leftbrained.uptaskapp.classes.Checks -import org.leftbrained.uptaskapp.classes.Checks.emptyCheck -import org.leftbrained.uptaskapp.classes.Checks.priorityCheck -import org.leftbrained.uptaskapp.classes.Checks.tagCheck -import org.leftbrained.uptaskapp.classes.Logs +import org.leftbrained.uptaskapp.classes.Verifications +import org.leftbrained.uptaskapp.classes.Verifications.emptyCheck +import org.leftbrained.uptaskapp.classes.Verifications.priorityCheck +import org.leftbrained.uptaskapp.classes.Verifications.tagCheck +import org.leftbrained.uptaskapp.other.Logs import org.leftbrained.uptaskapp.db.DatabaseStateViewmodel import org.leftbrained.uptaskapp.db.Tag import org.leftbrained.uptaskapp.db.TaskList import org.leftbrained.uptaskapp.db.UserTask +import org.leftbrained.uptaskapp.reminders.ReminderUtils.setReminder import org.leftbrained.uptaskapp.viewmodel.TagViewModel import org.leftbrained.uptaskapp.viewmodel.TaskViewModel -import java.util.Locale import kotlin.time.Duration @OptIn(ExperimentalMaterial3Api::class) @@ -152,13 +148,17 @@ fun AddTaskDialog( value = name, onValueChange = { name = it }, label = { Text(stringResource(R.string.name)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() ) OutlinedTextField( value = desc, onValueChange = { desc = it }, label = { Text(stringResource(R.string.description)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() ) Slider( value = priority.toFloat(), @@ -168,12 +168,17 @@ fun AddTaskDialog( modifier = Modifier.padding(top = 16.dp), ) Text(stringResource(R.string.priority_label, priority)) - Row(verticalAlignment = Alignment.CenterVertically) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) { OutlinedButton( onClick = { showDatePicker = true }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.weight(1f) ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), @@ -188,7 +193,9 @@ fun AddTaskDialog( } TextButton(onClick = { dueDate = null - }) { + }, modifier = Modifier + .weight(0.5f) + .align(Alignment.CenterVertically)) { Text(text = stringResource(R.string.clear)) } } @@ -224,12 +231,17 @@ fun AddTaskDialog( } } } - Row(verticalAlignment = Alignment.CenterVertically) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) { OutlinedButton( onClick = { showTimePicker = true }, - modifier = Modifier.padding(top = 16.dp), + modifier = Modifier.weight(1f) ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), @@ -244,7 +256,9 @@ fun AddTaskDialog( } TextButton(onClick = { dueTime = "" - }) { + }, modifier = Modifier + .weight(0.5f) + .align(Alignment.CenterVertically)) { Text(stringResource(R.string.clear)) } } @@ -285,6 +299,7 @@ fun AddTaskDialog( modifier = Modifier .menuAnchor() .fillMaxWidth() + .padding(top = 12.dp) ) ExposedDropdownMenu( expanded = showReminderDropdown, @@ -319,8 +334,8 @@ fun AddTaskDialog( trailingIcon = { IconButton(onClick = { if (!tagCheck(tagEnter, context)) return@IconButton - if (!Checks.tagAddedCheck(tags, tagEnter, context)) return@IconButton - if (!Checks.tagExistsCheck(tagEnter, context)) return@IconButton + if (!Verifications.tagAddedCheck(tags, tagEnter, context)) return@IconButton + if (!Verifications.tagExistsCheck(tagEnter, context)) return@IconButton transaction { tags.add(tagEnter) } @@ -385,35 +400,7 @@ fun AddTaskDialog( dueDate = dueDate!! + Duration.parse("${timePickerState.hour}h ${timePickerState.minute}m") if (selectedReminder.second != Duration.ZERO) { - val alarmManager = - context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val intent = Intent(context, AlarmReceiver::class.java) - intent.putExtra("taskName", name) - intent.putExtra("taskDesc", desc) - intent.putExtra( - "taskDueDate", - dueDate!!.toLocalDateTime(TimeZone.UTC).let { - "${it.dayOfMonth} ${ - it.month.name.let { name -> - name.substring(0, 1) - .uppercase(Locale.ROOT) + name.substring(1) - .lowercase(Locale.ROOT) - } - }, ${it.year} ${it.hour}:${it.minute}" - }) - val newTaskId = transaction { (UserTask.all().count() + 1).toInt() } - val pendingIntent = PendingIntent.getBroadcast( - context, newTaskId, intent, - PendingIntent.FLAG_IMMUTABLE - ) - val reminderMillis = dueDate!! - selectedReminder.second - if (alarmManager.canScheduleExactAlarms()) { - alarmManager.setExact( - AlarmManager.RTC_WAKEUP, - reminderMillis.toEpochMilliseconds(), - pendingIntent - ) - } + setReminder(context, name, desc, dueDate, selectedReminder) } } transaction { diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/AddTaskListDialog.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/AddTaskListDialog.kt similarity index 95% rename from app/src/main/java/org/leftbrained/uptaskapp/dialogs/AddTaskListDialog.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/AddTaskListDialog.kt index ac98312..9fa963b 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/AddTaskListDialog.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/AddTaskListDialog.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.dialogs +package org.leftbrained.uptaskapp.ui.dialogs import android.widget.Toast import androidx.compose.foundation.layout.Arrangement @@ -63,13 +63,13 @@ fun AddTaskListDialog(onDismissRequest: () -> Unit, userId: Int, vm: DatabaseSta value = name, onValueChange = { name = it }, label = { Text(stringResource(R.string.name)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp).fillMaxWidth() ) OutlinedTextField( value = emoji, onValueChange = { emoji = it }, label = { Text(stringResource(R.string.emoji)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp).fillMaxWidth() ) Row( horizontalArrangement = Arrangement.spacedBy(16.dp), diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/FilterSortDialog.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/FilterSortDialog.kt similarity index 99% rename from app/src/main/java/org/leftbrained/uptaskapp/dialogs/FilterSortDialog.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/FilterSortDialog.kt index 3e6bcf2..89ceb6e 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/FilterSortDialog.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/FilterSortDialog.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.dialogs +package org.leftbrained.uptaskapp.ui.dialogs import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/ModifyTaskDialog.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/ModifyTaskDialog.kt similarity index 85% rename from app/src/main/java/org/leftbrained/uptaskapp/dialogs/ModifyTaskDialog.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/ModifyTaskDialog.kt index ebbc39c..c02a86c 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/ModifyTaskDialog.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/ModifyTaskDialog.kt @@ -1,9 +1,6 @@ -package org.leftbrained.uptaskapp.dialogs +package org.leftbrained.uptaskapp.ui.dialogs -import android.app.AlarmManager -import android.app.PendingIntent import android.content.Context -import android.content.Intent import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -64,18 +61,17 @@ import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime import org.jetbrains.exposed.sql.transactions.transaction import org.leftbrained.uptaskapp.R -import org.leftbrained.uptaskapp.classes.AlarmReceiver -import org.leftbrained.uptaskapp.classes.Checks -import org.leftbrained.uptaskapp.classes.Checks.tagAddedCheck -import org.leftbrained.uptaskapp.classes.Checks.tagCheck -import org.leftbrained.uptaskapp.classes.Checks.tagExistsCheck -import org.leftbrained.uptaskapp.classes.Logs +import org.leftbrained.uptaskapp.classes.Verifications +import org.leftbrained.uptaskapp.classes.Verifications.tagAddedCheck +import org.leftbrained.uptaskapp.classes.Verifications.tagCheck +import org.leftbrained.uptaskapp.classes.Verifications.tagExistsCheck +import org.leftbrained.uptaskapp.other.Logs import org.leftbrained.uptaskapp.db.DatabaseStateViewmodel import org.leftbrained.uptaskapp.db.Tag import org.leftbrained.uptaskapp.db.UptaskDb import org.leftbrained.uptaskapp.db.UserTask +import org.leftbrained.uptaskapp.reminders.ReminderUtils import org.leftbrained.uptaskapp.viewmodel.TaskViewModel -import java.util.Locale import kotlin.time.Duration @OptIn(ExperimentalMaterial3Api::class) @@ -167,13 +163,17 @@ fun ModifyTaskDialog( value = name, onValueChange = { name = it }, label = { Text(stringResource(R.string.name)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() ) OutlinedTextField( value = desc ?: "", onValueChange = { desc = it }, label = { Text(stringResource(R.string.description)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() ) Slider( value = priority.toFloat(), @@ -183,11 +183,16 @@ fun ModifyTaskDialog( modifier = Modifier.padding(top = 16.dp), ) Text(stringResource(R.string.priority_label, priority)) - Row(verticalAlignment = Alignment.CenterVertically) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) { OutlinedButton( onClick = { showDatePicker = true - }, modifier = Modifier.padding(top = 16.dp) + }, modifier = Modifier.weight(1f) ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), @@ -202,7 +207,9 @@ fun ModifyTaskDialog( } TextButton(onClick = { dueDate = null - }) { + }, modifier = Modifier + .align(Alignment.CenterVertically) + .weight(0.5f)) { Text(stringResource(R.string.clear)) } } @@ -238,11 +245,16 @@ fun ModifyTaskDialog( } } } - Row(verticalAlignment = Alignment.CenterVertically) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + ) { OutlinedButton( onClick = { showTimePicker = true - }, modifier = Modifier.padding(top = 16.dp) + }, modifier = Modifier.weight(1f) ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), @@ -257,7 +269,9 @@ fun ModifyTaskDialog( } TextButton(onClick = { dueTime = "" - }) { + }, modifier = Modifier + .align(Alignment.CenterVertically) + .weight(0.5f)) { Text(stringResource(R.string.clear)) } } @@ -298,6 +312,7 @@ fun ModifyTaskDialog( modifier = Modifier .menuAnchor() .fillMaxWidth() + .padding(top = 12.dp) ) ExposedDropdownMenu(expanded = showReminderDropdown, onDismissRequest = { showReminderDropdown = false }) { @@ -355,11 +370,11 @@ fun ModifyTaskDialog( onClick = {}, label = { Text(stringResource(R.string.pick_attachment)) }, leadingIcon = { - Icon( - imageVector = Icons.Rounded.AddCircle, - contentDescription = "Add attachment icon" - ) - }) + Icon( + imageVector = Icons.Rounded.AddCircle, + contentDescription = "Add attachment icon" + ) + }) } Row( Modifier @@ -396,39 +411,19 @@ fun ModifyTaskDialog( .padding(top = 16.dp) ) { Button(onClick = { - if (!Checks.emptyCheck(name, context)) return@Button - if (!Checks.priorityCheck(priority, context)) return@Button + if (!Verifications.emptyCheck(name, context)) return@Button + if (!Verifications.priorityCheck(priority, context)) return@Button if (dueDate != null) { dueDate = dueDate!! + Duration.parse("${timePickerState.hour}h ${timePickerState.minute}m") if (selectedReminder.second != Duration.ZERO) { - val alarmManager = - context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val intent = Intent(context, AlarmReceiver::class.java) - intent.putExtra("taskName", name) - intent.putExtra("taskDesc", desc) - intent.putExtra("taskDueDate", - dueDate!!.toLocalDateTime(TimeZone.UTC).let { - "${it.dayOfMonth} ${ - it.month.name.let { name -> - name.substring(0, 1) - .uppercase(Locale.ROOT) + name.substring(1) - .lowercase(Locale.ROOT) - } - }, ${it.year} ${it.hour}:${it.minute}" - }) - val newTaskId = transaction { (UserTask.all().count() + 1).toInt() } - val pendingIntent = PendingIntent.getBroadcast( - context, newTaskId, intent, PendingIntent.FLAG_IMMUTABLE + ReminderUtils.setReminder( + context, + name, + desc ?: "None", + dueDate, + selectedReminder ) - val reminderMillis = dueDate!! - selectedReminder.second - if (alarmManager.canScheduleExactAlarms()) { - alarmManager.setExact( - AlarmManager.RTC_WAKEUP, - reminderMillis.toEpochMilliseconds(), - pendingIntent - ) - } } } transaction { diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/ModifyTaskListDialog.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/ModifyTaskListDialog.kt similarity index 95% rename from app/src/main/java/org/leftbrained/uptaskapp/dialogs/ModifyTaskListDialog.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/ModifyTaskListDialog.kt index fe33c7b..5c43eb2 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/ModifyTaskListDialog.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/ModifyTaskListDialog.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.dialogs +package org.leftbrained.uptaskapp.ui.dialogs import android.widget.Toast import androidx.compose.foundation.layout.* @@ -59,13 +59,13 @@ fun ModifyTaskListDialog(onDismissRequest: () -> Unit, taskListId: Int, vm: Data value = name, onValueChange = { name = it }, label = { Text(stringResource(R.string.name)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp).fillMaxWidth() ) OutlinedTextField( value = emoji, onValueChange = { emoji = it }, label = { Text(stringResource(R.string.emoji)) }, - modifier = Modifier.padding(top = 16.dp) + modifier = Modifier.padding(top = 16.dp).fillMaxWidth() ) Row( horizontalArrangement = Arrangement.spacedBy(16.dp), diff --git a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/SettingsDialog.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/SettingsDialog.kt similarity index 97% rename from app/src/main/java/org/leftbrained/uptaskapp/dialogs/SettingsDialog.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/SettingsDialog.kt index c3a8ff9..4c59f33 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/dialogs/SettingsDialog.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/dialogs/SettingsDialog.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp.dialogs +package org.leftbrained.uptaskapp.ui.dialogs import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer diff --git a/app/src/main/java/org/leftbrained/uptaskapp/AuthActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/AuthActivity.kt similarity index 88% rename from app/src/main/java/org/leftbrained/uptaskapp/AuthActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/AuthActivity.kt index 344663c..b0bd691 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/AuthActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/AuthActivity.kt @@ -1,12 +1,11 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import android.app.Activity import android.content.Context import android.content.ContextWrapper -import android.content.SharedPreferences -import android.widget.Toast import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowForward import androidx.compose.material.icons.rounded.* import androidx.compose.material3.* import androidx.compose.runtime.* @@ -15,14 +14,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import androidx.navigation.compose.rememberNavController import org.jetbrains.exposed.sql.transactions.transaction -import org.leftbrained.uptaskapp.classes.Checks.checkAuth -import org.leftbrained.uptaskapp.db.UptaskDb -import org.leftbrained.uptaskapp.db.User +import org.leftbrained.uptaskapp.R +import org.leftbrained.uptaskapp.classes.Verifications.checkAuth import org.leftbrained.uptaskapp.db.connectToDb import org.leftbrained.uptaskapp.ui.theme.AppTheme import org.leftbrained.uptaskapp.viewmodel.UserViewModel @@ -118,7 +114,7 @@ fun AuthActivity(navController: NavController) { }) { Text(text = stringResource(R.string.sign_in)) Spacer(Modifier.size(8.dp)) - Icon(Icons.Rounded.ArrowForward, "Arrow forward icon") + Icon(Icons.AutoMirrored.Rounded.ArrowForward, "Arrow forward icon") } Text( text = stringResource(R.string.don_t_have_an_account), @@ -131,10 +127,4 @@ fun AuthActivity(navController: NavController) { } } } -} - -@Preview(device = "spec:width=411dp,height=891dp", showSystemUi = true) -@Composable -fun AuthActivityPreview() { - AuthActivity(rememberNavController()) } \ No newline at end of file diff --git a/app/src/main/java/org/leftbrained/uptaskapp/MainActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/MainActivity.kt similarity index 93% rename from app/src/main/java/org/leftbrained/uptaskapp/MainActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/MainActivity.kt index 195c4cd..f45de8f 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/MainActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/MainActivity.kt @@ -1,8 +1,9 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context +import android.content.Intent import android.content.res.Configuration import android.os.Bundle import androidx.activity.ComponentActivity @@ -33,7 +34,11 @@ import androidx.compose.ui.tooling.preview.Wallpapers import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController +import org.jetbrains.exposed.sql.transactions.transaction +import org.leftbrained.uptaskapp.R +import org.leftbrained.uptaskapp.classes.Exporter import org.leftbrained.uptaskapp.components.UnorderedList +import org.leftbrained.uptaskapp.db.User import org.leftbrained.uptaskapp.db.connectToDb import org.leftbrained.uptaskapp.nav.GeneralNav import org.leftbrained.uptaskapp.ui.theme.AppTheme @@ -124,16 +129,4 @@ fun WelcomeScreen(navController: NavController) { UnorderedList(stringResource(R.string.create_reminders)) } } -} - -@Preview( - showBackground = true, device = "id:pixel_7_pro", showSystemUi = true, - uiMode = Configuration.UI_MODE_NIGHT_NO or Configuration.UI_MODE_TYPE_NORMAL, - wallpaper = Wallpapers.BLUE_DOMINATED_EXAMPLE -) -@Composable -fun MainActivityPreview() { - AppTheme { - WelcomeScreen(rememberNavController()) - } } \ No newline at end of file diff --git a/app/src/main/java/org/leftbrained/uptaskapp/RegisterActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/RegisterActivity.kt similarity index 94% rename from app/src/main/java/org/leftbrained/uptaskapp/RegisterActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/RegisterActivity.kt index 0a5636c..4ef7ea7 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/RegisterActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/RegisterActivity.kt @@ -1,9 +1,10 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import android.content.Context import android.widget.Toast import androidx.compose.foundation.layout.* import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowForward import androidx.compose.material.icons.rounded.* import androidx.compose.material3.* import androidx.compose.runtime.* @@ -16,7 +17,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController import org.jetbrains.exposed.sql.transactions.transaction -import org.leftbrained.uptaskapp.db.UptaskDb +import org.leftbrained.uptaskapp.R import org.leftbrained.uptaskapp.db.User import org.leftbrained.uptaskapp.db.connectToDb import org.leftbrained.uptaskapp.ui.theme.AppTheme @@ -118,7 +119,7 @@ fun RegisterActivity(navController: NavController) { ) { Text(text = stringResource(R.string.sign_up)) Spacer(Modifier.size(8.dp)) - Icon(Icons.Rounded.ArrowForward, "Arrow forward icon") + Icon(Icons.AutoMirrored.Rounded.ArrowForward, "Arrow forward icon") } Text( stringResource(R.string.want_to_sign_in_instead), @@ -131,10 +132,4 @@ fun RegisterActivity(navController: NavController) { } } } -} - -@Preview(showSystemUi = true) -@Composable -fun RegisterActivityPreview() { - RegisterActivity(navController = NavController(LocalContext.current)) } \ No newline at end of file diff --git a/app/src/main/java/org/leftbrained/uptaskapp/StatsActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/StatsActivity.kt similarity index 76% rename from app/src/main/java/org/leftbrained/uptaskapp/StatsActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/StatsActivity.kt index dd36e77..63cd978 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/StatsActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/StatsActivity.kt @@ -1,6 +1,7 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import android.content.Context +import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -30,15 +31,18 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.navigation.NavController import kotlinx.datetime.Instant import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime -import org.leftbrained.uptaskapp.classes.Stats +import org.leftbrained.uptaskapp.R +import org.leftbrained.uptaskapp.other.Stats @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -74,7 +78,7 @@ fun StatsActivity(navController: NavController, userId: Int) { Row( Modifier .height(500.dp) - .padding(8.dp) + .padding(24.dp) .background( color = MaterialTheme.colorScheme.surfaceVariant, shape = RoundedCornerShape(8.dp) @@ -82,11 +86,20 @@ fun StatsActivity(navController: NavController, userId: Int) { .padding(8.dp) ) { DateRangePicker( - state = pickerState + state = pickerState, + modifier = Modifier.height(400.dp) ) } Button( onClick = { + if (pickerState.selectedStartDateMillis == null || pickerState.selectedEndDateMillis == null) { + Toast.makeText( + navController.context, + navController.context.getString(R.string.no_date_period_selected), + Toast.LENGTH_SHORT + ).show() + return@Button + } val startDate = Instant.fromEpochMilliseconds(pickerState.selectedStartDateMillis!!) .toLocalDateTime( @@ -98,17 +111,26 @@ fun StatsActivity(navController: NavController, userId: Int) { ) currentStats = currentStats.checkStats(sharedPref, startDate, endDate, userId) }, - modifier = Modifier.padding(8.dp) + modifier = Modifier.padding(start = 24.dp) ) { Text(stringResource(R.string.apply)) } - Column(Modifier.padding(16.dp)) { + Column( + Modifier + .padding(16.dp) + .background( + MaterialTheme.colorScheme.tertiaryContainer, + shape = RoundedCornerShape(8.dp) + ) + .padding(16.dp)) { Text( - textAlign = TextAlign.Center, modifier = Modifier - .fillMaxWidth(), + .fillMaxWidth() + .padding(bottom = 12.dp), text = stringResource(R.string.stats_period), - style = MaterialTheme.typography.titleLarge + fontWeight = FontWeight.SemiBold, + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.tertiary ) Text( stringResource(R.string.total_tasks_added, currentStats.totalTasks) diff --git a/app/src/main/java/org/leftbrained/uptaskapp/TaskActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/TaskActivity.kt similarity index 94% rename from app/src/main/java/org/leftbrained/uptaskapp/TaskActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/TaskActivity.kt index adaf2e5..d769c0f 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/TaskActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/TaskActivity.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -15,13 +15,11 @@ import androidx.navigation.NavController import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.transactions.transaction -import org.leftbrained.uptaskapp.classes.Exporter import org.leftbrained.uptaskapp.components.TaskView import org.leftbrained.uptaskapp.db.* -import org.leftbrained.uptaskapp.dialogs.AddTaskDialog -import org.leftbrained.uptaskapp.dialogs.FilterSortDialog -import org.leftbrained.uptaskapp.dialogs.SettingsDialog -import org.leftbrained.uptaskapp.viewmodel.UserViewModel +import org.leftbrained.uptaskapp.ui.dialogs.AddTaskDialog +import org.leftbrained.uptaskapp.ui.dialogs.FilterSortDialog +import org.leftbrained.uptaskapp.ui.dialogs.SettingsDialog @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/org/leftbrained/uptaskapp/TaskListActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/TaskListActivity.kt similarity index 57% rename from app/src/main/java/org/leftbrained/uptaskapp/TaskListActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/TaskListActivity.kt index 0118a29..3913bcb 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/TaskListActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/TaskListActivity.kt @@ -1,31 +1,41 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import android.app.Activity +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context -import android.os.Environment -import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.rounded.Add +import androidx.compose.material.icons.rounded.ContentCopy import androidx.compose.material.icons.rounded.DateRange import androidx.compose.material.icons.rounded.Download import androidx.compose.material.icons.rounded.Person import androidx.compose.material.icons.rounded.Settings import androidx.compose.material3.BottomAppBar import androidx.compose.material3.BottomAppBarDefaults +import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable @@ -38,10 +48,13 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import org.jetbrains.exposed.sql.transactions.transaction +import org.leftbrained.uptaskapp.R import org.leftbrained.uptaskapp.classes.Exporter import org.leftbrained.uptaskapp.components.TaskListRow import org.leftbrained.uptaskapp.db.DatabaseStateViewmodel @@ -49,11 +62,8 @@ import org.leftbrained.uptaskapp.db.TaskList import org.leftbrained.uptaskapp.db.UptaskDb import org.leftbrained.uptaskapp.db.User import org.leftbrained.uptaskapp.db.connectToDb -import org.leftbrained.uptaskapp.dialogs.AddTaskListDialog -import org.leftbrained.uptaskapp.dialogs.SettingsDialog -import java.io.File -import java.io.FileOutputStream -import java.io.OutputStreamWriter +import org.leftbrained.uptaskapp.ui.dialogs.AddTaskListDialog +import org.leftbrained.uptaskapp.ui.dialogs.SettingsDialog @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -78,6 +88,7 @@ fun TaskListActivity( } } } + var showExported by remember { mutableStateOf(false) } Scaffold(topBar = { TopAppBar( title = { @@ -120,9 +131,11 @@ fun TaskListActivity( } IconButton( onClick = { - navController.navigate( - "user/$userId" - ) + transaction { + navController.navigate( + "user/$userId" + ) + } } ) { Icon( @@ -130,26 +143,75 @@ fun TaskListActivity( contentDescription = "Profile tab" ) } + if (showExported) { + Dialog(onDismissRequest = { showExported = false }) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + shape = RoundedCornerShape(16.dp), + ) { + val scrollState = rememberScrollState() + val exportedData = transaction { + Exporter().dataToMarkdown(User.findById(userId)!!.login) + } + Column( + Modifier + .padding(16.dp) + .verticalScroll(scrollState) + ) { + Text( + text = stringResource(R.string.here_is_your_export), + style = MaterialTheme.typography.titleLarge, + modifier = Modifier + .padding(bottom = 16.dp) + .fillMaxWidth(), + textAlign = TextAlign.Center + ) + Text( + text = stringResource(R.string.click_copy), + modifier = Modifier.fillMaxWidth().padding(bottom = 24.dp), + textAlign = TextAlign.Center, + ) + OutlinedTextField( + value = exportedData, + enabled = true, + modifier = Modifier.fillMaxWidth(), + onValueChange = {}, + singleLine = true, + label = { Text("Exported data") } + ) + Row( + Modifier.padding(top = 12.dp), + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + TextButton(onClick = { + val clipboardManager = context.getSystemService( + Context.CLIPBOARD_SERVICE + ) as ClipboardManager + clipboardManager.setPrimaryClip( + ClipData.newPlainText( + "Exported data", + exportedData + ) + ) + showExported = false + }) { + Text(stringResource(R.string.copy)) + } + TextButton(onClick = { + showExported = false + }) { + Text(stringResource(R.string.cancel)) + } + } + } + } + } + } IconButton( onClick = { - val exporter = Exporter() - val exported = - transaction { exporter.dataToMarkdown(User.findById(userId)!!.login) } - val documentsDir = - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) - val file = File(documentsDir, "uptask_export_data.txt") - - val fileOutputStream = FileOutputStream(file) - val outputStreamWriter = OutputStreamWriter(fileOutputStream) - - outputStreamWriter.write(exported) - outputStreamWriter.close() - Toast.makeText( - context, - "Data exported to file in the Documents folder", - Toast.LENGTH_LONG - ).show( - ) + showExported = true } ) { Icon( diff --git a/app/src/main/java/org/leftbrained/uptaskapp/UserActivity.kt b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/UserActivity.kt similarity index 66% rename from app/src/main/java/org/leftbrained/uptaskapp/UserActivity.kt rename to app/src/main/java/org/leftbrained/uptaskapp/ui/screens/UserActivity.kt index a854d2d..53f6ded 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/UserActivity.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/ui/screens/UserActivity.kt @@ -1,4 +1,4 @@ -package org.leftbrained.uptaskapp +package org.leftbrained.uptaskapp.ui.screens import android.app.Activity import android.content.Context @@ -8,13 +8,18 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowLeft +import androidx.compose.material.icons.rounded.Clear +import androidx.compose.material.icons.rounded.Email +import androidx.compose.material.icons.rounded.Lock import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -34,6 +39,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import org.jetbrains.exposed.sql.deleteAll import org.jetbrains.exposed.sql.transactions.transaction +import org.leftbrained.uptaskapp.R import org.leftbrained.uptaskapp.db.UptaskDb import org.leftbrained.uptaskapp.db.User import org.leftbrained.uptaskapp.db.connectToDb @@ -49,6 +55,18 @@ fun UserActivity(navController: NavController, userId: Int) { val user = User.findById(userId) user?.login ?: context.getString(R.string.no_login) } + var isClearedUsername by remember { + mutableStateOf(false) + } + var isClearedPassword by remember { + mutableStateOf(false) + } + var isClearedPassCur by remember { + mutableStateOf(false) + } + var isSure by remember { + mutableStateOf(false) + } var userNameEdit by remember { mutableStateOf(userName) } var password by remember { mutableStateOf("") } var currentPass by remember { mutableStateOf("") } @@ -99,31 +117,68 @@ fun UserActivity(navController: NavController, userId: Int) { ) OutlinedTextField( value = userNameEdit, - onValueChange = { userNameEdit = it }, - label = { Text(stringResource(R.string.login)) } + onValueChange = { + userNameEdit = it + isClearedUsername = it.isNotEmpty() + }, + label = { Text(stringResource(R.string.login)) }, + modifier = Modifier.fillMaxWidth(), + leadingIcon = { Icon(Icons.Rounded.Email, "Password icon") }, + trailingIcon = { + IconButton( + onClick = { userNameEdit = "" }, + enabled = isClearedUsername + ) { Icon(Icons.Rounded.Clear, "Clear icon") } + }, ) OutlinedTextField( value = currentPass, - onValueChange = { currentPass = it }, - label = { Text(stringResource(R.string.current_password)) } - ) + onValueChange = { + currentPass = it + isClearedPassCur = it.isNotEmpty() + }, + label = { Text(stringResource(R.string.current_password)) }, + modifier = Modifier.fillMaxWidth(), + leadingIcon = { Icon(Icons.Rounded.Lock, "Password icon") }, + trailingIcon = { + IconButton( + onClick = { currentPass = "" }, + enabled = isClearedPassCur + ) { Icon(Icons.Rounded.Clear, "Clear icon") } + }, + + ) OutlinedTextField( value = password, - onValueChange = { password = it }, - label = { Text(stringResource(R.string.new_password)) } + onValueChange = { + password = it + isClearedPassword = it.isNotEmpty() + }, + label = { Text(stringResource(R.string.new_password)) }, + modifier = Modifier.fillMaxWidth(), + leadingIcon = { Icon(Icons.Rounded.Lock, "Password icon") }, + trailingIcon = { + IconButton( + onClick = { password = "" }, + enabled = isClearedPassword + ) { Icon(Icons.Rounded.Clear, "Clear icon") } + }, ) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Button( onClick = { transaction { val user = User.findById(userId) - println( - "User: ${user?.login}, ${user?.password}, $currentPass, $password" - ) if (user?.password == currentPass) { if (password.isNotEmpty() && password != currentPass && userName.isNotEmpty()) { user.login = userNameEdit user.password = password + Toast.makeText( + navController.context, + context.getString(R.string.info_changed), + Toast.LENGTH_SHORT + ).show() + navController.navigate("taskList/$userId") } else { Toast.makeText( navController.context, @@ -139,12 +194,22 @@ fun UserActivity(navController: NavController, userId: Int) { ).show() } } - } + }, + modifier = Modifier.weight(1f) ) { Text(stringResource(R.string.save)) } - Button( + OutlinedButton( onClick = { + if (!isSure) { + isSure = true + Toast.makeText( + navController.context, + context.getString(R.string.is_sure), + Toast.LENGTH_LONG + ).show() + return@OutlinedButton + } transaction { UptaskDb.Users.deleteAll() UptaskDb.UserTasks.deleteAll() @@ -162,7 +227,8 @@ fun UserActivity(navController: NavController, userId: Int) { apply() } navController.navigate("auth") - } + }, + modifier = Modifier.weight(1f) ) { Text(stringResource(R.string.clear_database)) } diff --git a/app/src/main/java/org/leftbrained/uptaskapp/viewmodel/TaskViewModel.kt b/app/src/main/java/org/leftbrained/uptaskapp/viewmodel/TaskViewModel.kt index ba89cc6..7463f8e 100644 --- a/app/src/main/java/org/leftbrained/uptaskapp/viewmodel/TaskViewModel.kt +++ b/app/src/main/java/org/leftbrained/uptaskapp/viewmodel/TaskViewModel.kt @@ -37,4 +37,6 @@ class TaskViewModel : ViewModel() { fun removeTask(taskId: Int) = transaction { UserTask.find(UptaskDb.UserTasks.id eq taskId).elementAt(0).delete() } + + fun getNextTask() = transaction { (UserTask.all().count() + 1).toInt() } } \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ad926d6..fab471d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -89,4 +89,10 @@ 1 день Статистика Нет + Если вы уверены, нажмите еще раз + Не выбран временной период + Копировать + Нажмите, чтобы скопировать его + Вот ваш экспорт + Данные были изменены \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 518f3f2..20f1b58 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -89,4 +89,10 @@ 1 day Stats None + If you\'re sure, press again + No date period selected + Copy + Click copy to save it to your clipboard + Here is your export + Info has been changed \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 7771714..8e17b5e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id("com.android.application") version "8.1.1" apply false + id("com.android.application") version "8.4.0" apply false id("org.jetbrains.kotlin.android") version "1.8.10" apply false id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10" apply false } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 64f40d5..53c5b61 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Nov 01 19:00:32 MSK 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/banner.png b/images/banner.png similarity index 100% rename from banner.png rename to images/banner.png diff --git a/screens.png b/images/screens.png similarity index 100% rename from screens.png rename to images/screens.png