Compare commits

..

75 Commits
main ... lab5

Author SHA1 Message Date
524c1f135a Дай бог здоровья Андрею, помог отчистить кеш для отчета 2023-12-28 14:23:04 +04:00
f70e94584e Не пон... 2023-12-27 14:09:45 +04:00
a4676f90eb Merge remote-tracking branch 'origin/lab5' into lab5
# Conflicts:
#	app/src/main/java/com/example/myapplication/api/MyServerService.kt
#	app/src/main/java/com/example/myapplication/database/AppViewModelProvider.kt
#	app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEdit.kt
#	app/src/main/java/com/example/myapplication/database/entities/composeui/edit/BikeEditViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/bike/BikeListViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/bike/BikeViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/cart/CartViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/item/ItemEditViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/item/ItemListViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/rent/RentListViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/rent/RentViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/rent/ReportViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/user/CurrentUserViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/user/EntryUserViewModel.kt
#	app/src/main/java/com/example/myapplication/database/room/viewmodel/user/RegisterUserViewModel.kt
#	app/src/main/java/com/example/myapplication/ui/edit/ItemEdit.kt
#	app/src/main/java/com/example/myapplication/ui/list/BikeList.kt
#	app/src/main/java/com/example/myapplication/ui/list/ItemList.kt
#	app/src/main/java/com/example/myapplication/ui/navigation/MainNavbar.kt
#	app/src/main/java/com/example/myapplication/ui/view/BikeView.kt
#	app/src/main/res/xml/network_security_config.xml
#	server/data.json
2023-12-27 14:08:37 +04:00
3242f68787 Так, убрал анимацию пагинаций, вроде все работает, остался рефакторинг, отчет и пабеда.... 2023-12-27 14:07:10 +04:00
8d30b2eddb ой 2023-12-27 11:14:42 +04:00
eb0ca104f1 папочка 2023-12-27 11:14:37 +04:00
afc8625f3f ой 2023-12-27 03:19:06 +04:00
f977aa8f09 папочка 2023-12-27 03:17:40 +04:00
68d533c81b Сделал на 2 листа пагинацию, хохо 2023-12-27 01:15:26 +04:00
c7f73c4d5a Такс, подшаманил дезигн, по сути остолось только 2........... Сделать прогрузку, валидацию и поиск(надеюсь, успею...) 2023-12-26 22:32:27 +04:00
c7ffbb6ca5 Так, ну вроде отчет доделан................. 2023-12-26 20:30:37 +04:00
e1f83a204a Я профукал провод, комичу все с пк на ноут, чтобы подключиться 2023-12-26 19:42:41 +04:00
529af56d53 Фух, ну вроде добавил все перменные, что хотел. Осталось мб сделать "назначение админа", добавить в отчет "текущую дату" и в название может это + дополнить норм данными остаток, а ну еще и придумать с bikeid 2023-12-26 15:41:35 +04:00
a985333daf Добавил новое поле Радиус, осталость еще цвет, и вообще супер 2023-12-26 14:01:55 +04:00
ec12fd5cbf Заполнил красиво данные user и bike 2023-12-26 13:15:19 +04:00
7e9322c283 Проверка работы с ноута, запоминаем ip 2023-12-26 11:39:24 +04:00
94a3838c7c Короче, надо будет все coroutineScope посмотреть, перенсти их в ViewModel + вроде бы доделал дезигн... 2023-12-26 01:54:34 +04:00
34f56e6c89 Я бы не заснул, если бы не доделал дезигн, вот сейчас еще доделаю ошибочки а завтра на работу к 9.... 2023-12-26 01:27:26 +04:00
3b1dfec3bd Наизменял я дезигн конечно....
Эх, что то я так устал, вот бы много деняг просто...
2023-12-26 01:12:04 +04:00
c785a76e03 Перенес бизнес логику, хы 2023-12-26 00:01:44 +04:00
99f294da5d Норм путь к отчету 2023-12-25 23:53:50 +04:00
34f379b65d Пытался сделать краисво, ничего не получиолось, только норм отчет. Сейчас сделаю просто норм дезигн, перенсу бизнесс логику, а там посмотрим, уже 0:00.... 2023-12-25 23:39:52 +04:00
c1251004ba Супер кринж, но сейчас этот кринж будем делать красивым и чилить 2023-12-25 22:22:48 +04:00
61faa1af74 Украл все, что можно, каким то чудом работает, сейчас буду норм переделывать под PDF 2023-12-25 20:40:46 +04:00
41d688791d А теперь это чудо в виду pdf файла 2023-12-25 18:59:53 +04:00
748564a8a6 Меняем сервер, чтобы делать запросик, вот бы сегодня сделать отчет.......... 2023-12-25 18:51:04 +04:00
dc0b81dc74 Сделал ошибку для регистраций и авторизаций, победа 2023-12-25 01:12:18 +04:00
7ed6a2bf09 короче, я поменял библиотеку, и только так смог выбрать русский язык 2023-12-25 00:14:44 +04:00
359d8311fb Все круто классно, но русский язык не работает... 2023-12-24 22:48:44 +04:00
3fe3d41253 Добавил роль, добавил UI ограничения по ролям 2023-12-24 18:39:23 +04:00
54ee3a0a18 Добавил поле role, чичас буду менять UI под нее... 2023-12-24 18:16:37 +04:00
c3e82ff61b Зачем я это делаю, если надо делать отчеты и добавлять роли... 2023-12-24 17:54:42 +04:00
0456d68b16 Что то вынес, туда сюда кидаю, надо отчеты делать, а я фигней маюсь... 2023-12-24 17:18:59 +04:00
e4b66ae036 Кринжовый кринж 2 2023-12-24 00:11:55 +04:00
212ef57bff Какой то кринж, но рабочий кринж 2023-12-24 00:05:07 +04:00
ef9a391caf Изменял цвета, не смог изменить курсор :( 2023-12-23 22:01:46 +04:00
194dab430b Красиво вынесли. Осталась страничку пользователя и отчеты 2023-12-23 03:39:04 +04:00
361caca3c8 Теперь, обязательн надо авторизоваться, пабеда 2023-12-23 03:31:18 +04:00
68a276d549 Ура, адекватный пользователь сделан, осталось адекватную авторизацию или регистрацию сделать... 2023-12-23 03:03:40 +04:00
17a4568123 Опа, пофикисили отображение 2023-12-23 02:15:16 +04:00
f76e0ff450 Вроде робит, дальше допиливаем 2023-12-23 02:04:02 +04:00
37f2701ce7 так и живем 2023-12-20 13:10:55 +03:00
c9c689dbbc Отчеты 2023-12-20 12:32:37 +03:00
2fa37312d6 Отчеты 2023-12-20 12:32:09 +03:00
c358c953ba Так и живем 2023-11-29 22:50:13 +04:00
2884f2b4eb 1 2023-11-29 22:33:49 +04:00
4226f90712 Откатил к старой версий, без пагинаций 2023-11-25 10:42:53 +04:00
ee980fe775 Вроде сделал что то типо шуточной привязки 2023-11-24 19:51:29 +04:00
e63c1c1df0 Создал interface. Все перекинул 2023-11-24 18:05:02 +04:00
e96160ad7d Вынес factory 2023-11-24 17:38:41 +04:00
b46391b649 Хотел сделать норм пагинацию, не получилось, но файлы сохраню на всякимй 2023-11-24 02:11:44 +04:00
e903f1729c Сделал кринж года, есть пагинация, но мне вообще не нравится не проект, не реализация, не мои программистические способности :( 2023-11-23 22:45:17 +04:00
dc5cc2e862 Абалдеть, я не знаю, что сделал, на атворизация заработала 2023-11-23 22:02:38 +04:00
0cc1d56a3a Пытаюсь сделать авторизацию, пока что не робит :( 2023-11-23 16:52:30 +03:00
96d09edf51 Работает добавление 2023-11-23 15:31:34 +03:00
36c001ca2e Работает обновление 2023-11-23 15:25:37 +03:00
25cdd9b083 Вроде робит, хз насколько правильно только я это сделал 2023-11-23 15:18:36 +03:00
a11e81ab27 Делаем репозиторрий, виюмодели и прочее, сейлюсь перед тем, как их внедрять 2023-11-23 14:34:37 +03:00
e3bc68c14c Так, проверяем, норм ли создать 4 ветка и коммит в нее (+ репозиторий) 2023-11-23 13:26:26 +03:00
eb79619b6c Чуть чуть декораций 2023-11-22 23:53:54 +04:00
9565e46d37 Отображает занято или нет 2023-11-22 22:59:19 +04:00
bce45a28f0 Немного лишнее убрал 2023-11-22 22:54:06 +04:00
eb3bc2ea3d Сделал супер шутку "личного кабинета" 2023-11-22 22:49:55 +04:00
3e68677d23 Вроде бы сделал связь, ура победа 2023-11-22 22:00:09 +04:00
d9cb297f65 Абалдеть, я сделал редактирование.... 2023-11-21 01:04:47 +04:00
0b8c882823 Сделал шуточную авторизацию 2023-11-21 00:24:18 +04:00
4b3c61ca06 Вроде бы сделал регистрацию, но я не знаю, как ее проверить то... 2023-11-20 23:55:47 +04:00
1984f32b8a Крутим вертим, кнопку добавить починили 2023-11-20 23:34:26 +04:00
b622cb235b Чуть чуть сделал красивее, расскидал по папочкам :3 2023-11-20 22:30:30 +04:00
9c8d38f9f4 я не знаю каким чудом, но я сделал добавление 2023-11-20 20:41:00 +04:00
4df3eff3b1 ПВАЗХЩПХЗВАЩЗПХВАХЗПЩВ, ЭТО САМЫЙ ЖОСКИЙ КОСТЫЛЬ, НО ОНО РАБОТАЕТ 2023-11-19 23:09:23 +04:00
61b1507973 Абалдеть, я сделал удаление велосипеда, абалдеть.... 2023-11-19 22:46:47 +04:00
cf8aefb83d Я НЕ ЗНАЮ КАКАОЕ ЧУДО ПРОИЗОШЛО, НО ОНО РАБОТАЕТ!!! 2023-11-19 20:04:27 +04:00
62854ccff8 Меняю версию джавы, добавляю библиотеки, надеюсь, ничего не взорвется 2023-11-16 23:38:33 +04:00
a9f21b28d2 Новая ветка, сейчас буду пытаться что нить с бд, мб поменяю с "библиотеки", на "велосипеды" 2023-11-16 22:31:01 +04:00
150 changed files with 307280 additions and 1081 deletions

41
.gitignore vendored
View File

@ -1,26 +1,15 @@
# ---> Kotlin
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

3
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
My Application

View File

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

6
.idea/compiler.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>

20
.idea/gradle.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -0,0 +1,41 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
<option name="composableFile" value="true" />
<option name="previewFile" value="true" />
</inspection_tool>
</profile>
</component>

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.8.20" />
</component>
</project>

9
.idea/misc.xml Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

3
app/.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

14
app/.idea/gradle.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
</GradleProjectSettings>
</option>
</component>
</project>

9
app/.idea/misc.xml Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

6
app/.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
</component>
</project>

View File

@ -1,15 +1,17 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.plugin.serialization")
}
android {
namespace = "com.example.labwork"
compileSdk = 34
namespace = "com.example.myapplication"
compileSdk = 33
defaultConfig {
applicationId = "com.example.labwork"
minSdk = 24
applicationId = "com.example.myapplication"
minSdk = 26
targetSdk = 33
versionCode = 1
versionName = "1.0"
@ -30,17 +32,18 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.3"
kotlinCompilerExtensionVersion = "1.4.5"
}
packaging {
resources {
@ -50,18 +53,42 @@ android {
}
dependencies {
implementation("org.threeten:threetenbp:1.5.0")
implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("io.github.vanpra.compose-material-dialogs:datetime:0.8.1-rc")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
implementation("androidx.navigation:navigation-compose:2.7.4")
implementation("androidx.compose.material:material:1.5.4")
// Core
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.activity:activity-compose:1.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
// UI
implementation("androidx.activity:activity-compose:1.7.2")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.navigation:navigation-compose:2.6.0")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material3:material3:1.1.2")
implementation("androidx.compose.material:material:1.4.3")
// Room
val room_version = "2.5.2"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
// retrofit
val retrofitVersion = "2.9.0"
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
implementation("androidx.paging:paging-compose:3.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
// Tests
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
@ -69,4 +96,6 @@ dependencies {
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
//
}

View File

@ -1,13 +1,11 @@
package com.example.labwork
package com.example.myapplication
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
@ -19,6 +17,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.labwork", appContext.packageName)
assertEquals("com.example.myapplication", appContext.packageName)
}
}

View File

@ -2,7 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:name=".BikeApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@ -10,13 +17,14 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.LabWork"
tools:targetApi="31">
android:theme="@style/Theme.Pmudemo"
tools:targetApi="31"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:name=".MainComposeActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.LabWork">
android:theme="@style/Theme.Pmudemo">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -1,22 +0,0 @@
package com.example.labwork
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.labwork.button_navigation.MainScreen
import com.example.labwork.ui.theme.LabWorkTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LabWorkTheme {
MainScreen()
}
}
}
}

View File

@ -1,21 +0,0 @@
package com.example.labwork.button_navigation
import android.annotation.SuppressLint
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.navigation.compose.rememberNavController
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun MainScreen() {
val navController = rememberNavController()
Scaffold(
bottomBar = {
SlideNavigation(navController = navController)
}
) {
SlideGraph(navHostController = navController)
}
}

View File

@ -1,40 +0,0 @@
package com.example.labwork.button_navigation
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.labwork.models.getItemProducts
import com.example.labwork.pages.ListInfo
import com.example.labwork.pages.ListProduct
import com.example.labwork.pages.RegisteryOrLogin
@Composable
fun ScreenInfo() {
ListInfo()
}
@Composable
fun ScreenProfile() {
RegisteryOrLogin()
}
@Composable
fun ScreenListProduct() {
LazyColumn(
modifier = Modifier.fillMaxHeight().padding(bottom = 65.dp)
) {
itemsIndexed(
getItemProducts()
)
{
_, item ->
ListProduct(item = item)
}
}
}

View File

@ -1,26 +0,0 @@
package com.example.labwork.button_navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@Composable
fun SlideGraph(
navHostController: NavHostController
) {
NavHost(navController = navHostController, startDestination = "Profile"){
composable("Profile"){
ScreenProfile()
}
composable("Info"){
ScreenInfo()
}
composable("ListProduct"){
ScreenListProduct()
}
}
}

View File

@ -1,9 +0,0 @@
package com.example.labwork.button_navigation
import com.example.labwork.R
sealed class SlideItem(val title: String, val iconId: Int, val route: String){
object ScreenInfo: SlideItem("Информация", R.drawable.baseline_info,"Info" )
object ScreenProfile: SlideItem("Профиль", R.drawable.baseline_account_circle,"Profile" )
object ScreenListProduct: SlideItem("Товары", R.drawable.baseline_manage_search,"ListProduct" )
}

View File

@ -1,67 +0,0 @@
package com.example.labwork.button_navigation
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.Icon
import androidx.compose.material.SnackbarDefaults.backgroundColor
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.example.labwork.ui.theme.DarkBluePolitech
import com.example.labwork.ui.theme.LightBluePolitech
@Composable
fun SlideNavigation(
navController: NavController
) {
val listItems = listOf(
SlideItem.ScreenInfo,
SlideItem.ScreenProfile,
SlideItem.ScreenListProduct
)
BottomNavigation(
backgroundColor = Color.White,
modifier = Modifier.padding(10.dp),
elevation = 5.dp
) {
val backStackEntry by navController.currentBackStackEntryAsState()
val currentRout = backStackEntry?.destination?.route
listItems.forEach { item ->
BottomNavigationItem(
selected = currentRout == item.route,
onClick = {
navController.navigate(item.route)
},
icon = {
Icon(
painter = painterResource(id = item.iconId),
contentDescription = "Icon"
)
},
label = {
Text(
text = item.title,
fontSize = 9.sp
)
},
selectedContentColor = LightBluePolitech,
unselectedContentColor = DarkBluePolitech
)
}
}
}

View File

@ -1,25 +0,0 @@
package com.example.labwork.models
import com.example.labwork.R
import java.io.Serializable
data class ItemProduct(
val imageId: Int,
val name: String,
val about: String
) : Serializable
fun getItemProducts(): List<ItemProduct> {
return listOf(
ItemProduct(R.drawable.baseline_menu_book,"Золотая рыбка","Старик трудолюбивый (трижды забрасывал невод), добрый (отпустил просящую о пощаде единственную добычу), покорный (не перечит жене). Появление волшебной рыбки проявило алчную натуру женщины: требовательную, грубую, жаждущую от корыта до возможности быть владычицей морскою."),
ItemProduct(R.drawable.baseline_menu_book,"451° по Фаренгейту","Мастер мирового масштаба, совмещающий в литературе несовместимое. Создатель таких ярчайших шедевров, как \"Марсианские хроники\", \"451° по Фаренгейту"),
ItemProduct(R.drawable.baseline_menu_book,"1984","Своеобразный антипод второй великой антиутопии XX века - \"О дивный новый мир\" Олдоса Хаксли. Что, в сущности, страшнее: доведенное до абсурда \"общество потребления\""),
ItemProduct(R.drawable.baseline_menu_book,"Мастер и Маргарита","Один из самых загадочных и удивительных романов XX века. «Мастер и Маргарита» визитная карточка Михаила Булгакова. Более десяти лет он работал над"),
ItemProduct(R.drawable.baseline_menu_book,"Шантарам","Представляем читателю один из самых поразительных романов начала XXI века (в 2015 году получивший долгожданное продолжение «Тень горы»)."),
ItemProduct(R.drawable.baseline_menu_book,"Цветы для Элджернона","Сорок лет назад это считалось фантастикой. Сорок лет назад это читалось как фантастика."),
ItemProduct(R.drawable.baseline_menu_book,"Три товарища","Трое друзей - Робби, отчаянный автогонщик Кестер и \"последний романтик\" Ленц прошли Первую мировую войну."),
ItemProduct(R.drawable.baseline_menu_book,"Портрет Дориана Грея","«Портрет Дориана Грея» одно из величайших произведений последних полутора столетий, роман, который пытались запретить, а автора осуждали"),
ItemProduct(R.drawable.baseline_menu_book,"Маленький принц","«Маленький принц» — аллегорическая повесть, наиболее известное произведение Антуана де Сент-Экзюпери. ")
)
}

View File

@ -1,244 +0,0 @@
package com.example.labwork.pages
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.IconButton
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.labwork.R
import com.example.labwork.ui.theme.LightBluePolitech
@Composable
fun RegisteryOrLogin() {
val navController = rememberNavController()
NavHost(navController, startDestination = "login") {
composable("login") {
LoginPage(navController)
}
composable("register") {
RegisteryPage(navController)
}
}
}
@Composable
fun LoginPage(navController: NavController) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var showPassword by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.logo_ulstu),
contentDescription = "Logo",
modifier = Modifier.size(200.dp)
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Логин") },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textStyle = TextStyle(fontSize = 16.sp),
colors = TextFieldDefaults.textFieldColors(
cursorColor = LightBluePolitech,
backgroundColor = Color.White,
textColor = LightBluePolitech,
unfocusedLabelColor = LightBluePolitech,
focusedIndicatorColor = LightBluePolitech,
unfocusedIndicatorColor = LightBluePolitech,
focusedLabelColor = LightBluePolitech
),
singleLine = true
)
Spacer(modifier = Modifier.height(8.dp))
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Пароль") },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textStyle = TextStyle(fontSize = 16.sp),
colors = TextFieldDefaults.textFieldColors(
cursorColor = LightBluePolitech,
backgroundColor = Color.White,
textColor = LightBluePolitech,
unfocusedLabelColor = LightBluePolitech,
focusedIndicatorColor = LightBluePolitech,
unfocusedIndicatorColor = LightBluePolitech,
focusedLabelColor = LightBluePolitech
),
singleLine = true,
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(
onClick = { showPassword = !showPassword }
) {
Image(
painter = if(showPassword) painterResource(R.drawable.baseline_visibility) else painterResource(R.drawable.baseline_visibility_off),
contentDescription = "LogoVissable",
modifier = Modifier.size(24.dp)
)
}
}
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { /* Login logic */ },
colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech, ),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Text(text = "Авторизоваться", color = Color.White)
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { navController.navigate("register") },
colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Text(text = "Регистрация", color = Color.White)
}
}
}
@Composable
fun RegisteryPage(navController: NavController) {
var username by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") }
var confirmPassword by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(R.drawable.logo_ulstu),
contentDescription = "Logo",
modifier = Modifier.size(200.dp)
)
Spacer(modifier = Modifier.height(16.dp))
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Логин") },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textStyle = TextStyle(fontSize = 16.sp),
colors = TextFieldDefaults.textFieldColors(
cursorColor = LightBluePolitech,
backgroundColor = Color.White,
textColor = LightBluePolitech,
unfocusedLabelColor = LightBluePolitech,
focusedIndicatorColor = LightBluePolitech,
unfocusedIndicatorColor = LightBluePolitech,
focusedLabelColor = LightBluePolitech
),
singleLine = true
)
Spacer(modifier = Modifier.height(8.dp))
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Пароль") },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textStyle = TextStyle(fontSize = 16.sp),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White,
textColor = LightBluePolitech,
unfocusedLabelColor = LightBluePolitech,
focusedIndicatorColor = LightBluePolitech,
unfocusedIndicatorColor = LightBluePolitech,
focusedLabelColor = LightBluePolitech
),
singleLine = true,
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier.height(8.dp))
TextField(
value = confirmPassword,
onValueChange = { confirmPassword = it },
label = { Text("Подтвердите пароль") },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
textStyle = TextStyle(fontSize = 16.sp),
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White,
textColor = LightBluePolitech,
unfocusedLabelColor = LightBluePolitech,
focusedIndicatorColor = LightBluePolitech,
unfocusedIndicatorColor = LightBluePolitech,
focusedLabelColor = LightBluePolitech
),
singleLine = true,
visualTransformation = PasswordVisualTransformation()
)
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = { /* Register logic */ },
colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Text(text = "Зарегистрироваться", color = Color.White)
}
Spacer(modifier = Modifier.height(8.dp))
}
}

View File

@ -1,335 +0,0 @@
package com.example.labwork.pages
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Tab
import androidx.compose.material.TabRow
import androidx.compose.material.TabRowDefaults
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.labwork.R
import com.example.labwork.ui.theme.LightBluePolitech
@Composable
fun ListInfo() {
var selectedTab by remember { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize()) {
Column(
modifier = Modifier
.padding(16.dp)
.align(Alignment.TopCenter)
) {
Box(
modifier = Modifier
.size(200.dp)
.align(CenterHorizontally)
.clip(shape = RoundedCornerShape(8.dp))
.shadow(16.dp, shape = RoundedCornerShape(8.dp))
) {
Image(
painter = painterResource(id = R.drawable.logo_ulstu),
contentDescription = "Menu Image",
Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.height(16.dp))
TabRow(
selectedTabIndex = selectedTab,
backgroundColor = LightBluePolitech,
contentColor = Color.White,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
color = Color.White,
height = 4.dp,
modifier = Modifier
.tabIndicatorOffset(tabPositions[selectedTab])
)
},
modifier = Modifier.shadow(16.dp)
) {
Tab(
selected = selectedTab == 0,
onClick = { selectedTab = 0 }
) {
Text(
text = "Университет",
modifier = Modifier.padding(8.dp),
textAlign = TextAlign.Center,
)
}
Tab(
selected = selectedTab == 1,
onClick = { selectedTab = 1 }
) {
Text(
text = "Библиотека",
modifier = Modifier.padding(8.dp),
textAlign = TextAlign.Center,
)
}
Tab(
selected = selectedTab == 2,
onClick = { selectedTab = 2 }
) {
Text(
text = "Контакты разработчиков",
modifier = Modifier
.padding(8.dp),
textAlign = TextAlign.Center,
)
}
}
Spacer(modifier = Modifier.height(16.dp))
when (selectedTab) {
0 -> InfoUniver()
1 -> InfoBiblio()
2 -> InfoProgrammer()
}
}
}
}
@Composable
fun InfoUniver() {
LazyColumn(
modifier = Modifier.fillMaxSize().padding(bottom = 65.dp),
contentPadding = PaddingValues(16.dp)
) {
item {
Text(
text = "Основные сведения",
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Text(
"Полное наименование образовательной организации",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "Федеральное государственное бюджетное образовательное учреждение высшего образования \"Ульяновский государственный технический университет\""
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"Сокращенное (при наличии) наименование образовательной организации",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "УлГТУ, Ульяновский государственный технический университет")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Дата создания образовательной организации",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "18.09.1957")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Адрес местонахождения образовательной организации",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "432027, Ульяновская область, г. Ульяновск, улица Северный Венец, дом 32")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Филиалы образовательной организации",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "Имеются")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Представительства образовательной организации",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "Имеются")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Режим, график работы",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "Понедельник-четверг - 8.30 - 17.22")
Text(text = "Пятница- 8.30 - 16.22")
Text(text = "Перерыв 13.00 до 13.40")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Контактные телефоны",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "8(8422)43-02-37")
Spacer(modifier = Modifier.height(8.dp))
Text(
"Адреса электронной почты",
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
Text(text = "rector@ulstu.ru")
}
}
}
@Composable
fun InfoBiblio() {
LazyColumn(
modifier = Modifier.fillMaxSize().padding(bottom = 65.dp),
contentPadding = PaddingValues(16.dp)
) {
item {
Text(
text = "Основные сведения",
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(16.dp))
Text("Технический университет подобным сокровищем обладает!",modifier = Modifier.padding(bottom = 16.dp))
Text(
text = "Наша Научная библиотека соответствует всем современным требованиям технического вуза. Постоянно пополняющийся библиотечный фонд составляет более 1100 тыс. единиц хранения, формируется по специальностям (с учетом всех направлений учебного и научного процессов университета) изданиями, отвечающими требованиям программ высшей школы. В фонде имеются зарубежные издания, диссертации, авторефераты, документы на электронных носителях. Пользователям предоставляется доступ к электронным ресурсам удалённого доступа, входящим в состав электронно-библиотечной системы «ЭльбруС» УлГТУ."
,modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "К услугам читателей: зал каталогов, aвонементы научной и учебной литературы, общий читальный зал, отдел экономической и иностранной литературы. Выдача комплектов учебников первокурсникам, зал для аспирантов и преподавателей, зал периодических изданий, читальный зал машиностроительного факультета, читальный зал электронных ресурсов, отдел художественной литературы, научно-библиографический отдел, библиотека Института авиационных технологий и управления, Барышского колледжа — филиала УлГТУ."
,modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "Абонемент учебной литературы обеспечивает студентов и лицеистов учебниками и учебными пособиями в соответствии с учебными программами. Научные, научно-популярные и справочные издания для подготовки рефератов, докладов, курсовых и дипломных работ, а также издания по орган. Располагает богатым фондом справочных и информационных изданий. В отделе художественной литературы собраны книги на любой вкус: есть классическая русская и зарубежная, современная отечественная и иностранная литература; тематика книг - от фантастики, детективов, приключений и женских романов до серьезной, философской литературы. В отделе иностранной литературы хранятся учебные и научные издания на английском, немецком и французском языках. Читальный зал электронных ресурсов (медиатека) НБ УлГТУ располагает фондом CD, DVD по различным отраслям знаний. На территории Научной библиотеки размещены точки доступа в Интернет по технологии Wi-Fi. Право доступа в беспроводную локальной сети предоставляется всем желающим студентам и сотрудникам университета, имеющим учетную запись пользователя корпоративной сети университета."
,modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "События библиотечной жизни оперативно отражаются на нашем сайте (http://lib.ulstu.ru), сайт также обеспечивает круглосуточный доступ пользователей к каталогам и базам данных библиотеки."
,modifier = Modifier.padding(bottom = 16.dp)
)
Text(
text = "Научная библиотека ведёт активную работу в помощь духовно-нравственному, патриотическому и эстетическому воспитанию своих читателей, используя различные формы и методы библиотечного обслуживания, такие как: книжные выставки, тематические обзоры, беседы, творческие встречи с интересными людьми, конкурсы и викторины."
,modifier = Modifier.padding(bottom = 16.dp)
)
Spacer(modifier = Modifier.height(16.dp))
Text("Адреса электронной почты",
fontSize = 16.sp,
fontWeight = FontWeight.Bold)
Text(text = "rector@ulstu.ru")
}
}
}
@Composable
fun InfoProgrammer() {
LazyColumn(
modifier = Modifier.fillMaxSize().padding(bottom = 65.dp),
contentPadding = PaddingValues(16.dp)
) {
item {
Text(
text = "Основные сведения",
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Box(
modifier = Modifier
.size(200.dp)
.clip(shape = RoundedCornerShape(8.dp))
) {
Image(
painter = painterResource(id = R.drawable.kashinmaksim),
contentDescription = "Maxim",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
Spacer(modifier = Modifier.height(16.dp))
Text("Подробная информация",
fontSize = 16.sp,
fontWeight = FontWeight.Bold)
Text(
text = "Кашин Максим Игоревич"
)
Text("Личная информация",
fontSize = 16.sp,
fontWeight = FontWeight.Bold)
Text(
text = "Родной город: Сыктывкар"
)
Text(
text = "Языки: Русский"
)
Text(
text = "День рождения: 23 декабря"
)
Text("Место работы",
fontSize = 16.sp,
fontWeight = FontWeight.Bold)
Text(
text = "ДЦТ ЛАОП"
)
Text("Образование",
fontSize = 16.sp,
fontWeight = FontWeight.Bold)
Text(
text = "Вуз: УлГТУ"
)
Text(
text = "Факультет: Факультет информационных систем и технологий"
)
Text(
text = "Специальность: Программная инженерия"
)
Text(
text = "Школа: Школа № 7 Димитровград до 2021 г."
)
}
}
}

View File

@ -1,160 +0,0 @@
package com.example.labwork.pages
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
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.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Card
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.labwork.models.ItemProduct
import com.example.labwork.ui.theme.LightBluePolitech
@Composable
fun ListProduct(item: ItemProduct) {
var isFullAbout by remember { mutableStateOf(false) }
val scale by animateFloatAsState(if (isFullAbout) 1f else 0f)
val textSize by animateDpAsState(if (isFullAbout) 18.dp else 24.dp)
Card(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
shape = RoundedCornerShape(15.dp),
elevation = 5.dp
) {
Box {
Row(verticalAlignment = Alignment.CenterVertically) {
Column {
Image(
painter = painterResource(id = item.imageId),
contentDescription = "book",
contentScale = ContentScale.Crop,
modifier = Modifier
.padding(10.dp)
.size(128.dp)
.shadow(2.dp)
)
Button(
colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
modifier = Modifier
.padding(start = 9.dp, bottom = 9.dp)
.size(height = 32.dp, width = 128.dp),
onClick = {
isFullAbout = !isFullAbout
},
shape = RoundedCornerShape(15.dp)
) {
Text(
text = if(isFullAbout) "Убрать" else "Подробнее",
color = Color.White,
fontSize = 10.sp,
textAlign = TextAlign.Center,
)
}
Row {
Button(
colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
modifier = Modifier
.padding(start = 9.dp, bottom = 9.dp)
.size(height = 32.dp, width = 40.dp),
onClick = {
/*TODO*/
},
shape = RoundedCornerShape(15.dp)
) {
Text(
text = "+",
color = Color.White,
fontSize = 10.sp,
textAlign = TextAlign.Center,
)
}
Text(
text = "1000",
color = Color.Black,
fontSize = 14.sp,
textAlign = TextAlign.Center,
modifier = Modifier.padding(start = 8.dp, top = 4.dp)
)
Button(
colors = ButtonDefaults.buttonColors(backgroundColor = LightBluePolitech),
modifier = Modifier
.padding(start = 9.dp, bottom = 9.dp)
.size(height = 32.dp, width = 40.dp),
onClick = {
/*TODO*/
},
shape = RoundedCornerShape(15.dp)
) {
Text(
text = "-",
color = Color.White,
fontSize = 10.sp,
textAlign = TextAlign.Center,
)
}
}
}
Column {
Text(
text = item.name,
color = Color.Black,
fontSize = textSize.value.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = 8.dp)
)
AnimatedVisibility(
visible = isFullAbout,
enter = fadeIn() + expandIn(),
exit = fadeOut() + shrinkOut(),
modifier = Modifier.scale(scale)
) {
Text(
color = Color.Black,
text = item.about,
maxLines = 10,
fontSize = 14.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 8.dp)
)
}
}
}
}
}
}

View File

@ -1,14 +0,0 @@
package com.example.labwork.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
val LightBluePolitech = Color(0xFF0158AF)
val DarkBluePolitech = Color(0xFF123E6D)

View File

@ -0,0 +1,14 @@
package com.example.myapplication
import android.app.Application
import com.example.myapplication.database.room.repository.AppContainer
import com.example.myapplication.database.room.repository.AppDataContainer
class BikeApplication : Application() {
lateinit var container: AppContainer
override fun onCreate() {
super.onCreate()
container = AppDataContainer(this)
}
}

View File

@ -0,0 +1,41 @@
package com.example.myapplication
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import com.example.myapplication.ui.navigation.MainNavbar
import com.example.myapplication.setting.DataStoreManager
import com.example.myapplication.ui.theme.PmudemoTheme
class MainComposeActivity : ComponentActivity() {
private val dataStoreManager = DataStoreManager(this)
private val isDarkTheme = mutableStateOf(true)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
application.deleteDatabase("pmy-db")
setContent {
PmudemoTheme(darkTheme = isDarkTheme.value) {
LaunchedEffect(key1 = true) {
dataStoreManager.getSettings().collect { setting ->
isDarkTheme.value = setting.isDarkTheme
}
}
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainNavbar(
isDarkTheme = isDarkTheme,
dataStoreManager = dataStoreManager
)
}
}
}
}
}

View File

@ -0,0 +1,5 @@
package com.example.myapplication.api
enum class ApiStatus {
LOADING, ERROR, DONE
}

View File

@ -0,0 +1,176 @@
package com.example.myapplication.api
import com.example.myapplication.api.bike.remote.BikeRemote
import com.example.myapplication.api.rent.remote.RentRemote
import com.example.myapplication.api.item.remote.ItemFromBikeRemote
import com.example.myapplication.api.item.remote.ItemRemote
import com.example.myapplication.api.item.remote.ItemWithBikeRemote
import com.example.myapplication.api.report.remote.ReportRemote
import com.example.myapplication.api.user.remote.UserRemote
import com.example.myapplication.setting.LocalDateTimeSerializer
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
interface MyServerService {
@GET("report")
suspend fun getReportInfo(
@Query("fromDate") fromDate: String,
@Query("toDate") toDate: String
): List<ReportRemote>
@GET("rents")
suspend fun getRents(): List<RentRemote>
@GET("users/{id}")
suspend fun getUser(
@Path("id") id: Int,
): UserRemote
@GET("users")
suspend fun getUsers(): List<UserRemote>
@POST("users")
suspend fun createUser(
@Body user: UserRemote,
): UserRemote
@PUT("users/{id}")
suspend fun updateUser(
@Path("id") id: Int,
@Body user: UserRemote,
): UserRemote
@DELETE("users/{id}")
suspend fun deleteUser(
@Path("id") id: Int,
): UserRemote
@GET("bikes")
suspend fun getBikes(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<BikeRemote>
@GET("bikes/{id}")
suspend fun getBike(
@Path("id") id: Int,
): BikeRemote
@POST("bikes")
suspend fun createBike(
@Body bike: BikeRemote,
): BikeRemote
@PUT("bikes/{id}")
suspend fun updateBike(
@Path("id") id: Int,
@Body bike: BikeRemote,
): BikeRemote
@DELETE("bikes/{id}")
suspend fun deleteBike(
@Path("id") id: Int,
)
@GET("bikes/{bikeId}/items")
suspend fun getItemsForBike(
@Path("bikeId") bikeId: Int
): List<ItemFromBikeRemote>
@GET("items/{id}?_expand=bike")
suspend fun getItem(
@Path("id") id: Int,
): ItemWithBikeRemote
@POST("items")
suspend fun createItem(
@Body item: ItemRemote,
): ItemRemote
@PUT("items/{id}")
suspend fun updateItem(
@Path("id") id: Int,
@Body item: ItemRemote,
): ItemRemote
@DELETE("items/{id}")
suspend fun deleteItem(
@Path("id") id: Int,
): ItemFromBikeRemote
@GET("users/{id}")
suspend fun getUserCart(
@Path("id") id: Int,
): UserRemote
@PUT("users/{id}")
suspend fun updateUserCart(
@Path("id") id: Int,
@Body userRemote: UserRemote,
): UserRemote
@GET("rents")
suspend fun getRents(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<RentRemote>
@GET("rents/{id}")
suspend fun getRent(
@Path("id") id: Int,
): RentRemote
@POST("rents")
suspend fun createRent(
@Body bike: RentRemote,
): RentRemote
@DELETE("rents/{id}")
suspend fun deleteRent(
@Path("id") id: Int
)
@PUT("rents/{id}")
suspend fun updateRent(
@Path("id") id: Int,
@Body rentRemote: RentRemote,
): RentRemote
companion object {
//Раздача с тлф
private const val BASE_URL = "http://192.168.138.47:8079/"
//Пк дома
/*private const val BASE_URL = "http://192.168.0.104:8079/"*/
//private const val BASE_URL = "http://10.0.2.2:8079/"
@Volatile
private var INSTANCE: MyServerService? = null
fun getInstance(): MyServerService {
return INSTANCE ?: synchronized(this) {
val logger = HttpLoggingInterceptor()
logger.level = HttpLoggingInterceptor.Level.BASIC
val client = OkHttpClient.Builder().addInterceptor(logger).build()
val json = Json {
ignoreUnknownKeys = true
serializersModule = SerializersModule {
contextual(LocalDateTimeSerializer)
}
}
return Retrofit.Builder().baseUrl(BASE_URL).client(client)
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build().create(MyServerService::class.java).also { INSTANCE = it }
}
}
}
}

View File

@ -0,0 +1,122 @@
package com.example.myapplication.api.bike.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.bike.remote.toBike
import com.example.myapplication.api.item.remote.toItem
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
import com.example.myapplication.database.remotekeys.model.RemoteKeys
import com.example.myapplication.database.remotekeys.repository.offline.OfflineRemoteKeyRepository
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.repository.offline.OfflineBikeRepository
import com.example.myapplication.database.room.repository.offline.OfflineItemRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class BikeRemoteMediator(
private val service: MyServerService,
private val dbBikeRepository: OfflineBikeRepository,
private val dbItemRepository: OfflineItemRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase
) : RemoteMediator<Int, Bike>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Bike>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val bikes = service.getBikes(page, state.config.pageSize).map { it.toBike() }
val bikesWithItems = bikes.map { bike ->
service.getItemsForBike(bike.uid).map {
service.getItem(it.id).toItem()
}
}
val endOfPaginationReached = bikes.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.CINEMA)
dbItemRepository.clearItems()
dbBikeRepository.clearBikes()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = bikes.map {
RemoteKeys(
entityId = it.uid,
type = RemoteKeyType.CINEMA,
prevKey = prevKey,
nextKey = nextKey
)
}
dbRemoteKeyRepository.createRemoteKeys(keys)
dbBikeRepository.insertBikes(bikes)
bikesWithItems.forEach {
try {
dbItemRepository.insertItems(it)
} catch (_: Exception) {
}
}
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Bike>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { bike ->
dbRemoteKeyRepository.getAllRemoteKeys(bike.uid, RemoteKeyType.CINEMA)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Bike>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { bike ->
dbRemoteKeyRepository.getAllRemoteKeys(bike.uid, RemoteKeyType.CINEMA)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Bike>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.uid?.let { bikeUid ->
dbRemoteKeyRepository.getAllRemoteKeys(bikeUid, RemoteKeyType.CINEMA)
}
}
}
}

View File

@ -0,0 +1,26 @@
package com.example.myapplication.api.bike.remote
import com.example.myapplication.database.room.model.bike.Bike
import kotlinx.serialization.Serializable
@Serializable
data class BikeRemote(
val id: Int = 0,
val name: String = "",
val description: String = "",
val image: ByteArray? = null
)
fun BikeRemote.toBike(): Bike = Bike(
id,
name,
description,
image
)
fun Bike.toBikeRemote(): BikeRemote = BikeRemote(
uid,
name,
description,
image
)

View File

@ -0,0 +1,93 @@
package com.example.myapplication.api.bike.repository
import android.util.Log
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.bike.mediator.BikeRemoteMediator
import com.example.myapplication.api.bike.remote.toBike
import com.example.myapplication.api.bike.remote.toBikeRemote
import com.example.myapplication.database.room.repository.AppContainer
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.remotekeys.repository.offline.OfflineRemoteKeyRepository
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.bike.BikeWithItems
import com.example.myapplication.database.room.model.item.ItemFromBike
import com.example.myapplication.database.room.repository.offline.OfflineBikeRepository
import com.example.myapplication.database.room.repository.offline.OfflineItemRepository
import com.example.myapplication.database.room.repository.online.BikeRepository
import kotlinx.coroutines.flow.Flow
class RestBikeRepository(
private val service: MyServerService,
private val dbBikeRepository: OfflineBikeRepository,
private val dbItemRepository: OfflineItemRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase
) : BikeRepository {
override fun getAllBikes(): Flow<PagingData<Bike>> {
Log.d(RestBikeRepository::class.simpleName, "Get bikes")
val pagingSourceFactory = { dbBikeRepository.getAllBikesPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = BikeRemoteMediator(
service,
dbBikeRepository,
dbItemRepository,
dbRemoteKeyRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
override suspend fun getBike(uid: Int): BikeWithItems {
val bike = service.getBike(uid).toBike()
val items = service.getItemsForBike(uid).map { x ->
ItemFromBike(
x.id,
x.dateTime,
x.weight,
x.radius,
x.colorbike,
x.maxCount - service.getRents().flatMap { rent ->
rent.items.filter { item -> item.id == x.id }
}.sumOf { item -> item.count },
uid
)
}
return BikeWithItems(bike, items)
}
override suspend fun insertBike(bike: Bike) {
service.createBike(bike.toBikeRemote()).toBike()
}
override suspend fun updateBike(bike: Bike) {
service.updateBike(bike.uid, bike.toBikeRemote()).toBike()
}
override suspend fun deleteBike(bike: Bike) {
val cart = service.getUsers()
cart.forEach { userRemote ->
userRemote.items = userRemote.items.filter { x -> x.bikeId != bike.uid }
userRemote.id?.let { service.updateUserCart(it, userRemote) }
}
val rents = service.getRents()
rents.forEach { rentRemote ->
rentRemote.items = rentRemote.items.filter { x -> x.bikeId != bike.uid }
service.updateRent(rentRemote.id, rentRemote)
}
service.deleteBike(bike.uid)
dbBikeRepository.deleteBike(bike)
}
}

View File

@ -0,0 +1,57 @@
package com.example.myapplication.api.item.remote
/*
@Serializable
data class BikeWithItemsRemote(
val id: Int = 0,
val name: String = "",
val description: String = "",
val image: ByteArray? = null
@SerialName("items")
val items: List<ItemFromBikeRemote>,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as BikeWithItemsRemote
if (id != other.id) return false
if (name != other.name) return false
if (description != other.description) return false
if (image != null) {
if (other.image == null) return false
if (!image.contentEquals(other.image)) return false
} else if (other.image != null) return false
if (items != other.items) return false
return true
}
override fun hashCode(): Int {
var result = id
result = 31 * result + name.hashCode()
result = 31 * result + description.hashCode()
result = 31 * result + (image?.contentHashCode() ?: 0)
result = 31 * result + items.hashCode()
return result
}
}
fun BikeWithItemsRemote.toBikeWithItems(): BikeWithItems = BikeWithItems(
Bike(
id,
name,
description,
image
),
items.map { x -> x.toItemFromBike() }
)
fun Bike.toBikeWithItemsRemote(): BikeWithItemsRemote = BikeWithItemsRemote(
uid,
name,
description,
image,
items = emptyList()
)*/

View File

@ -0,0 +1,39 @@
package com.example.myapplication.api.item.remote
import com.example.myapplication.database.room.model.item.ItemFromBike
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.threeten.bp.LocalDateTime
@Serializable
class ItemFromBikeRemote(
val id: Int = 0,
@Contextual
val dateTime: LocalDateTime = LocalDateTime.MIN,
val weight: Double = 0.0,
val radius: Double = 0.0,
val colorbike: String = "",
val maxCount: Int = 0,
val availableCount: Int = 0,
val bikeId: Int = 0,
)
fun ItemFromBikeRemote.toItemFromBike(): ItemFromBike = ItemFromBike(
id,
dateTime,
weight,
radius,
colorbike,
availableCount,
bikeId
)
fun ItemFromBike.toItemFromBikeRemote(): ItemFromBikeRemote = ItemFromBikeRemote(
uid,
dateTime,
weight,
radius,
colorbike,
availableCount,
bikeId
)

View File

@ -0,0 +1,19 @@
package com.example.myapplication.api.item.remote
import com.example.myapplication.api.bike.remote.BikeRemote
import com.example.myapplication.api.bike.remote.toBike
import com.example.myapplication.database.room.model.item.ItemFromCart
import kotlinx.serialization.Serializable
import org.threeten.bp.LocalDateTime
@Serializable
class ItemFromCartRemote(
val id: Int = 0,
var count: Int = 0,
var bikeId: Int = 0,
)
fun ItemFromCartRemote.toItemFromCart(bike: BikeRemote, dateTime: LocalDateTime, weight: Double, radius: Double, colorbike : String, availableCount: Int): ItemFromCart =
ItemFromCart(
id, dateTime, weight, radius, colorbike, availableCount, count, bike.id, bike.toBike()
)

View File

@ -0,0 +1,24 @@
package com.example.myapplication.api.item.remote
import com.example.myapplication.api.bike.remote.BikeRemote
import com.example.myapplication.api.bike.remote.toBike
import com.example.myapplication.database.room.model.item.ItemFromRent
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.threeten.bp.LocalDateTime
@Serializable
class ItemFromRentRemote(
val id: Int = 0,
@Contextual val dateTime: LocalDateTime = LocalDateTime.MIN,
val frozenWeight: Double = 0.0,
val radius: Double = 0.0,
val colorbike: String = "",
val count: Int = 0,
val bikeId: Int = 0,
)
fun ItemFromRentRemote.toItemFromRent(bike: BikeRemote): ItemFromRent =
ItemFromRent(
id, dateTime, frozenWeight, radius,colorbike, count, bikeId, bike.toBike()
)

View File

@ -0,0 +1,38 @@
package com.example.myapplication.api.item.remote
import com.example.myapplication.database.room.model.item.Item
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.threeten.bp.LocalDateTime
@Serializable
data class ItemRemote(
val id: Int = 0,
@Contextual
val dateTime: LocalDateTime,
val weight: Double,
val radius: Double,
val colorbike: String,
val maxCount: Int,
val bikeId: Int = 0
)
fun ItemRemote.toItem(): Item = Item(
id,
dateTime,
weight,
radius,
colorbike,
maxCount,
bikeId
)
fun Item.toItemRemote(): ItemRemote = ItemRemote(
uid,
dateTime,
weight,
radius,
colorbike,
maxCount,
bikeId
)

View File

@ -0,0 +1,30 @@
package com.example.myapplication.api.item.remote
import com.example.myapplication.api.bike.remote.BikeRemote
import com.example.myapplication.database.room.model.item.Item
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import org.threeten.bp.LocalDateTime
@Serializable
data class ItemWithBikeRemote(
val id: Int = 0,
@Contextual
val dateTime: LocalDateTime,
val weight: Double,
val radius: Double,
val colorbike: String,
val maxCount: Int,
val bikeId: Int = 0,
val bike: BikeRemote,
)
fun ItemWithBikeRemote.toItem(): Item = Item(
id,
dateTime,
weight,
radius,
colorbike,
maxCount,
bikeId
)

View File

@ -0,0 +1,163 @@
package com.example.myapplication.api.item.repository
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.pdf.PdfDocument
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.item.remote.toItem
import com.example.myapplication.api.item.remote.toItemRemote
import com.example.myapplication.api.report.remote.ReportRemote
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.repository.offline.OfflineItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineRentItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineUserItemRepository
import com.example.myapplication.database.room.repository.online.ItemRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
import java.io.OutputStream
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class RestItemRepository(
private val service: MyServerService,
private val dbItemRepository: OfflineItemRepository,
private val dbUserItemRepository: OfflineUserItemRepository,
private val dbRentItemRepository: OfflineRentItemRepository,
) : ItemRepository {
override suspend fun getItem(uid: Int): Item {
return service.getItem(uid).toItem()
}
override suspend fun insertItem(item: Item) {
dbItemRepository.insertItem(
service.createItem(item.toItemRemote()).toItem()
)
}
override suspend fun updateItem(item: Item) {
dbItemRepository.updateItem(
service.updateItem(
item.uid,
item.toItemRemote()
).toItem()
)
}
override suspend fun deleteItem(item: Item) {
val cart = service.getUsers()
cart.forEach { userRemote ->
userRemote.items = userRemote.items.filter { x -> x.id != item.uid }
userRemote.id?.let { service.updateUserCart(it, userRemote) }
}
val rents = service.getRents()
rents.forEach { rentRemote ->
rentRemote.items = rentRemote.items.filter { x -> x.id != item.uid }
service.updateRent(rentRemote.id, rentRemote)
}
service.deleteItem(item.uid)
dbUserItemRepository.deleteItemsByUid(item.uid)
dbRentItemRepository.deleteItemsByUid(item.uid)
dbItemRepository.deleteItem(item)
}
suspend fun getReport(fromDate: String, toDate: String):List<ReportRemote>
{
return service.getReportInfo(fromDate,toDate)
}
suspend fun createPdfFile(context: Context, fileName: String, reportData: List<ReportRemote>) {
withContext(Dispatchers.IO) {
val currentDate = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(Date())
val contentValues = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "$fileName от $currentDate")
put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS)
}
val contentResolver: ContentResolver = context.contentResolver
val uri: Uri? = contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues)
uri?.let {
try {
contentResolver.openOutputStream(uri)?.use { outputStream ->
createPdfContent(outputStream, reportData)
}
// Optional: Notify MediaStore about the new file
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
private fun createPdfContent(outputStream: OutputStream, reportData: List<ReportRemote>) {
val currentDate = SimpleDateFormat("dd.MM.yyyy", Locale.getDefault()).format(Date())
val pdfDocument = PdfDocument()
val pageInfo = PdfDocument.PageInfo.Builder(595, 842, 1).create()
val page = pdfDocument.startPage(pageInfo)
val canvas = page.canvas
val paint = Paint()
paint.textSize = 16f
paint.isAntiAlias = true
// Отображаем заголовок
val title = "Отчет от ${currentDate}"
val xTitle = (pageInfo.pageWidth - paint.measureText(title)) / 2
val yTitle = 40f
canvas.drawText(title, xTitle, yTitle, paint)
// Отображаем данные в виде таблицы
val tableStartY = yTitle + 60f
val columnWidths = floatArrayOf(0.5f, 1f, 0.6f, 0.5f, 1f, 1f, 1f)
val rowHeight = 30f
drawTableRow(canvas, arrayOf("ID", "Дата", "Вес", "R", "Цвет", "Кол-во", "Имя"), columnWidths, tableStartY, paint)
for ((index, report) in reportData.withIndex()) {
val row = arrayOf(
report.id.toString(),
report.dateTime.toString(),
report.weight.toString(),
report.radius.toString(),
report.colorbike.toString(),
report.maxCount.toString(),
report.bikeName.toString()
)
drawTableRow(canvas, row, columnWidths, tableStartY + (index + 1) * rowHeight, paint)
}
pdfDocument.finishPage(page)
pdfDocument.writeTo(outputStream)
pdfDocument.close()
}
private fun drawTableRow(
canvas: Canvas,
row: Array<String>,
columnWidths: FloatArray,
y: Float,
paint: Paint
) {
var x = 40f
for (i in row.indices) {
val text = row[i]
canvas.drawText(text, x, y, paint)
x += columnWidths[i] * 80
}
}
}

View File

@ -0,0 +1,107 @@
package com.example.myapplication.api.rent.mediator
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.rent.remote.toRent
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
import com.example.myapplication.database.remotekeys.model.RemoteKeys
import com.example.myapplication.database.remotekeys.repository.offline.OfflineRemoteKeyRepository
import com.example.myapplication.database.room.model.rent.Rent
import com.example.myapplication.database.room.repository.offline.OfflineRentRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class RentRemoteMediator(
private val service: MyServerService,
private val dbRentRepository: OfflineRentRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase
) : RemoteMediator<Int, Rent>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Rent>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val rents = service.getRents(page, state.config.pageSize).map { it.toRent() }
val endOfPaginationReached = rents.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.ORDER)
dbRentRepository.clearRents()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = rents.map {
RemoteKeys(
entityId = it.uid,
type = RemoteKeyType.ORDER,
prevKey = prevKey,
nextKey = nextKey
)
}
dbRemoteKeyRepository.createRemoteKeys(keys)
dbRentRepository.insertRents(rents)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Rent>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { rent ->
dbRemoteKeyRepository.getAllRemoteKeys(rent.uid, RemoteKeyType.ORDER)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Rent>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { rent ->
dbRemoteKeyRepository.getAllRemoteKeys(rent.uid, RemoteKeyType.ORDER)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Rent>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.uid?.let { rentUid ->
dbRemoteKeyRepository.getAllRemoteKeys(rentUid, RemoteKeyType.ORDER)
}
}
}
}

View File

@ -0,0 +1,18 @@
package com.example.myapplication.api.rent.remote
import com.example.myapplication.api.item.remote.ItemFromRentRemote
import com.example.myapplication.database.room.model.rent.Rent
import kotlinx.serialization.Serializable
@Serializable
data class RentRemote(
val id: Int = 0, val userId: Int = 0, var items: List<ItemFromRentRemote> = emptyList()
)
fun RentRemote.toRent(): Rent = Rent(
id, userId
)
fun Rent.toRentRemote(): RentRemote = RentRemote(
uid, userId!!, items = emptyList()
)

View File

@ -0,0 +1,42 @@
package com.example.myapplication.api.rent.rentitem.repository
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.item.remote.ItemFromRentRemote
import com.example.myapplication.api.item.remote.toItem
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
import com.example.myapplication.database.room.repository.offline.OfflineRentItemRepository
import com.example.myapplication.database.room.repository.online.RentItemRepository
class RestRentItemRepository(
private val service: MyServerService,
private val dbRentItemRepository: OfflineRentItemRepository
) : RentItemRepository {
override suspend fun insertRentItem(rentItemCrossRef: RentItemCrossRef) {
var rentRemote = service.getRent(rentItemCrossRef.rentId)
val item = service.getItem(rentItemCrossRef.itemId).toItem()
val itemFromRent = ItemFromRentRemote(
item.uid,
item.dateTime,
item.weight,
item.radius,
item.colorbike,
rentItemCrossRef.count,
item.bikeId
)
val updatedItems = rentRemote.items.toMutableList()
updatedItems.add(itemFromRent)
rentRemote = rentRemote.copy(items = updatedItems)
service.updateRent(rentItemCrossRef.rentId, rentRemote)
dbRentItemRepository.insertRentItem(rentItemCrossRef)
}
override suspend fun updateRentItem(rentItemCrossRef: RentItemCrossRef) {
}
override suspend fun deleteRentItem(rentItemCrossRef: RentItemCrossRef) {
}
}

View File

@ -0,0 +1,82 @@
package com.example.myapplication.api.rent.repository
import android.util.Log
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.bike.remote.toBikeRemote
import com.example.myapplication.api.item.remote.toItemFromRent
import com.example.myapplication.api.rent.mediator.RentRemoteMediator
import com.example.myapplication.api.rent.remote.toRent
import com.example.myapplication.api.rent.remote.toRentRemote
import com.example.myapplication.database.room.repository.AppContainer
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.remotekeys.repository.offline.OfflineRemoteKeyRepository
import com.example.myapplication.database.room.model.item.ItemFromRent
import com.example.myapplication.database.room.model.rent.Rent
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
import com.example.myapplication.database.room.repository.offline.OfflineBikeRepository
import com.example.myapplication.database.room.repository.offline.OfflineRentItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineRentRepository
import com.example.myapplication.database.room.repository.online.RentRepository
import kotlinx.coroutines.flow.Flow
class RestRentRepository(
private val service: MyServerService,
private val dbRentRepository: OfflineRentRepository,
private val dbBikeRepository: OfflineBikeRepository,
private val dbRentItemRepository: OfflineRentItemRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val database: AppDatabase
) : RentRepository {
override fun getAllRents(userId: Int?): Flow<PagingData<Rent>> {
Log.d(RestRentRepository::class.simpleName, "Get rents")
val pagingSourceFactory = { dbRentRepository.getAllRentsPagingSource(userId) }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = RentRemoteMediator(
service,
dbRentRepository,
dbRemoteKeyRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
override suspend fun getRent(uid: Int): List<ItemFromRent> {
val rent = service.getRent(uid)
dbRentItemRepository.deleteRentItems(uid)
rent.items.map {
dbRentItemRepository.insertRentItem(
RentItemCrossRef(
uid,
it.id,
it.frozenWeight,
it.radius,
it.colorbike,
it.count
)
)
}
return rent.items.map { x -> x.toItemFromRent(dbBikeRepository.getBike(x.bikeId).bike.toBikeRemote()) }
}
override suspend fun insertRent(rent: Rent): Long {
return dbRentRepository.insertRent(service.createRent(rent.toRentRemote()).toRent())
}
override suspend fun deleteRent(rent: Rent) {
service.deleteRent(rent.uid)
dbRentRepository.deleteRent(rent)
}
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.api.report.remote
import kotlinx.serialization.Serializable
@Serializable
data class ReportRemote(
val id: Int = 0,
val dateTime: Int = 0,
val weight: Double = 0.0,
val radius: Double = 0.0,
val colorbike: String = "",
val maxCount: Int = 0,
val bikeName: String = ""
)

View File

@ -0,0 +1,28 @@
package com.example.myapplication.api.user.remote
import com.example.myapplication.api.item.remote.ItemFromCartRemote
import com.example.myapplication.database.room.model.user.User
import kotlinx.serialization.Serializable
@Serializable
data class UserRemote(
val id: Int? = 0,
val login: String = "",
val role: String = "",
val password: String = "",
var items: List<ItemFromCartRemote> = emptyList()
)
fun UserRemote.toUser(): User = User(
id,
login,
role,
password
)
fun User.toUserRemote(): UserRemote = UserRemote(
uid,
login,
role,
password
)

View File

@ -0,0 +1,78 @@
package com.example.myapplication.api.user.repository
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.item.remote.toItemFromCart
import com.example.myapplication.api.user.remote.toUser
import com.example.myapplication.api.user.remote.toUserRemote
import com.example.myapplication.database.room.model.item.ItemFromCart
import com.example.myapplication.database.room.model.user.User
import com.example.myapplication.database.room.model.user.UserItemCrossRef
import com.example.myapplication.database.room.repository.offline.OfflineUserItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineUserRepository
import com.example.myapplication.database.room.repository.online.UserRepository
class RestUserRepository(
private val service: MyServerService,
private val dbUserRepository: OfflineUserRepository,
private val dbUserItemRepository: OfflineUserItemRepository,
) : UserRepository {
override suspend fun getAllUsers(): List<User> {
val existUsers = dbUserRepository.getAllUsers().associateBy { it.uid }.toMutableMap()
service.getUsers()
.map { it.toUser() }
.forEach { user ->
val existUser = existUsers[user.uid]
if (existUser == null) {
dbUserRepository.insertUser(user)
} else if (existUser != user) {
dbUserRepository.updateUser(user)
}
existUsers[user.uid] = user
}
return existUsers.map { it.value }.sortedBy { it.uid }
}
override suspend fun getCartByUser(userId: Int): List<ItemFromCart> {
val cart = service.getUserCart(userId)
dbUserItemRepository.deleteUserItems(userId)
cart.items.map { itemFromCartRemote ->
dbUserItemRepository.insertUserItem(
UserItemCrossRef(
userId,
itemFromCartRemote.id,
itemFromCartRemote.count
)
)
}
return cart.items.map {
val item = service.getItem(it.id)
it.toItemFromCart(
item.bike,
item.dateTime,
item.weight,
item.radius,
item.colorbike,
item.maxCount - service.getRents().flatMap { rent ->
rent.items.filter { item -> item.id == it.id }
}.sumOf { item -> item.count })
}
}
override suspend fun insertUser(user: User) {
service.createUser(user.toUserRemote()).toUser()
}
override suspend fun updateUser(user: User) {
user.uid?.let { service.updateUser(it, user.toUserRemote()).toUser() }
}
override suspend fun deleteUser(user: User) {
user.uid?.let { service.deleteUser(it).toUser() }
}
override suspend fun getUserById(idUser: Int?): User? =
idUser?.let { service.getUser(it).toUser() }
}

View File

@ -0,0 +1,62 @@
package com.example.myapplication.api.useritem.repository
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.item.remote.ItemFromCartRemote
import com.example.myapplication.api.item.remote.toItem
import com.example.myapplication.database.room.model.user.UserItemCrossRef
import com.example.myapplication.database.room.repository.offline.OfflineUserItemRepository
import com.example.myapplication.database.room.repository.online.UserItemRepository
class RestUserItemRepository(
private val service: MyServerService,
private val dbUserItemRepository: OfflineUserItemRepository
) : UserItemRepository {
override suspend fun insertUserItem(userItemCrossRef: UserItemCrossRef) {
var cartItems = service.getUserCart(userItemCrossRef.userId)
cartItems.items.forEach { item ->
if (item.id == userItemCrossRef.itemId)
return
}
val item = service.getItem(userItemCrossRef.itemId).toItem()
val itemFromCart = ItemFromCartRemote(
item.uid,
userItemCrossRef.count,
item.bikeId,
)
val updatedItems = cartItems.items.toMutableList()
updatedItems.add(itemFromCart)
cartItems = cartItems.copy(items = updatedItems)
service.updateUserCart(userItemCrossRef.userId, cartItems)
dbUserItemRepository.insertUserItem(userItemCrossRef)
}
override suspend fun updateUserItem(userItemCrossRef: UserItemCrossRef) {
val userRemote = service.getUserCart(userItemCrossRef.userId)
if (userItemCrossRef.count <= 0) {
userRemote.items =
userRemote.items.filter { x -> x.id != userItemCrossRef.itemId }
} else
userRemote.items.forEach {
if (it.id == userItemCrossRef.itemId) {
it.count = userItemCrossRef.count
}
}
service.updateUserCart(userItemCrossRef.userId, userRemote)
dbUserItemRepository.updateUserItem(userItemCrossRef)
}
override suspend fun deleteUserItem(userItemCrossRef: UserItemCrossRef) {
updateUserItem(userItemCrossRef)
dbUserItemRepository.deleteUserItem(userItemCrossRef)
}
override suspend fun deleteUserItems(userId: Int) {
val userRemote = service.getUserCart(userId)
userRemote.items = emptyList()
service.updateUserCart(userId, userRemote)
dbUserItemRepository.deleteUserItems(userId)
}
}

View File

@ -0,0 +1,122 @@
package com.example.myapplication.database
import android.content.Context
import android.graphics.Bitmap
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.database.remotekeys.dao.RemoteKeysDao
import com.example.myapplication.database.remotekeys.model.RemoteKeys
import com.example.myapplication.database.room.dao.bike.BikeDao
import com.example.myapplication.database.room.dao.item.ItemDao
import com.example.myapplication.database.room.dao.rent.RentDao
import com.example.myapplication.database.room.dao.rent.RentItemCrossRefDao
import com.example.myapplication.database.room.dao.user.UserDao
import com.example.myapplication.database.room.dao.user.UserItemCrossRefDao
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.model.rent.Rent
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
import com.example.myapplication.database.room.model.user.User
import com.example.myapplication.database.room.model.user.UserItemCrossRef
import com.example.myapplication.setting.LocalDateTimeConverter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.ByteArrayOutputStream
@Database(
entities = [
Bike::class,
Item::class,
Rent::class,
RentItemCrossRef::class,
User::class,
UserItemCrossRef::class,
RemoteKeys::class
],
version = 1,
exportSchema = false
)
@TypeConverters(LocalDateTimeConverter::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun bikeDao(): BikeDao
abstract fun itemDao(): ItemDao
abstract fun rentDao(): RentDao
abstract fun rentItemCrossRefDao(): RentItemCrossRefDao
abstract fun userDao(): UserDao
abstract fun userItemCrossRefDao(): UserItemCrossRefDao
abstract fun remoteKeysDao(): RemoteKeysDao
companion object {
private const val DB_NAME: String = "pmy-db"
@Volatile
private var INSTANCE: AppDatabase? = null
private suspend fun populateDatabase() {
INSTANCE?.let { database ->
// Users
val userDao = database.userDao()
val user1 = User(1, "login", "admin", "password")
userDao.insert(user1)
}
}
fun getInstance(appContext: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
DB_NAME
)
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch {
populateDatabase()
}
}
})
.build()
.also { INSTANCE = it }
}
}
private fun createColoredImage(color: Int): ByteArray {
val width = 100
val height = 100
val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
bmp.eraseColor(color)
val stream = ByteArrayOutputStream()
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream)
return stream.toByteArray()
}
private fun getRandomColorInt(): Int {
val red = (0..255).random()
val green = (0..255).random()
val blue = (0..255).random()
return (0xFF shl 24) or (red shl 16) or (green shl 8) or blue
}
private fun generateBikeName(index: Int): String {
val base = 'a'.code
val alphabetSize = 26
val sb = StringBuilder()
var remainder = index
do {
val letter = (remainder % alphabetSize + base).toChar()
sb.insert(0, letter)
remainder /= alphabetSize
} while (remainder > 0)
return sb.toString()
}
}
}

View File

@ -0,0 +1,88 @@
package com.example.myapplication.database
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.myapplication.BikeApplication
import com.example.myapplication.database.entities.composeui.BikeListViewModel
import com.example.myapplication.database.entities.composeui.BikeViewModel
import com.example.myapplication.database.entities.composeui.edit.BikeEditViewModel
import com.example.myapplication.database.entities.composeui.edit.ItemEditViewModel
import com.example.myapplication.database.room.viewmodel.cart.CartViewModel
import com.example.myapplication.database.room.viewmodel.user.CurrentUserViewModel
import com.example.myapplication.database.room.viewmodel.user.EntryUserViewModel
import com.example.myapplication.database.room.viewmodel.item.ItemListViewModel
import com.example.myapplication.database.room.viewmodel.user.RegisterUserViewModel
import com.example.myapplication.database.room.viewmodel.rent.RentListViewModel
import com.example.myapplication.database.room.viewmodel.rent.RentViewModel
import com.example.myapplication.database.room.viewmodel.rent.ReportViewModel
object AppViewModelProvider {
val Factory = viewModelFactory {
initializer {
CurrentUserViewModel(bikeApplication().container.userRestRepository)
}
initializer {
EntryUserViewModel(bikeApplication().container.userRestRepository)
}
initializer {
RegisterUserViewModel(bikeApplication().container.userRestRepository)
}
initializer {
BikeListViewModel(bikeApplication().container.bikeRestRepository)
}
initializer {
ReportViewModel(
bikeApplication().container.itemRestRepository,
)
}
initializer {
BikeEditViewModel(
this.createSavedStateHandle(),
bikeApplication().container.bikeRestRepository
)
}
initializer {
BikeViewModel(
this.createSavedStateHandle(),
bikeApplication().container.bikeRestRepository,
)
}
initializer {
ItemListViewModel(
bikeApplication().container.itemRestRepository,
bikeApplication().container.userItemRestRepository,
)
}
initializer {
ItemEditViewModel(
this.createSavedStateHandle(),
bikeApplication().container.itemRestRepository,
)
}
initializer {
CartViewModel(
bikeApplication().container.userItemRestRepository,
bikeApplication().container.rentRestRepository,
bikeApplication().container.rentItemRestRepository,
bikeApplication().container.userRestRepository,
)
}
initializer {
RentListViewModel(
bikeApplication().container.rentRestRepository,
)
}
initializer {
RentViewModel(
this.createSavedStateHandle(),
bikeApplication().container.rentRestRepository,
)
}
}
}
fun CreationExtras.bikeApplication(): BikeApplication =
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as BikeApplication)

View File

@ -0,0 +1,120 @@
package com.example.myapplication.database.entities.composeui.edit
import android.content.res.Configuration
import android.graphics.BitmapFactory
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.myapplication.R
import com.example.myapplication.database.AppViewModelProvider
import com.example.myapplication.setting.ImageUploader
import com.example.myapplication.ui.theme.PmudemoTheme
import kotlinx.coroutines.launch
@Composable
fun BikeEdit(
navController: NavController,
viewModel: BikeEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
) {
val coroutineScope = rememberCoroutineScope()
BikeEdit(
bikeUiState = viewModel.bikeUiState,
onClick = {
coroutineScope.launch {
viewModel.saveBike()
navController.popBackStack()
}
},
onUpdate = viewModel::updateUiState,
)
}
@Composable
private fun BikeEdit(
bikeUiState: BikeUiState,
onClick: () -> Unit,
onUpdate: (BikeDetails) -> Unit,
) {
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = bikeUiState.bikeDetails.name,
onValueChange = { onUpdate(bikeUiState.bikeDetails.copy(name = it)) },
label = { Text(stringResource(id = R.string.Bike_name)) },
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onSecondary
)
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = bikeUiState.bikeDetails.description,
onValueChange = { onUpdate(bikeUiState.bikeDetails.copy(description = it)) },
label = { Text(stringResource(id = R.string.Bike_description)) },
singleLine = true,
textStyle = MaterialTheme.typography.bodyLarge.copy(
color = MaterialTheme.colorScheme.onSecondary
)
)
if (bikeUiState.bikeDetails.image != null)
ImageUploader(
bitmap = BitmapFactory.decodeByteArray(
bikeUiState.bikeDetails.image,
0,
bikeUiState.bikeDetails.image.size
),
onResult = { byteArray: ByteArray? ->
onUpdate(
bikeUiState.bikeDetails.copy(
image = byteArray
)
)
}
)
Button(
onClick = onClick,
enabled = bikeUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.Save_button))
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun BikeEditPreview() {
PmudemoTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
BikeEdit(
bikeUiState = BikeUiState(),
onClick = {},
onUpdate = {},
)
}
}
}

View File

@ -0,0 +1,92 @@
package com.example.myapplication.database.entities.composeui.edit
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.bike.BikeWithItems
import com.example.myapplication.database.room.model.item.ItemFromBike
import com.example.myapplication.database.room.repository.online.BikeRepository
import kotlinx.coroutines.launch
class BikeEditViewModel(
savedStateHandle: SavedStateHandle,
private val bikeRepository: BikeRepository
) : ViewModel() {
var bikeUiState by mutableStateOf(BikeUiState())
private set
private val bikeUid: Int = checkNotNull(savedStateHandle["id"])
init {
viewModelScope.launch {
if (bikeUid > 0) {
bikeUiState = bikeRepository.getBike(bikeUid)
.toUiState(true)
}
}
}
fun updateUiState(bikeDetails: BikeDetails) {
bikeUiState = BikeUiState(
bikeDetails = bikeDetails,
isEntryValid = validateInput(bikeDetails)
)
}
suspend fun saveBike() {
if (validateInput()) {
if (bikeUid > 0) {
bikeRepository.updateBike(bikeUiState.bikeDetails.toBike(bikeUid))
} else {
bikeRepository.insertBike(bikeUiState.bikeDetails.toBike())
}
}
}
private fun validateInput(uiState: BikeDetails = bikeUiState.bikeDetails): Boolean {
return with(uiState) {
name.isNotBlank()
&& description.isNotBlank()
}
}
}
data class BikeUiState(
val bikeDetails: BikeDetails = BikeDetails(),
val isEntryValid: Boolean = false
)
data class BikeDetails(
val name: String = "",
val description: String = "",
val image: ByteArray? = byteArrayOf(),
val items: List<ItemFromBike> = emptyList()
)
fun BikeDetails.toBike(uid: Int = 0): Bike = Bike(
uid = uid,
name = name,
description = description,
image = image
)
fun BikeWithItems.toDetails(): BikeDetails {
val bike = this.bike
val items = this.items
return BikeDetails(
name = bike.name,
description = bike.description,
image = bike.image,
items = items
)
}
fun BikeWithItems.toUiState(isEntryValid: Boolean = false): BikeUiState = BikeUiState(
bikeDetails = this.toDetails(),
isEntryValid = isEntryValid
)

View File

@ -0,0 +1,20 @@
package com.example.myapplication.database.remotekeys.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
import com.example.myapplication.database.remotekeys.model.RemoteKeys
@Dao
interface RemoteKeysDao {
@Query("SELECT * FROM remote_keys WHERE entityId = :entityId AND type = :type")
suspend fun getRemoteKeys(entityId: Int, type: RemoteKeyType): RemoteKeys?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(remoteKey: List<RemoteKeys>)
@Query("DELETE FROM remote_keys WHERE type = :type")
suspend fun clearRemoteKeys(type: RemoteKeyType)
}

View File

@ -0,0 +1,30 @@
package com.example.myapplication.database.remotekeys.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverter
import androidx.room.TypeConverters
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.model.rent.Rent
enum class RemoteKeyType(private val type: String) {
CINEMA(Bike::class.simpleName ?: "Bike"),
ORDER(Rent::class.simpleName ?: "Rent"),
SESSION(Item::class.simpleName ?: "Item");
@TypeConverter
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
@TypeConverter
fun fromRemoteKeyType(value: RemoteKeyType) = value.type
}
@Entity(tableName = "remote_keys")
data class RemoteKeys(
@PrimaryKey val entityId: Int,
@TypeConverters(RemoteKeyType::class)
val type: RemoteKeyType,
val prevKey: Int?,
val nextKey: Int?
)

View File

@ -0,0 +1,10 @@
package com.example.myapplication.database.remotekeys.repository
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
import com.example.myapplication.database.remotekeys.model.RemoteKeys
interface RemoteKeyRepository {
suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType): RemoteKeys?
suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>)
suspend fun deleteRemoteKey(type: RemoteKeyType)
}

View File

@ -0,0 +1,17 @@
package com.example.myapplication.database.remotekeys.repository.offline
import com.example.myapplication.database.remotekeys.dao.RemoteKeysDao
import com.example.myapplication.database.remotekeys.model.RemoteKeyType
import com.example.myapplication.database.remotekeys.model.RemoteKeys
import com.example.myapplication.database.remotekeys.repository.RemoteKeyRepository
class OfflineRemoteKeyRepository(private val remoteKeysDao: RemoteKeysDao) : RemoteKeyRepository {
override suspend fun getAllRemoteKeys(id: Int, type: RemoteKeyType) =
remoteKeysDao.getRemoteKeys(id, type)
override suspend fun createRemoteKeys(remoteKeys: List<RemoteKeys>) =
remoteKeysDao.insertAll(remoteKeys)
override suspend fun deleteRemoteKey(type: RemoteKeyType) =
remoteKeysDao.clearRemoteKeys(type)
}

View File

@ -0,0 +1,39 @@
package com.example.myapplication.database.room.dao.bike
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.item.ItemFromBike
import kotlinx.coroutines.flow.Flow
@Dao
interface BikeDao {
@Query("select * from bikes order by name")
fun getAll(): PagingSource<Int, Bike>
@Query(
"SELECT c.*, s.uid as item_uid, s.date_time, s.weight, s.radius,s.colorbike, s.max_count-IFNULL(SUM(os.count), 0) as available_count, c.uid as bike_id " +
"FROM bikes AS c " +
"LEFT JOIN items AS s ON s.bike_id = c.uid " +
"LEFT JOIN rents_items AS os ON os.item_id = s.uid " +
"WHERE c.uid = :bikeId " +
"GROUP BY item_uid"
)
fun getByUid(bikeId: Int?): Flow<Map<Bike, List<ItemFromBike>>>
@Insert
suspend fun insert(vararg bike: Bike)
@Update
suspend fun update(bike: Bike)
@Delete
suspend fun delete(bike: Bike)
@Query("DELETE FROM bikes")
suspend fun deleteAll()
}

View File

@ -0,0 +1,35 @@
package com.example.myapplication.database.room.dao.item
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.database.room.model.item.Item
@Dao
interface ItemDao {
@Query("select * from items where items.uid = :uid")
suspend fun getByUid(uid: Int): Item
@Insert
suspend fun insert(vararg item: Item)
@Update
suspend fun update(item: Item)
@Delete
suspend fun delete(item: Item)
@Query("DELETE FROM items")
suspend fun deleteAll()
@Query(
"SELECT s.max_count-IFNULL(SUM(os.count), 0) as available_count " +
"FROM items AS s " +
"LEFT JOIN rents_items AS os ON os.item_id = s.uid " +
"WHERE s.uid = :itemId " +
"GROUP BY s.uid"
)
suspend fun getAvailableCountOfItem(itemId: Int): Int
}

View File

@ -0,0 +1,37 @@
package com.example.myapplication.database.room.dao.rent
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.database.room.model.item.ItemFromRent
import com.example.myapplication.database.room.model.rent.Rent
@Dao
interface RentDao {
@Query("select * from rents where user_id = :userId")
fun getAll(userId: Int?): PagingSource<Int, Rent>
@Query(
"SELECT o.*, s.*, os.count, os.frozen_weight, os.radius, os.colorbike " +
"FROM rents AS o " +
"JOIN rents_items AS os ON os.rent_id = o.uid " +
"JOIN items AS s ON s.uid = os.item_id " +
"WHERE o.uid = :rentId"
)
fun getByUid(rentId: Int?): List<ItemFromRent>
@Insert
suspend fun insert(vararg rent: Rent): List<Long>
@Update
suspend fun update(rent: Rent)
@Delete
suspend fun delete(rent: Rent)
@Query("DELETE FROM rents")
suspend fun deleteAll()
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.database.room.dao.rent
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
@Dao
interface RentItemCrossRefDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(rentItemCrossRef: RentItemCrossRef)
@Update
suspend fun update(rentItemCrossRef: RentItemCrossRef)
@Delete
suspend fun delete(rentItemCrossRef: RentItemCrossRef)
@Query("DELETE FROM rents_items where rents_items.rent_id = :rentId")
suspend fun deleteByRentUid(rentId: Int)
@Query("DELETE FROM rents_items where rents_items.item_id = :itemId")
suspend fun deleteByItemUid(itemId: Int)
}

View File

@ -0,0 +1,42 @@
package com.example.myapplication.database.room.dao.user
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.database.room.model.item.ItemFromCart
import com.example.myapplication.database.room.model.user.User
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
@Query("select * from users")
suspend fun getAll(): List<User>
@Query("select * from users where users.uid = :idUser")
fun getUserById(idUser: Int?): Flow<User>
@Query(
"SELECT items.*, items.max_count-IFNULL(SUM(rents_items.count), 0) as available_count, " +
"users_items.count FROM items " +
"join users_items on items.uid = users_items.item_id " +
"left join rents_items on items.uid = rents_items.item_id " +
"where users_items.user_id = :userId " +
"GROUP BY users_items.item_id "
)
suspend fun getCartByUid(userId: Int): List<ItemFromCart>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(vararg user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
@Query("DELETE FROM users")
suspend fun deleteAll()
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.database.room.dao.user
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.database.room.model.user.UserItemCrossRef
@Dao
interface UserItemCrossRefDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(userItemCrossRef: UserItemCrossRef)
@Update
suspend fun update(userItemCrossRef: UserItemCrossRef)
@Delete
suspend fun delete(userItemCrossRef: UserItemCrossRef)
@Query("DELETE FROM users_items where users_items.user_id = :userId")
suspend fun deleteByUserUid(userId: Int)
@Query("DELETE FROM users_items where users_items.item_id = :itemId")
suspend fun deleteByItemUid(itemId: Int)
}

View File

@ -0,0 +1,52 @@
package com.example.myapplication.database.room.model.bike
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
@Entity(tableName = "bikes")
data class Bike(
@PrimaryKey(autoGenerate = true)
val uid: Int = 0,
val name: String,
val description: String,
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
val image: ByteArray?
) {
@Ignore
constructor(
name: String,
description: String,
image: ByteArray?
) : this(0, name, description, image)
companion object {
fun getBike(index: Int = 0): Bike {
return Bike(
index,
"name",
"desc",
byteArrayOf()
)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Bike
if (uid != other.uid) return false
if (name != other.name) return false
if (description != other.description) return false
return image.contentEquals(other.image)
}
override fun hashCode(): Int {
var result = uid
result = 31 * result + name.hashCode()
result = 31 * result + description.hashCode()
return result
}
}

View File

@ -0,0 +1,26 @@
package com.example.myapplication.database.room.model.bike
import com.example.myapplication.database.room.model.item.ItemFromBike
data class BikeWithItems(
val bike: Bike,
val items: List<ItemFromBike>
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as BikeWithItems
if (bike != other.bike) return false
if (items != other.items) return false
return true
}
override fun hashCode(): Int {
var result = bike.hashCode()
result = 31 * result + items.hashCode()
return result
}
}

View File

@ -0,0 +1,84 @@
package com.example.myapplication.database.room.model.item
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Ignore
import androidx.room.PrimaryKey
import com.example.myapplication.database.room.model.bike.Bike
import org.threeten.bp.LocalDateTime
@Entity(
tableName = "items", foreignKeys = [
ForeignKey(
entity = Bike::class,
parentColumns = ["uid"],
childColumns = ["bike_id"],
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.RESTRICT
)
]
)
data class Item(
@PrimaryKey(autoGenerate = true)
val uid: Int = 0,
@ColumnInfo(name = "date_time")
val dateTime: LocalDateTime,
val weight: Double,
val radius: Double,
val colorbike: String,
@ColumnInfo(name = "max_count")
val maxCount: Int,
@ColumnInfo(name = "bike_id", index = true)
val bikeId: Int = 0,
) {
@Ignore
constructor(
dateTime: LocalDateTime,
weight: Double,
radius: Double,
colorbike: String,
maxCount: Int,
bike: Bike,
) : this(0, dateTime, weight,radius,colorbike, maxCount, bike.uid)
companion object {
fun getItem(index: Int = 0): Item {
return Item(
index,
LocalDateTime.MIN,
0.0,
0.0,
"",
0,
0
)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Item
if (uid != other.uid) return false
if (dateTime != other.dateTime) return false
if (weight != other.weight) return false
if (radius != other.radius) return false
if (colorbike != other.colorbike) return false
if (maxCount != other.maxCount) return false
if (bikeId != other.bikeId) return false
return true
}
override fun hashCode(): Int {
var result = uid
result = 31 * result + dateTime.hashCode()
result = 31 * result + weight.hashCode()
result = 31 * result + radius.hashCode()
result = 31 * result + colorbike.hashCode()
result = 31 * result + maxCount.hashCode()
result = 31 * result + bikeId.hashCode()
return result
}
}

View File

@ -0,0 +1,55 @@
package com.example.myapplication.database.room.model.item
import androidx.room.ColumnInfo
import org.threeten.bp.LocalDateTime
import org.threeten.bp.format.DateTimeFormatter
data class ItemFromBike(
@ColumnInfo(name = "item_uid")
val uid: Int,
@ColumnInfo(name = "date_time")
val dateTime: LocalDateTime,
val weight: Double,
val radius: Double,
val colorbike: String,
@ColumnInfo(name = "available_count")
var availableCount: Int,
@ColumnInfo(name = "bike_id")
val bikeId: Int,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ItemFromBike
if (uid != other.uid) return false
val dateFormatter = DateTimeFormatter.ofPattern("yyyy")
if (dateFormatter.format(dateTime) != dateFormatter.format(other.dateTime)) return false
if (weight != other.weight) return false
if (radius != other.radius) return false
if (colorbike != other.colorbike) return false
if (availableCount != other.availableCount) return false
if (bikeId != other.bikeId) return false
return true
}
override fun hashCode(): Int {
var result = uid
result = 31 * result + dateTime.hashCode()
result = 31 * result + weight.hashCode()
result = 31 * result + radius.hashCode()
result = 31 * result + colorbike.hashCode()
result = 31 * result + availableCount.hashCode()
result = 31 * result + bikeId.hashCode()
return result
}
}
fun ItemFromBike.toItem(): Item = Item (
uid,
dateTime,
weight,
radius,
colorbike,
availableCount,
bikeId
)

View File

@ -0,0 +1,26 @@
package com.example.myapplication.database.room.model.item
import androidx.room.ColumnInfo
import androidx.room.Relation
import com.example.myapplication.database.room.model.bike.Bike
import org.threeten.bp.LocalDateTime
data class ItemFromCart(
@ColumnInfo(name = "uid")
val uid: Int = 0,
@ColumnInfo(name = "date_time")
val dateTime: LocalDateTime,
val weight: Double,
val radius: Double,
val colorbike: String,
@ColumnInfo(name = "available_count")
val availableCount: Int,
val count: Int,
@ColumnInfo(name = "bike_id")
val bikeId: Int = 0,
@Relation(
parentColumn = "bike_id",
entity = Bike::class,
entityColumn = "uid"
) val bike: Bike
)

View File

@ -0,0 +1,26 @@
package com.example.myapplication.database.room.model.item
import androidx.room.ColumnInfo
import androidx.room.Relation
import com.example.myapplication.database.room.model.bike.Bike
import org.threeten.bp.LocalDateTime
data class ItemFromRent(
@ColumnInfo(name = "uid")
val uid: Int = 0,
@ColumnInfo(name = "date_time")
val dateTime: LocalDateTime,
@ColumnInfo(name = "frozen_weight")
val frozenWeight: Double,
val radius: Double,
val colorbike: String,
val count: Int,
@ColumnInfo(name = "bike_id")
val bikeId: Int = 0,
@Relation(
parentColumn = "bike_id",
entity = Bike::class,
entityColumn = "uid"
)
val bike: Bike
)

View File

@ -0,0 +1,38 @@
package com.example.myapplication.database.room.model.rent
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.example.myapplication.database.room.model.user.User
@Entity(
tableName = "rents", foreignKeys = [
ForeignKey(
entity = User::class,
parentColumns = ["uid"],
childColumns = ["user_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT
)
]
)
data class Rent(
@PrimaryKey(autoGenerate = true)
val uid: Int,
@ColumnInfo(name = "user_id", index = true)
val userId: Int?,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Rent
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid
}
}

View File

@ -0,0 +1,39 @@
package com.example.myapplication.database.room.model.rent
import androidx.room.ColumnInfo
import androidx.room.Entity
import java.util.Objects
@Entity(
tableName = "rents_items",
primaryKeys = ["rent_id", "item_id"]
)
data class RentItemCrossRef(
@ColumnInfo(name = "rent_id", index = true)
val rentId: Int,
@ColumnInfo(name = "item_id", index = true)
val itemId: Int,
@ColumnInfo(name = "frozen_weight")
val frozenWeight: Double,
val radius: Double,
val colorbike: String,
val count: Int
) {
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (javaClass != other?.javaClass) {
return false
}
other as RentItemCrossRef
if (rentId == other.rentId && itemId == other.itemId) {
return true
}
return false
}
override fun hashCode(): Int {
return Objects.hash(rentId, itemId)
}
}

View File

@ -0,0 +1,25 @@
package com.example.myapplication.database.room.model.user
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true)
val uid: Int? = 0,
val login: String,
val role: String,
val password: String
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as User
if (uid != other.uid) return false
return true
}
override fun hashCode(): Int {
return uid ?: -1
}
}

View File

@ -0,0 +1,36 @@
package com.example.myapplication.database.room.model.user
import androidx.room.ColumnInfo
import androidx.room.Entity
import java.util.Objects.hash
@Entity(
tableName = "users_items",
primaryKeys = ["user_id", "item_id"]
)
data class UserItemCrossRef(
@ColumnInfo(name = "user_id", index = true)
val userId: Int,
@ColumnInfo(name = "item_id", index = true)
val itemId: Int,
@ColumnInfo(name = "count")
val count: Int,
) {
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (javaClass != other?.javaClass) {
return false
}
other as UserItemCrossRef
if (userId == other.userId && itemId == other.itemId) {
return true
}
return false
}
override fun hashCode(): Int {
return hash(userId, itemId)
}
}

View File

@ -0,0 +1,102 @@
package com.example.myapplication.database.room.repository
import android.content.Context
import com.example.myapplication.api.MyServerService
import com.example.myapplication.api.bike.repository.RestBikeRepository
import com.example.myapplication.api.rent.repository.RestRentRepository
import com.example.myapplication.api.rent.rentitem.repository.RestRentItemRepository
import com.example.myapplication.api.item.repository.RestItemRepository
import com.example.myapplication.api.user.repository.RestUserRepository
import com.example.myapplication.api.useritem.repository.RestUserItemRepository
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.database.remotekeys.repository.offline.OfflineRemoteKeyRepository
import com.example.myapplication.database.room.repository.offline.OfflineBikeRepository
import com.example.myapplication.database.room.repository.offline.OfflineItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineRentItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineRentRepository
import com.example.myapplication.database.room.repository.offline.OfflineUserItemRepository
import com.example.myapplication.database.room.repository.offline.OfflineUserRepository
interface AppContainer {
val bikeRestRepository: RestBikeRepository
val itemRestRepository: RestItemRepository
val userRestRepository: RestUserRepository
val rentRestRepository: RestRentRepository
val rentItemRestRepository: RestRentItemRepository
val userItemRestRepository: RestUserItemRepository
companion object {
const val TIMEOUT = 5000L
const val LIMIT = 10
}
}
class AppDataContainer(private val context: Context) : AppContainer {
private val bikeRepository: OfflineBikeRepository by lazy {
OfflineBikeRepository(AppDatabase.getInstance(context).bikeDao())
}
private val rentRepository: OfflineRentRepository by lazy {
OfflineRentRepository(AppDatabase.getInstance(context).rentDao())
}
private val rentItemRepository: OfflineRentItemRepository by lazy {
OfflineRentItemRepository(AppDatabase.getInstance(context).rentItemCrossRefDao())
}
private val itemRepository: OfflineItemRepository by lazy {
OfflineItemRepository(AppDatabase.getInstance(context).itemDao())
}
private val userRepository: OfflineUserRepository by lazy {
OfflineUserRepository(AppDatabase.getInstance(context).userDao())
}
private val userItemRepository: OfflineUserItemRepository by lazy {
OfflineUserItemRepository(AppDatabase.getInstance(context).userItemCrossRefDao())
}
private val remoteKeyRepository: OfflineRemoteKeyRepository by lazy {
OfflineRemoteKeyRepository(AppDatabase.getInstance(context).remoteKeysDao())
}
override val bikeRestRepository: RestBikeRepository by lazy {
RestBikeRepository(
MyServerService.getInstance(),
bikeRepository,
itemRepository,
remoteKeyRepository,
AppDatabase.getInstance(context)
)
}
override val itemRestRepository: RestItemRepository by lazy {
RestItemRepository(
MyServerService.getInstance(),
itemRepository,
userItemRepository,
rentItemRepository,
)
}
override val userRestRepository: RestUserRepository by lazy {
RestUserRepository(
MyServerService.getInstance(),
userRepository,
userItemRepository,
)
}
override val rentRestRepository: RestRentRepository by lazy {
RestRentRepository(
MyServerService.getInstance(),
rentRepository,
bikeRepository,
rentItemRepository,
remoteKeyRepository,
AppDatabase.getInstance(context)
)
}
override val userItemRestRepository: RestUserItemRepository by lazy {
RestUserItemRepository(
MyServerService.getInstance(),
userItemRepository,
)
}
override val rentItemRestRepository: RestRentItemRepository by lazy {
RestRentItemRepository(
MyServerService.getInstance(),
rentItemRepository,
)
}
}

View File

@ -0,0 +1,51 @@
package com.example.myapplication.database.room.repository.offline
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.myapplication.database.room.dao.bike.BikeDao
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.bike.BikeWithItems
import com.example.myapplication.database.room.repository.AppContainer
import com.example.myapplication.database.room.repository.online.BikeRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
class OfflineBikeRepository(private val bikeDao: BikeDao) : BikeRepository {
override fun getAllBikes(): Flow<PagingData<Bike>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = bikeDao::getAll
).flow
override suspend fun getBike(uid: Int): BikeWithItems {
val item = bikeDao.getByUid(uid)
.map { map ->
map.firstNotNullOf {
BikeWithItems(
bike = it.key,
items = it.value
)
}
}
.first()
return item
}
override suspend fun insertBike(bike: Bike) = bikeDao.insert(bike)
override suspend fun updateBike(bike: Bike) = bikeDao.update(bike)
override suspend fun deleteBike(bike: Bike) = bikeDao.delete(bike)
fun getAllBikesPagingSource(): PagingSource<Int, Bike> = bikeDao.getAll()
suspend fun insertBikes(bikes: List<Bike>) =
bikeDao.insert(*bikes.toTypedArray())
suspend fun clearBikes() = bikeDao.deleteAll()
}

View File

@ -0,0 +1,20 @@
package com.example.myapplication.database.room.repository.offline
import com.example.myapplication.database.room.dao.item.ItemDao
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.repository.online.ItemRepository
class OfflineItemRepository(private val itemDao: ItemDao) : ItemRepository {
override suspend fun getItem(uid: Int): Item = itemDao.getByUid(uid)
override suspend fun insertItem(item: Item) = itemDao.insert(item)
override suspend fun updateItem(item: Item) = itemDao.update(item)
override suspend fun deleteItem(item: Item) = itemDao.delete(item)
suspend fun insertItems(items: List<Item>) =
itemDao.insert(*items.toTypedArray())
suspend fun clearItems() = itemDao.deleteAll()
}

View File

@ -0,0 +1,22 @@
package com.example.myapplication.database.room.repository.offline
import com.example.myapplication.database.room.dao.rent.RentItemCrossRefDao
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
import com.example.myapplication.database.room.repository.online.RentItemRepository
class OfflineRentItemRepository(private val rentItemDao: RentItemCrossRefDao) :
RentItemRepository {
override suspend fun insertRentItem(rentItemCrossRef: RentItemCrossRef) =
rentItemDao.insert(rentItemCrossRef)
override suspend fun updateRentItem(rentItemCrossRef: RentItemCrossRef) =
rentItemDao.update(rentItemCrossRef)
override suspend fun deleteRentItem(rentItemCrossRef: RentItemCrossRef) =
rentItemDao.delete(rentItemCrossRef)
suspend fun deleteRentItems(userId: Int) = rentItemDao.deleteByRentUid(userId)
suspend fun deleteItemsByUid(itemId: Int) = rentItemDao.deleteByItemUid(itemId)
}

View File

@ -0,0 +1,36 @@
package com.example.myapplication.database.room.repository.offline
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.myapplication.database.room.dao.rent.RentDao
import com.example.myapplication.database.room.model.item.ItemFromRent
import com.example.myapplication.database.room.model.rent.Rent
import com.example.myapplication.database.room.repository.AppContainer
import com.example.myapplication.database.room.repository.online.RentRepository
import kotlinx.coroutines.flow.Flow
class OfflineRentRepository(private val rentDao: RentDao) : RentRepository {
override fun getAllRents(userId: Int?): Flow<PagingData<Rent>> = Pager(
config = PagingConfig(
pageSize = AppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = { rentDao.getAll(userId) }
).flow
override suspend fun getRent(uid: Int): List<ItemFromRent> = rentDao.getByUid(uid)
override suspend fun insertRent(rent: Rent): Long = rentDao.insert(rent).first()
fun getAllRentsPagingSource(userId: Int?): PagingSource<Int, Rent> = rentDao.getAll(userId)
suspend fun clearRents() = rentDao.deleteAll()
suspend fun insertRents(rents: List<Rent>) = rentDao.insert(*rents.toTypedArray())
override suspend fun deleteRent(rent: Rent) {
rentDao.delete(rent)
}
}

View File

@ -0,0 +1,21 @@
package com.example.myapplication.database.room.repository.offline
import com.example.myapplication.database.room.dao.user.UserItemCrossRefDao
import com.example.myapplication.database.room.model.user.UserItemCrossRef
import com.example.myapplication.database.room.repository.online.UserItemRepository
class OfflineUserItemRepository(private val userItemDao: UserItemCrossRefDao) :
UserItemRepository {
override suspend fun insertUserItem(userItemCrossRef: UserItemCrossRef) =
userItemDao.insert(userItemCrossRef)
override suspend fun updateUserItem(userItemCrossRef: UserItemCrossRef) =
userItemDao.update(userItemCrossRef)
override suspend fun deleteUserItem(userItemCrossRef: UserItemCrossRef) =
userItemDao.delete(userItemCrossRef)
override suspend fun deleteUserItems(userId: Int) = userItemDao.deleteByUserUid(userId)
suspend fun deleteItemsByUid(itemId: Int) = userItemDao.deleteByItemUid(itemId)
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.database.room.repository.offline
import com.example.myapplication.database.room.dao.user.UserDao
import com.example.myapplication.database.room.model.item.ItemFromCart
import com.example.myapplication.database.room.model.user.User
import com.example.myapplication.database.room.repository.online.UserRepository
import kotlinx.coroutines.flow.first
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
override suspend fun getAllUsers(): List<User> = userDao.getAll()
override suspend fun getUserById(idUser: Int?): User? = userDao.getUserById(idUser).first()
override suspend fun getCartByUser(userId: Int): List<ItemFromCart> =
userDao.getCartByUid(userId)
override suspend fun insertUser(user: User) = userDao.insert(user)
override suspend fun updateUser(user: User) = userDao.update(user)
override suspend fun deleteUser(user: User) = userDao.delete(user)
suspend fun insertUsers(users: List<User>) =
userDao.insert(*users.toTypedArray())
suspend fun clearUsers() = userDao.deleteAll()
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.database.room.repository.online
import androidx.paging.PagingData
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.model.bike.BikeWithItems
import kotlinx.coroutines.flow.Flow
interface BikeRepository {
fun getAllBikes(): Flow<PagingData<Bike>>
suspend fun getBike(uid: Int): BikeWithItems
suspend fun insertBike(bike: Bike)
suspend fun updateBike(bike: Bike)
suspend fun deleteBike(bike: Bike)
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.database.room.repository.online
import com.example.myapplication.database.room.model.item.Item
interface ItemRepository {
suspend fun getItem(uid: Int): Item
suspend fun insertItem(item: Item)
suspend fun updateItem(item: Item)
suspend fun deleteItem(item: Item)
}

View File

@ -0,0 +1,10 @@
package com.example.myapplication.database.room.repository.online
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
interface RentItemRepository {
suspend fun insertRentItem(rentItemCrossRef: RentItemCrossRef)
suspend fun updateRentItem(rentItemCrossRef: RentItemCrossRef)
suspend fun deleteRentItem(rentItemCrossRef: RentItemCrossRef)
}

View File

@ -0,0 +1,13 @@
package com.example.myapplication.database.room.repository.online
import androidx.paging.PagingData
import com.example.myapplication.database.room.model.item.ItemFromRent
import com.example.myapplication.database.room.model.rent.Rent
import kotlinx.coroutines.flow.Flow
interface RentRepository {
fun getAllRents(userId: Int?): Flow<PagingData<Rent>>
suspend fun getRent(uid: Int): List<ItemFromRent>
suspend fun insertRent(rent: Rent): Long
suspend fun deleteRent(rent: Rent)
}

View File

@ -0,0 +1,11 @@
package com.example.myapplication.database.room.repository.online
import com.example.myapplication.database.room.model.user.UserItemCrossRef
interface UserItemRepository {
suspend fun insertUserItem(userItemCrossRef: UserItemCrossRef)
suspend fun updateUserItem(userItemCrossRef: UserItemCrossRef)
suspend fun deleteUserItem(userItemCrossRef: UserItemCrossRef)
suspend fun deleteUserItems(userId: Int)
}

View File

@ -0,0 +1,14 @@
package com.example.myapplication.database.room.repository.online
import com.example.myapplication.database.room.model.item.ItemFromCart
import com.example.myapplication.database.room.model.user.User
interface UserRepository {
suspend fun getAllUsers(): List<User>
suspend fun getUserById(idUser: Int?): User?
suspend fun getCartByUser(userId: Int): List<ItemFromCart>
suspend fun insertUser(user: User)
suspend fun updateUser(user: User)
suspend fun deleteUser(user: User)
}

View File

@ -0,0 +1,50 @@
package com.example.myapplication.database.room.viewmodel
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.api.ApiStatus
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
open class LoadingViewModel : ViewModel() {
var apiStatus by mutableStateOf(ApiStatus.DONE)
private set
var apiError by mutableStateOf("")
private set
fun runInScope(
actionSuccess: suspend () -> Unit,
actionError: suspend () -> Unit,
needLoadingScreen: Boolean = true,
) {
viewModelScope.launch {
if (needLoadingScreen)
apiStatus = ApiStatus.LOADING
runCatching {
actionSuccess()
apiStatus = ApiStatus.DONE
apiError = ""
}.onFailure { e: Throwable ->
when (e) {
is IOException,
is HttpException -> {
actionError()
apiStatus = ApiStatus.ERROR
apiError = e.localizedMessage ?: e.toString()
}
else -> throw e
}
}
}
}
fun runInScope(actionSuccess: suspend () -> Unit) {
runInScope(actionSuccess, actionError = {})
}
}

View File

@ -0,0 +1,17 @@
package com.example.myapplication.database.entities.composeui
import androidx.lifecycle.ViewModel
import androidx.paging.PagingData
import com.example.myapplication.database.room.model.bike.Bike
import com.example.myapplication.database.room.repository.online.BikeRepository
import kotlinx.coroutines.flow.Flow
class BikeListViewModel(
private val bikeRepository: BikeRepository
) : ViewModel() {
val bikeListUiState: Flow<PagingData<Bike>> = bikeRepository.getAllBikes()
suspend fun deleteBike(bike: Bike) {
bikeRepository.deleteBike(bike)
}
}

View File

@ -0,0 +1,45 @@
package com.example.myapplication.database.entities.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import com.example.myapplication.database.room.model.bike.BikeWithItems
import com.example.myapplication.database.room.repository.online.BikeRepository
class BikeViewModel(
savedStateHandle: SavedStateHandle, private val bikeRepository: BikeRepository
) : ViewModel() {
private val bikeUid: Int = checkNotNull(savedStateHandle["id"])
var bikeUiState by mutableStateOf(BikeUiState())
private set
suspend fun refreshState() {
if (bikeUid > 0) {
bikeUiState = BikeUiState(bikeRepository.getBike(bikeUid))
}
}
// init {
// viewModelScope.launch {
// if (bikeUid > 0) {
// bikeUiState = BikeUiState(bikeRepository.getBike(bikeUid))
// }
// }
// }
// val bikeUiState: mutableStateOf(BikeUiState()) = bikeRepository.getBike(
// bikeUid
// ).map
// {
// BikeUiState(it)
// }.stateIn(
// scope = viewModelScope,
// started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
// initialValue = BikeUiState()
// )
}
data class BikeUiState(val bikeWithItems: BikeWithItems? = null)

View File

@ -0,0 +1,71 @@
package com.example.myapplication.database.room.viewmodel.cart
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.model.item.ItemFromCart
import com.example.myapplication.database.room.model.rent.Rent
import com.example.myapplication.database.room.model.rent.RentItemCrossRef
import com.example.myapplication.database.room.model.user.UserItemCrossRef
import com.example.myapplication.database.room.repository.online.RentItemRepository
import com.example.myapplication.database.room.repository.online.RentRepository
import com.example.myapplication.database.room.repository.online.UserItemRepository
import com.example.myapplication.database.room.repository.online.UserRepository
class CartViewModel(
private val userItemRepository: UserItemRepository,
private val rentRepository: RentRepository,
private val rentItemRepository: RentItemRepository,
private val userRepository: UserRepository,
) : ViewModel() {
//private val userUid: Int = 1
var cartUiState by mutableStateOf(CartUiState())
private set
suspend fun refreshState(userId: Int) {
val cart = userRepository.getCartByUser(userId)
cartUiState = CartUiState(cart)
}
suspend fun addToRent(userId: Int, items: List<ItemFromCart>) {
if (items.isEmpty())
return
val rentId = rentRepository.insertRent(Rent(0, userId))
items.forEach { item ->
rentItemRepository.insertRentItem(
RentItemCrossRef(
rentId.toInt(),
item.uid,
item.weight,
item.radius,
item.colorbike,
item.count
)
)
}
userItemRepository.deleteUserItems(userId)
refreshState(userId)
}
suspend fun removeFromCart(user: Int, item: Item, count: Int = 1) {
userItemRepository.deleteUserItem(UserItemCrossRef(user, item.uid, count))
refreshState(user)
}
suspend fun updateFromCart(userId: Int, item: Item, count: Int, availableCount: Int)
: Boolean {
if (count == 0) {
removeFromCart(userId, item, count)
return false
}
if (count > availableCount)
return false
userItemRepository.updateUserItem(UserItemCrossRef(userId, item.uid, count))
refreshState(userId)
return true
}
}
data class CartUiState(val itemList: List<ItemFromCart> = listOf())

View File

@ -0,0 +1,112 @@
package com.example.myapplication.database.entities.composeui.edit
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.repository.online.ItemRepository
import kotlinx.coroutines.launch
import org.threeten.bp.LocalDateTime
class ItemEditViewModel(
savedStateHandle: SavedStateHandle,
private val itemRepository: ItemRepository
) : ViewModel() {
var itemUiState by mutableStateOf(ItemUiState())
private set
private val itemUid: Int = checkNotNull(savedStateHandle["id"])
private val bikeUid: Int = checkNotNull(savedStateHandle["bikeId"])
init {
viewModelScope.launch {
if (itemUid > 0) {
itemUiState = itemRepository.getItem(itemUid)
.toUiState(true)
}
}
}
fun updateUiState(itemDetails: ItemDetails) {
itemUiState = ItemUiState(
itemDetails = itemDetails,
isEntryValid = validateInput(itemDetails)
)
}
suspend fun saveItem() {
if (validateInput()) {
if (bikeUid > 0)
if (itemUid > 0) {
itemRepository.updateItem(
itemUiState.itemDetails
.toItem(uid = itemUid, bikeUid = bikeUid)
)
} else {
itemRepository.insertItem(
itemUiState.itemDetails.toItem(
bikeUid = bikeUid
)
)
}
}
}
private fun validateInput(uiState: ItemDetails = itemUiState.itemDetails): Boolean {
return with(uiState) {
dateTime != LocalDateTime.MIN
&& isValidDouble(weight)
&& maxCount > 0
&& bikeUid > 0
}
}
}
val regex = """^-?\d+(.\d+)?+(,\d+)?$""".toRegex()
fun isValidDouble(input: String): Boolean {
return regex.matches(input)
}
data class ItemUiState(
val itemDetails: ItemDetails = ItemDetails(),
val isEntryValid: Boolean = false
)
data class ItemDetails(
val uid: Int = 0,
val dateTime: LocalDateTime = LocalDateTime.MIN,
val weight: String = "0",
val radius: String = "0",
val colorbike: String = "0",
val maxCount: Int = 0,
val bikeId: Int = 0
)
fun ItemDetails.toItem(uid: Int = 0, bikeUid: Int = 0): Item = Item(
uid = uid,
dateTime = dateTime,
weight = weight.toDoubleOrNull() ?: 0.0,
radius = radius.toDoubleOrNull() ?: 0.0,
colorbike = colorbike,
maxCount = maxCount,
bikeId = bikeUid
)
fun Item.toDetails(): ItemDetails = ItemDetails(
uid = uid,
dateTime = dateTime,
weight = weight.toString(),
radius = radius.toString(),
colorbike = colorbike,
maxCount = maxCount,
bikeId = bikeId
)
fun Item.toUiState(isEntryValid: Boolean = false): ItemUiState = ItemUiState(
itemDetails = this.toDetails(),
isEntryValid = isEntryValid
)

View File

@ -0,0 +1,34 @@
package com.example.myapplication.database.room.viewmodel.item
import androidx.lifecycle.ViewModel
import com.example.myapplication.database.room.model.item.Item
import com.example.myapplication.database.room.model.item.ItemFromBike
import com.example.myapplication.database.room.model.user.UserItemCrossRef
import com.example.myapplication.database.room.repository.online.ItemRepository
import com.example.myapplication.database.room.repository.online.UserItemRepository
class ItemListViewModel(
private val itemRepository: ItemRepository,
private val userItemRepository: UserItemRepository
) : ViewModel() {
suspend fun deleteItem(item: ItemFromBike) {
itemRepository.deleteItem(
Item(
uid = item.uid,
dateTime = item.dateTime,
weight = item.weight,
radius = item.radius,
colorbike = item.colorbike,
maxCount = 0,
bikeId = 0
)
)
}
suspend fun addItemInCart(userId: Int, itemId: Int, count: Int = 1) {
try {
userItemRepository.insertUserItem(UserItemCrossRef(userId, itemId, count))
} catch (_: Exception) {
}
}
}

View File

@ -0,0 +1,31 @@
package com.example.myapplication.database.room.viewmodel.rent
import androidx.lifecycle.ViewModel
import androidx.paging.PagingData
import com.example.myapplication.database.room.model.rent.Rent
import com.example.myapplication.database.room.repository.online.RentRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
class RentListViewModel(
private val rentRepository: RentRepository
) : ViewModel() {
private val _userId = MutableStateFlow<Int?>(null)
val userId: StateFlow<Int?> get() = _userId
val rentListUiState: Flow<PagingData<Rent>> = userId.flatMapLatest { userId ->
rentRepository.getAllRents(userId)
}
suspend fun deleteRent(rent: Rent) {
rentRepository.deleteRent(rent)
}
fun setUserId(userId: Int) {
_userId.value = userId
}
}
data class RentListUiState(val rentList: List<Rent> = listOf())

View File

@ -0,0 +1,30 @@
package com.example.myapplication.database.room.viewmodel.rent
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.room.model.item.ItemFromRent
import com.example.myapplication.database.room.repository.AppContainer
import com.example.myapplication.database.room.repository.online.RentRepository
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
class RentViewModel(
savedStateHandle: SavedStateHandle,
private val rentRepository: RentRepository
) : ViewModel() {
private val rentUid: Int = checkNotNull(savedStateHandle["id"])
val rentUiState: StateFlow<RentUiState> = flow{emit(rentRepository.getRent(rentUid))} .map {
RentUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppContainer.TIMEOUT),
initialValue = RentUiState()
)
}
data class RentUiState(val itemList: List<ItemFromRent> = listOf())

View File

@ -0,0 +1,108 @@
package com.example.myapplication.database.room.viewmodel.rent
import android.content.Context
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.api.item.repository.RestItemRepository
import com.example.myapplication.api.report.remote.ReportRemote
import kotlinx.coroutines.launch
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
class ReportViewModel(private val itemRespository: RestItemRepository): ViewModel() {
var reportPageUiState by mutableStateOf(ReportPageUiState())
private set
var reportResultPageUiState by mutableStateOf(ReportResultPageUiState())
private set
var cardVisible by mutableStateOf(false)
private set
var errorState by mutableStateOf<String?>(null)
fun onUpdate(reportDetails: ReportDetails) {
reportPageUiState = ReportPageUiState(reportDetails = reportDetails, isEntryValid = validateInput(reportDetails))
}
private fun validateInput(uiState: ReportDetails = reportPageUiState.reportDetails): Boolean {
Log.d("validateInput", uiState.endDate.toString())
return with(uiState) {
startDate != Date(0) && endDate != Date(0) && startDate < endDate
}
}
suspend fun getReport() {
val dateFormatter = SimpleDateFormat("yyyy", Locale.getDefault())
// Преобразование даты в строку в формате "yyyy"
val startDateQueryParam = dateFormatter.format(reportPageUiState.reportDetails.startDate)
val endDateQueryParam = dateFormatter.format(reportPageUiState.reportDetails.endDate)
// Вызов репозитория с отформатированными датами
val res = itemRespository.getReport(startDateQueryParam, endDateQueryParam)
reportResultPageUiState = ReportResultPageUiState(res)
}
suspend fun generatePdfFile(context: Context, fileName: String, reportData: List<ReportRemote>) {
itemRespository.createPdfFile(context, fileName, reportData)
}
fun onButtonClick(startDate: String, endDate: String) {
viewModelScope.launch {
val parsedStartDate = parseDate(startDate)
val parsedEndDate = parseDate(endDate)
if (validateDates(parsedStartDate, parsedEndDate)) {
updateReportDetails(parsedStartDate, parsedEndDate)
getReport()
cardVisible = true
} else {
Log.d("Ошибка даты", "Неправильно указаны даты или года не являются 4-значными =${startDate}")
errorState = "Ошибка фильтраций: неверно указаны даты"
}
}
}
private fun validateDates(startDate: Date?, endDate: Date?): Boolean {
return startDate != null && endDate != null && startDate < endDate
}
private fun updateReportDetails(startDate: Date?, endDate: Date?) {
if (startDate != null && endDate != null) {
onUpdate(reportPageUiState.reportDetails.copy(startDate = startDate, endDate = endDate))
}
}
private fun parseDate(dateString: String): Date? {
return try {
// Use SimpleDateFormat or any other method to parse the string into a Date object
// For example:
SimpleDateFormat("yyyy", Locale.getDefault()).parse(dateString)
} catch (e: ParseException) {
// Handle parsing error
null
}
}
}
data class ReportDetails(
val startDate: Date = Date(0),
val endDate: Date = Date(0)
)
data class ReportPageUiState(
val reportDetails: ReportDetails = ReportDetails(),
val isEntryValid: Boolean = false
)
data class ReportResultPageUiState(
var resReport: List<ReportRemote> = emptyList()
)

View File

@ -0,0 +1,28 @@
package com.example.myapplication.database.room.viewmodel.user
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.room.model.user.User
import com.example.myapplication.database.room.repository.online.UserRepository
import kotlinx.coroutines.launch
class CurrentUserViewModel(private val userRepository: UserRepository) : ViewModel(){
val argument = mutableStateOf<String?>(null)
private val userid = mutableStateOf<Int?>(null)
var user by mutableStateOf<User?>(null)
fun setArgument(arg: String) {
argument.value = arg
userid.value = arg.toInt()
viewModelScope.launch {
user = userRepository.getUserById(userid.value)
}
}
fun setNullUser() {
user = null
}
}

View File

@ -0,0 +1,24 @@
package com.example.myapplication.database.room.viewmodel.user
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.room.model.user.User
import com.example.myapplication.database.room.repository.online.UserRepository
import kotlinx.coroutines.launch
class EntryUserViewModel(private val userRepository: UserRepository) : ViewModel() {
var userList by mutableStateOf<List<User>>(emptyList())
fun setUserList() {
viewModelScope.launch {
userList=userRepository.getAllUsers()
}
}
fun loginUser(username: String, password: String): User? {
return userList.find { it.login == username && it.password == password }
}
}

View File

@ -0,0 +1,38 @@
package com.example.myapplication.database.room.viewmodel.user
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapplication.database.room.model.user.User
import com.example.myapplication.database.room.repository.online.UserRepository
import kotlinx.coroutines.launch
class RegisterUserViewModel(private val userRepository: UserRepository) : ViewModel() {
private val _users = MutableLiveData<List<User>>()
val users: LiveData<List<User>> get() = _users
fun setUserList() {
viewModelScope.launch {
_users.value = userRepository.getAllUsers()
}
}
suspend fun insertUser(user: User) {
userRepository.insertUser(user)
}
suspend fun registerUser(username: String, password: String): Boolean {
if (password.isNotEmpty() && username.isNotEmpty()) {
val existingUser = _users.value?.find { it.login == username }
if (existingUser == null) {
val newUser = User(null, username,"user" ,password)
insertUser(newUser)
setUserList()
return true
}
}
return false
}
}

Some files were not shown because too many files have changed in this diff Show More