init
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
*.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
@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
6
.idea/compiler.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<bytecodeTargetLevel target="21" />
|
||||||
|
</component>
|
||||||
|
</project>
|
25
.idea/deploymentTargetSelector.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="FurniturePreview">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="FurnitureTextFieldPreview">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="PasswordFurnitureTextField">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="LoginScreen">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="MainActivity">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
19
.idea/gradle.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="resolveExternalAnnotations" value="false" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
57
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
<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="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK 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="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
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="KotlinJpsPluginSettings">
|
||||||
|
<option name="version" value="2.0.0" />
|
||||||
|
</component>
|
||||||
|
</project>
|
10
.idea/migrations.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectMigrations">
|
||||||
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
794
.idea/misc.xml
Normal file
@ -0,0 +1,794 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DeviceStreaming">
|
||||||
|
<option name="deviceSelectionList">
|
||||||
|
<list>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="Sony" />
|
||||||
|
<option name="codename" value="A402SO" />
|
||||||
|
<option name="id" value="A402SO" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Sony" />
|
||||||
|
<option name="name" value="Xperia 10" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2520" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="27" />
|
||||||
|
<option name="brand" value="DOCOMO" />
|
||||||
|
<option name="codename" value="F01L" />
|
||||||
|
<option name="id" value="F01L" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="FUJITSU" />
|
||||||
|
<option name="name" value="F-01L" />
|
||||||
|
<option name="screenDensity" value="360" />
|
||||||
|
<option name="screenX" value="720" />
|
||||||
|
<option name="screenY" value="1280" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="OnePlus" />
|
||||||
|
<option name="codename" value="OP535DL1" />
|
||||||
|
<option name="id" value="OP535DL1" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="OnePlus" />
|
||||||
|
<option name="name" value="CPH2409" />
|
||||||
|
<option name="screenDensity" value="401" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2412" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="OnePlus" />
|
||||||
|
<option name="codename" value="OP5552L1" />
|
||||||
|
<option name="id" value="OP5552L1" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="OnePlus" />
|
||||||
|
<option name="name" value="CPH2415" />
|
||||||
|
<option name="screenDensity" value="480" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2412" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="OPPO" />
|
||||||
|
<option name="codename" value="OP573DL1" />
|
||||||
|
<option name="id" value="OP573DL1" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="OPPO" />
|
||||||
|
<option name="name" value="CPH2557" />
|
||||||
|
<option name="screenDensity" value="480" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="28" />
|
||||||
|
<option name="brand" value="DOCOMO" />
|
||||||
|
<option name="codename" value="SH-01L" />
|
||||||
|
<option name="id" value="SH-01L" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="SHARP" />
|
||||||
|
<option name="name" value="AQUOS sense2 SH-01L" />
|
||||||
|
<option name="screenDensity" value="480" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2160" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="a15" />
|
||||||
|
<option name="id" value="a15" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="A15" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="a15x" />
|
||||||
|
<option name="id" value="a15x" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="A15 5G" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="a16x" />
|
||||||
|
<option name="id" value="a16x" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="A16 5G" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="a35x" />
|
||||||
|
<option name="id" value="a35x" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="A35" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="31" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="a51" />
|
||||||
|
<option name="id" value="a51" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy A51" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="akita" />
|
||||||
|
<option name="id" value="akita" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 8a" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="arcfox" />
|
||||||
|
<option name="id" value="arcfox" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="razr plus 2024" />
|
||||||
|
<option name="screenDensity" value="360" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="1272" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="austin" />
|
||||||
|
<option name="id" value="austin" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="moto g 5G (2022)" />
|
||||||
|
<option name="screenDensity" value="280" />
|
||||||
|
<option name="screenX" value="720" />
|
||||||
|
<option name="screenY" value="1600" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="b0q" />
|
||||||
|
<option name="id" value="b0q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S22 Ultra" />
|
||||||
|
<option name="screenDensity" value="600" />
|
||||||
|
<option name="screenX" value="1440" />
|
||||||
|
<option name="screenY" value="3088" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="b6q" />
|
||||||
|
<option name="id" value="b6q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Flip 6" />
|
||||||
|
<option name="screenDensity" value="340" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2640" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="32" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="bluejay" />
|
||||||
|
<option name="id" value="bluejay" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 6a" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="caiman" />
|
||||||
|
<option name="id" value="caiman" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9 Pro" />
|
||||||
|
<option name="screenDensity" value="360" />
|
||||||
|
<option name="screenX" value="960" />
|
||||||
|
<option name="screenY" value="2142" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="35" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="caiman" />
|
||||||
|
<option name="id" value="caiman" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9 Pro" />
|
||||||
|
<option name="screenDensity" value="360" />
|
||||||
|
<option name="screenX" value="960" />
|
||||||
|
<option name="screenY" value="2142" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="comet" />
|
||||||
|
<option name="default" value="true" />
|
||||||
|
<option name="id" value="comet" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9 Pro Fold" />
|
||||||
|
<option name="screenDensity" value="390" />
|
||||||
|
<option name="screenX" value="2076" />
|
||||||
|
<option name="screenY" value="2152" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="35" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="comet" />
|
||||||
|
<option name="default" value="true" />
|
||||||
|
<option name="id" value="comet" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9 Pro Fold" />
|
||||||
|
<option name="screenDensity" value="390" />
|
||||||
|
<option name="screenX" value="2076" />
|
||||||
|
<option name="screenY" value="2152" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="29" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="crownqlteue" />
|
||||||
|
<option name="id" value="crownqlteue" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy Note9" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="2220" />
|
||||||
|
<option name="screenY" value="1080" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="dm2q" />
|
||||||
|
<option name="id" value="dm2q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="S23 Plus" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="dm3q" />
|
||||||
|
<option name="id" value="dm3q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S23 Ultra" />
|
||||||
|
<option name="screenDensity" value="600" />
|
||||||
|
<option name="screenX" value="1440" />
|
||||||
|
<option name="screenY" value="3088" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="dubai" />
|
||||||
|
<option name="id" value="dubai" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="edge 30" />
|
||||||
|
<option name="screenDensity" value="405" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="e1q" />
|
||||||
|
<option name="default" value="true" />
|
||||||
|
<option name="id" value="e1q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S24" />
|
||||||
|
<option name="screenDensity" value="480" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="e3q" />
|
||||||
|
<option name="id" value="e3q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S24 Ultra" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1440" />
|
||||||
|
<option name="screenY" value="3120" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="eos" />
|
||||||
|
<option name="id" value="eos" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Eos" />
|
||||||
|
<option name="screenDensity" value="320" />
|
||||||
|
<option name="screenX" value="384" />
|
||||||
|
<option name="screenY" value="384" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="felix" />
|
||||||
|
<option name="id" value="felix" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel Fold" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="2208" />
|
||||||
|
<option name="screenY" value="1840" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="felix" />
|
||||||
|
<option name="id" value="felix" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel Fold" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="2208" />
|
||||||
|
<option name="screenY" value="1840" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="felix_camera" />
|
||||||
|
<option name="id" value="felix_camera" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel Fold (Camera-enabled)" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="2208" />
|
||||||
|
<option name="screenY" value="1840" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="fogona" />
|
||||||
|
<option name="id" value="fogona" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="moto g play - 2024" />
|
||||||
|
<option name="screenDensity" value="280" />
|
||||||
|
<option name="screenX" value="720" />
|
||||||
|
<option name="screenY" value="1600" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="fogos" />
|
||||||
|
<option name="id" value="fogos" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="moto g34 5G" />
|
||||||
|
<option name="screenDensity" value="280" />
|
||||||
|
<option name="screenX" value="720" />
|
||||||
|
<option name="screenY" value="1600" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="g0q" />
|
||||||
|
<option name="id" value="g0q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-S906U1" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gta9pwifi" />
|
||||||
|
<option name="id" value="gta9pwifi" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-X210" />
|
||||||
|
<option name="screenDensity" value="240" />
|
||||||
|
<option name="screenX" value="1200" />
|
||||||
|
<option name="screenY" value="1920" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gts7lwifi" />
|
||||||
|
<option name="id" value="gts7lwifi" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-T870" />
|
||||||
|
<option name="screenDensity" value="340" />
|
||||||
|
<option name="screenX" value="1600" />
|
||||||
|
<option name="screenY" value="2560" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gts7xllite" />
|
||||||
|
<option name="id" value="gts7xllite" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-T738U" />
|
||||||
|
<option name="screenDensity" value="340" />
|
||||||
|
<option name="screenX" value="1600" />
|
||||||
|
<option name="screenY" value="2560" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gts8uwifi" />
|
||||||
|
<option name="formFactor" value="Tablet" />
|
||||||
|
<option name="id" value="gts8uwifi" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy Tab S8 Ultra" />
|
||||||
|
<option name="screenDensity" value="320" />
|
||||||
|
<option name="screenX" value="1848" />
|
||||||
|
<option name="screenY" value="2960" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gts8wifi" />
|
||||||
|
<option name="formFactor" value="Tablet" />
|
||||||
|
<option name="id" value="gts8wifi" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy Tab S8" />
|
||||||
|
<option name="screenDensity" value="274" />
|
||||||
|
<option name="screenX" value="1600" />
|
||||||
|
<option name="screenY" value="2560" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gts9fe" />
|
||||||
|
<option name="id" value="gts9fe" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy Tab S9 FE 5G" />
|
||||||
|
<option name="screenDensity" value="280" />
|
||||||
|
<option name="screenX" value="1440" />
|
||||||
|
<option name="screenY" value="2304" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="gts9wifi" />
|
||||||
|
<option name="id" value="gts9wifi" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-X710" />
|
||||||
|
<option name="screenDensity" value="340" />
|
||||||
|
<option name="screenX" value="1600" />
|
||||||
|
<option name="screenY" value="2560" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="husky" />
|
||||||
|
<option name="id" value="husky" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 8 Pro" />
|
||||||
|
<option name="screenDensity" value="390" />
|
||||||
|
<option name="screenX" value="1008" />
|
||||||
|
<option name="screenY" value="2244" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="30" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="java" />
|
||||||
|
<option name="id" value="java" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="G20" />
|
||||||
|
<option name="screenDensity" value="280" />
|
||||||
|
<option name="screenX" value="720" />
|
||||||
|
<option name="screenY" value="1600" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="komodo" />
|
||||||
|
<option name="id" value="komodo" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9 Pro XL" />
|
||||||
|
<option name="screenDensity" value="360" />
|
||||||
|
<option name="screenX" value="1008" />
|
||||||
|
<option name="screenY" value="2244" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="35" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="komodo" />
|
||||||
|
<option name="id" value="komodo" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9 Pro XL" />
|
||||||
|
<option name="screenDensity" value="360" />
|
||||||
|
<option name="screenX" value="1008" />
|
||||||
|
<option name="screenY" value="2244" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="lynx" />
|
||||||
|
<option name="id" value="lynx" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 7a" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="manaus" />
|
||||||
|
<option name="id" value="manaus" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="edge 40 neo" />
|
||||||
|
<option name="screenDensity" value="400" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="motorola" />
|
||||||
|
<option name="codename" value="maui" />
|
||||||
|
<option name="id" value="maui" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Motorola" />
|
||||||
|
<option name="name" value="moto g play - 2023" />
|
||||||
|
<option name="screenDensity" value="280" />
|
||||||
|
<option name="screenX" value="720" />
|
||||||
|
<option name="screenY" value="1600" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="o1q" />
|
||||||
|
<option name="id" value="o1q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S21" />
|
||||||
|
<option name="screenDensity" value="421" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="31" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="oriole" />
|
||||||
|
<option name="id" value="oriole" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 6" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="35" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="pa3q" />
|
||||||
|
<option name="id" value="pa3q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S25 Ultra" />
|
||||||
|
<option name="screenDensity" value="600" />
|
||||||
|
<option name="screenX" value="1440" />
|
||||||
|
<option name="screenY" value="3120" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="panther" />
|
||||||
|
<option name="id" value="panther" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 7" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="q5q" />
|
||||||
|
<option name="id" value="q5q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy Z Fold5" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1812" />
|
||||||
|
<option name="screenY" value="2176" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="q6q" />
|
||||||
|
<option name="id" value="q6q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy Z Fold6" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1856" />
|
||||||
|
<option name="screenY" value="2160" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="30" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="r11" />
|
||||||
|
<option name="formFactor" value="Wear OS" />
|
||||||
|
<option name="id" value="r11" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel Watch" />
|
||||||
|
<option name="screenDensity" value="320" />
|
||||||
|
<option name="screenX" value="384" />
|
||||||
|
<option name="screenY" value="384" />
|
||||||
|
<option name="type" value="WEAR_OS" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="r11q" />
|
||||||
|
<option name="id" value="r11q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-S711U" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="30" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="redfin" />
|
||||||
|
<option name="id" value="redfin" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 5" />
|
||||||
|
<option name="screenDensity" value="440" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2340" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="shiba" />
|
||||||
|
<option name="id" value="shiba" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 8" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="t2q" />
|
||||||
|
<option name="id" value="t2q" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="Galaxy S21 Plus" />
|
||||||
|
<option name="screenDensity" value="394" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2400" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="33" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="tangorpro" />
|
||||||
|
<option name="formFactor" value="Tablet" />
|
||||||
|
<option name="id" value="tangorpro" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel Tablet" />
|
||||||
|
<option name="screenDensity" value="320" />
|
||||||
|
<option name="screenX" value="1600" />
|
||||||
|
<option name="screenY" value="2560" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="35" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="tegu" />
|
||||||
|
<option name="id" value="tegu" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9a" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2424" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="tokay" />
|
||||||
|
<option name="default" value="true" />
|
||||||
|
<option name="id" value="tokay" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2424" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="35" />
|
||||||
|
<option name="brand" value="google" />
|
||||||
|
<option name="codename" value="tokay" />
|
||||||
|
<option name="default" value="true" />
|
||||||
|
<option name="id" value="tokay" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Google" />
|
||||||
|
<option name="name" value="Pixel 9" />
|
||||||
|
<option name="screenDensity" value="420" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2424" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="samsung" />
|
||||||
|
<option name="codename" value="xcover7" />
|
||||||
|
<option name="id" value="xcover7" />
|
||||||
|
<option name="labId" value="google" />
|
||||||
|
<option name="manufacturer" value="Samsung" />
|
||||||
|
<option name="name" value="SM-G556B" />
|
||||||
|
<option name="screenDensity" value="450" />
|
||||||
|
<option name="screenX" value="1080" />
|
||||||
|
<option name="screenY" value="2408" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
</project>
|
17
.idea/runConfigurations.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
4
.idea/vcs.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||||
|
</project>
|
1
app/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/build
|
80
app/build.gradle.kts
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
alias(libs.plugins.kotlin.android)
|
||||||
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
kotlin("plugin.serialization") version "2.1.20"
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.example.furintiture"
|
||||||
|
compileSdk = 35
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.example.furintiture"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 35
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "11"
|
||||||
|
}
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation(libs.retrofit)
|
||||||
|
implementation(libs.converter.kotlinx.serialization)
|
||||||
|
implementation(libs.okhttp)
|
||||||
|
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
|
implementation(libs.kotlinx.datetime)
|
||||||
|
|
||||||
|
implementation (libs.androidx.datastore.preferences)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||||
|
|
||||||
|
|
||||||
|
implementation(libs.coil.compose)
|
||||||
|
implementation(libs.coil.network.okhttp)
|
||||||
|
|
||||||
|
implementation (libs.androidx.navigation.compose)
|
||||||
|
|
||||||
|
|
||||||
|
implementation(libs.androidx.core.ktx)
|
||||||
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
|
implementation(libs.androidx.activity.compose)
|
||||||
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
|
implementation(libs.androidx.ui)
|
||||||
|
implementation(libs.androidx.ui.graphics)
|
||||||
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
|
implementation(libs.androidx.material3)
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||||
|
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||||
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
|
}
|
21
app/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.example.furintiture
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("com.example.furintiture", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
29
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
|
android:theme="@style/Theme.Furintiture"
|
||||||
|
tools:targetApi="31">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:theme="@style/Theme.Furintiture">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
89
app/src/main/java/com/example/furintiture/MainActivity.kt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package com.example.furintiture
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import com.example.furintiture.data.DataStoreSettings
|
||||||
|
import com.example.furintiture.ui.common.BottomNavigation
|
||||||
|
import com.example.furintiture.ui.common.Destination
|
||||||
|
import com.example.furintiture.ui.common.FurnitureBottomNavigation
|
||||||
|
import com.example.furintiture.ui.screen.Cart
|
||||||
|
import com.example.furintiture.ui.screen.FurnitureGlobalNavigation
|
||||||
|
import com.example.furintiture.ui.screen.Login
|
||||||
|
import com.example.furintiture.ui.screen.Main
|
||||||
|
import com.example.furintiture.ui.screen.Order
|
||||||
|
import com.example.furintiture.ui.screen.Registration
|
||||||
|
import com.example.furintiture.ui.screen.Splash
|
||||||
|
import com.example.furintiture.ui.theme.FurintitureTheme
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class MainActivity : ComponentActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
|
val dataStoreSettings = DataStoreSettings(applicationContext)
|
||||||
|
setContent {
|
||||||
|
FurintitureTheme {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
val bottomBarIsVisible = remember { mutableStateOf(false) }
|
||||||
|
val userUuid = remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
bottomBar = {
|
||||||
|
if(bottomBarIsVisible.value) FurnitureBottomNavigation(){ destination ->
|
||||||
|
when(destination.route){
|
||||||
|
BottomNavigation.Home -> navController.navigate(Main(userUuid.value))
|
||||||
|
BottomNavigation.Cart -> navController.navigate(Cart(userUuid.value))
|
||||||
|
BottomNavigation.Order -> {
|
||||||
|
navController.navigate(Order(userUuid.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
paddingValues
|
||||||
|
FurnitureGlobalNavigation(dataStoreSettings, navController){
|
||||||
|
bottomBarIsVisible.value = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
navController.clearBackStack(Splash)
|
||||||
|
dataStoreSettings.userUuidFlow.collect{
|
||||||
|
if(it.isEmpty()){
|
||||||
|
navController.navigate(Login){
|
||||||
|
popUpTo(Splash){
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
userUuid.value = it
|
||||||
|
navController.navigate(Main(it)){
|
||||||
|
popUpTo(Splash){
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
popUpTo(Registration){
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
popUpTo(Login){
|
||||||
|
inclusive = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.example.furintiture.configure
|
||||||
|
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveKind
|
||||||
|
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
||||||
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
|
import kotlinx.serialization.encoding.Decoder
|
||||||
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
object BigDecimalSerializer: KSerializer<BigDecimal> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): BigDecimal {
|
||||||
|
return BigDecimal(decoder.decodeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: BigDecimal) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object UuidSerializer: KSerializer<UUID> {
|
||||||
|
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UUID", PrimitiveKind.STRING)
|
||||||
|
|
||||||
|
override fun deserialize(decoder: Decoder): UUID {
|
||||||
|
return UUID.fromString(decoder.decodeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun serialize(encoder: Encoder, value: UUID) {
|
||||||
|
encoder.encodeString(value.toString())
|
||||||
|
}
|
||||||
|
}
|
25
app/src/main/java/com/example/furintiture/data/DataStore.kt
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.example.furintiture.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.preferences.core.Preferences
|
||||||
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
|
class DataStoreSettings(private val context: Context){
|
||||||
|
private val UUID_KEY = stringPreferencesKey("user_uuid")
|
||||||
|
val userUuidFlow: Flow<String> = context.dataStore.data.map { preferences ->
|
||||||
|
preferences[UUID_KEY] ?: ""
|
||||||
|
}
|
||||||
|
suspend fun setUuid(uuid: UUID){
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[UUID_KEY] = uuid.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.example.furintiture.data
|
||||||
|
|
||||||
|
import com.example.furintiture.data.api.FurnitureApi
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||||
|
|
||||||
|
private const val BASE_URL = "http://185.207.0.137:8090"
|
||||||
|
object RetrofitClient {
|
||||||
|
private val mediaType = "application/json".toMediaType()
|
||||||
|
private val client by lazy {
|
||||||
|
Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL)
|
||||||
|
.addConverterFactory(Json.asConverterFactory(mediaType))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
val api by lazy {
|
||||||
|
client.create(FurnitureApi::class.java)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.example.furintiture.data.api
|
||||||
|
|
||||||
|
import com.example.furintiture.model.request.AddAddressRequest
|
||||||
|
import com.example.furintiture.model.request.AddToCartRequest
|
||||||
|
import com.example.furintiture.model.request.AddToWishlistRequest
|
||||||
|
import com.example.furintiture.model.request.ChangeCountFromCartRequest
|
||||||
|
import com.example.furintiture.model.request.CreateOrderRequest
|
||||||
|
import com.example.furintiture.model.request.LoginRequest
|
||||||
|
import com.example.furintiture.model.request.RegisterRequest
|
||||||
|
import com.example.furintiture.model.request.RemoveFromCartRequest
|
||||||
|
import com.example.furintiture.model.request.RemoveFromWishlistRequest
|
||||||
|
import com.example.furintiture.model.response.AllShopCategoryResponse
|
||||||
|
import com.example.furintiture.model.response.CartResponse
|
||||||
|
import com.example.furintiture.model.response.FurnitureCategoryResponse
|
||||||
|
import com.example.furintiture.model.response.FurnitureResponse
|
||||||
|
import com.example.furintiture.model.response.OrderResponse
|
||||||
|
import com.example.furintiture.model.response.SaleResponse
|
||||||
|
import com.example.furintiture.model.response.UserResponse
|
||||||
|
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 java.util.UUID
|
||||||
|
import kotlin.uuid.Uuid
|
||||||
|
|
||||||
|
interface FurnitureApi {
|
||||||
|
@POST("/auth/login")
|
||||||
|
suspend fun auth(@Body loginRequest: LoginRequest): UserResponse
|
||||||
|
|
||||||
|
@POST("/auth/register")
|
||||||
|
suspend fun register(@Body registerRequest: RegisterRequest): UserResponse
|
||||||
|
|
||||||
|
@GET("/user/{uuid}/wishlist")
|
||||||
|
suspend fun getWithListByUuid(@Path("uuid") uuid: UUID): List<FurnitureResponse>
|
||||||
|
|
||||||
|
@GET("/user/{uuid}/cart")
|
||||||
|
suspend fun getCartByUuid(@Path("uuid") uuid: UUID): List<CartResponse>
|
||||||
|
|
||||||
|
@GET("/user/{uuid}/order")
|
||||||
|
suspend fun getOrdersByUuid(@Path("uuid") uuid: UUID): List<OrderResponse>
|
||||||
|
|
||||||
|
@GET("/user/{uuid}/profile")
|
||||||
|
suspend fun getProfileByUuid(@Path("uuid") uuid: UUID): UserResponse
|
||||||
|
|
||||||
|
@POST("/user/{uuid}/address")
|
||||||
|
suspend fun addAddressByUuid(@Path("uuid") uuid: UUID, @Body addAddressRequest: AddAddressRequest)
|
||||||
|
|
||||||
|
@POST("/wishlist")
|
||||||
|
suspend fun addToWishlist(addToWishlistRequest: AddToWishlistRequest)
|
||||||
|
|
||||||
|
@DELETE("/wishlist")
|
||||||
|
suspend fun addToWishlist(removeFromWishlistRequest: RemoveFromWishlistRequest)
|
||||||
|
|
||||||
|
@GET("/shop_category")
|
||||||
|
suspend fun getAllShopCategory(): List<AllShopCategoryResponse>
|
||||||
|
|
||||||
|
@GET("/shop_category/{shopCategoryId}/furniture")
|
||||||
|
suspend fun getFurnitureByShopCategory(@Path("shopCategoryId") shopCategoryId: Long): List<FurnitureResponse>
|
||||||
|
|
||||||
|
@GET("/sale")
|
||||||
|
suspend fun getAllSales(): List<SaleResponse>
|
||||||
|
|
||||||
|
@POST("/order")
|
||||||
|
suspend fun createOrder(@Body createOrderRequest: CreateOrderRequest)
|
||||||
|
|
||||||
|
@GET("/furniture")
|
||||||
|
suspend fun getAllFurniture(): List<FurnitureResponse>
|
||||||
|
|
||||||
|
@GET("/furniture/{furnitureId}")
|
||||||
|
suspend fun getFurnitureById(@Path("furnitureId") furnitureId: Long): FurnitureResponse
|
||||||
|
|
||||||
|
@GET("/category")
|
||||||
|
suspend fun getAllFurnitureCategory(): List<FurnitureCategoryResponse>
|
||||||
|
|
||||||
|
@GET("/category/{id}/furniture")
|
||||||
|
suspend fun getFurnitureByCategory(@Path("id") categoryId: Long): List<FurnitureResponse>
|
||||||
|
|
||||||
|
@POST("/cart")
|
||||||
|
suspend fun addToCartByUuid(@Body addToCartRequest: AddToCartRequest)
|
||||||
|
|
||||||
|
@PUT("/cart")
|
||||||
|
suspend fun changeCountFromCartByUuid(@Body changeCountFromCartRequest: ChangeCountFromCartRequest)
|
||||||
|
|
||||||
|
@DELETE("/cart")
|
||||||
|
suspend fun removeFromCartByUuid(@Body removeFromCartRequest: RemoveFromCartRequest)
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AddAddressRequest(
|
||||||
|
var address: String = "",
|
||||||
|
var entrance: Int? = null,
|
||||||
|
var floor: Int? = null,
|
||||||
|
var apartment: Int? = null,
|
||||||
|
var comment: String? = null
|
||||||
|
)
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
@Serializable
|
||||||
|
data class AddToCartRequest(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val uuid: UUID,
|
||||||
|
val furnitureId: Long,
|
||||||
|
val count: Int,
|
||||||
|
)
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
@Serializable
|
||||||
|
data class AddToWishlistRequest(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val uuid: UUID,
|
||||||
|
val furnitureId: Long
|
||||||
|
)
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
@Serializable
|
||||||
|
data class ChangeCountFromCartRequest(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val uuid: UUID,
|
||||||
|
val furnitureId: Long,
|
||||||
|
val count: Int,
|
||||||
|
)
|
@ -0,0 +1,12 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.BigDecimalSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.math.BigDecimal
|
||||||
|
@Serializable
|
||||||
|
data class CreateOrderItemRequest(
|
||||||
|
val furnitureId : Long,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val furniturePrice: BigDecimal,
|
||||||
|
val count: Int
|
||||||
|
)
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.BigDecimalSerializer
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CreateOrderRequest(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val userUuid : UUID,
|
||||||
|
val addressId: Long,
|
||||||
|
val orderStatus : Int,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val orderTotalSum : BigDecimal,
|
||||||
|
val orderSet: List<CreateOrderItemRequest>
|
||||||
|
)
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class LoginRequest(val email: String, val password: String)
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RegisterRequest(
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
val email: String,
|
||||||
|
val password: String
|
||||||
|
)
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
@Serializable
|
||||||
|
data class RemoveFromCartRequest(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val uuid: UUID,
|
||||||
|
val furnitureId: Long,
|
||||||
|
)
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.furintiture.model.request
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
@Serializable
|
||||||
|
data class RemoveFromWishlistRequest(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val uuid: UUID,
|
||||||
|
val furnitureId: Long
|
||||||
|
)
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AddressResponse(
|
||||||
|
val addressId: Int,
|
||||||
|
val entrance: Int,
|
||||||
|
val floor: Int,
|
||||||
|
val apartment: Int,
|
||||||
|
val comment: String?,
|
||||||
|
val address: String
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class AllShopCategoryResponse(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
var furnitureList: List<FurnitureResponse>
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class CartResponse(
|
||||||
|
val count: Int,
|
||||||
|
val furnitureResponse: FurnitureResponse
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class FurnitureCategoryResponse(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
)
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.BigDecimalSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class FurnitureResponse(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val url: String,
|
||||||
|
val category: FurnitureCategoryResponse,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val price: BigDecimal,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val sale: BigDecimal,
|
||||||
|
var shopCategories: List<ShopCategoryResponse>,
|
||||||
|
)
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.BigDecimalSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.math.BigDecimal
|
||||||
|
@Serializable
|
||||||
|
data class OrderItemResponse(
|
||||||
|
val furnitureId : Long,
|
||||||
|
val orderId : Long,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val furniturePrice: BigDecimal,
|
||||||
|
val count: Int,
|
||||||
|
val furnitureResponse: FurnitureResponse
|
||||||
|
)
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.BigDecimalSerializer
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.*
|
||||||
|
@Serializable
|
||||||
|
data class OrderResponse(
|
||||||
|
val id : Long,
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val userUuid : UUID,
|
||||||
|
val addressId: Long,
|
||||||
|
val addressResponse: AddressResponse,
|
||||||
|
val dateTime: LocalDateTime,
|
||||||
|
val orderStatus : OrderStatusResponse,
|
||||||
|
@Serializable(with = BigDecimalSerializer::class)
|
||||||
|
val orderTotalSum : BigDecimal,
|
||||||
|
var orderSet: List<OrderItemResponse>
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OrderStatusResponse(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SaleResponse(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val url: String,
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ShopCategoryResponse(
|
||||||
|
val id: Long,
|
||||||
|
val name: String,
|
||||||
|
)
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.example.furintiture.model.response
|
||||||
|
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.*
|
||||||
|
@Serializable
|
||||||
|
data class UserResponse(
|
||||||
|
@Serializable(with = UuidSerializer::class)
|
||||||
|
val userUuid: UUID,
|
||||||
|
val email: String,
|
||||||
|
val firstName: String,
|
||||||
|
val lastName: String,
|
||||||
|
val imageUrl: String? = null,
|
||||||
|
val address: AddressResponse? = null,
|
||||||
|
)
|
@ -0,0 +1,173 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
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.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
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.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_40
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_70
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_90
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BasieFurnitureTextField(
|
||||||
|
value: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
textColor: Color,
|
||||||
|
label: @Composable () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
leadingIcon: @Composable (() -> Unit)? = null,
|
||||||
|
visualTransformation: VisualTransformation = VisualTransformation.None,
|
||||||
|
trailingIcon: @Composable (() -> Unit)? = null,
|
||||||
|
){
|
||||||
|
BasicTextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = Color.White,
|
||||||
|
shape = RoundedCornerShape(18.dp))
|
||||||
|
.border(
|
||||||
|
width = 1.dp, color = Neutral_40,
|
||||||
|
shape = RoundedCornerShape(18.dp)
|
||||||
|
),
|
||||||
|
singleLine = true,
|
||||||
|
textStyle = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
visualTransformation = visualTransformation,
|
||||||
|
){ innerTextField ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 8.dp, horizontal = 18.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
if (leadingIcon != null) {
|
||||||
|
leadingIcon()
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
Column {
|
||||||
|
label()
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
innerTextField()
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
trailingIcon?.let {
|
||||||
|
it()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureTextField(
|
||||||
|
value: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
textLabel: String,
|
||||||
|
leadingResourceId: Int,
|
||||||
|
tintColor: Color = Neutral_70,
|
||||||
|
textColor: Color = Neutral_90,
|
||||||
|
){
|
||||||
|
BasieFurnitureTextField(
|
||||||
|
value = value,
|
||||||
|
textColor = textColor,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
textLabel,
|
||||||
|
color = textColor,
|
||||||
|
style = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingIcon = { Icon(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
painter = painterResource(leadingResourceId),
|
||||||
|
tint = tintColor,
|
||||||
|
contentDescription = null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurniturePasswordTextField(
|
||||||
|
value: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
tintColor: Color = Neutral_70,
|
||||||
|
textColor: Color = Neutral_90,
|
||||||
|
){
|
||||||
|
val passwordIsVisible = remember { mutableStateOf(false) }
|
||||||
|
BasieFurnitureTextField(
|
||||||
|
value = value,
|
||||||
|
textColor = textColor,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
label = {
|
||||||
|
Text("Password",
|
||||||
|
color = textColor,
|
||||||
|
style = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
))
|
||||||
|
},
|
||||||
|
visualTransformation = if(passwordIsVisible.value) VisualTransformation.None else PasswordVisualTransformation('*'),
|
||||||
|
leadingIcon = { Icon(
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
painter = painterResource(R.drawable.lock_icon),
|
||||||
|
tint = tintColor,
|
||||||
|
contentDescription = null)
|
||||||
|
},
|
||||||
|
trailingIcon = { Icon(
|
||||||
|
modifier =
|
||||||
|
Modifier.size(20.dp).clickable {
|
||||||
|
passwordIsVisible.value = !passwordIsVisible.value
|
||||||
|
},
|
||||||
|
tint = tintColor,
|
||||||
|
painter = painterResource(R.drawable.fi_sr_eye_1),
|
||||||
|
contentDescription = null)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Home
|
||||||
|
import androidx.compose.material.icons.filled.Menu
|
||||||
|
import androidx.compose.material.icons.filled.ShoppingCart
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.NavigationBar
|
||||||
|
import androidx.compose.material3.NavigationBarItem
|
||||||
|
import androidx.compose.material3.NavigationBarItemColors
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_40
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
|
||||||
|
public enum class Destination(
|
||||||
|
val route: BottomNavigation,
|
||||||
|
val label: String,
|
||||||
|
val icon: ImageVector,
|
||||||
|
val contentDescription: String
|
||||||
|
) {
|
||||||
|
HOME(BottomNavigation.Home, "Основная", Icons.Default.Home, "Songs"),
|
||||||
|
CART(BottomNavigation.Cart, "Корзина", Icons.Default.ShoppingCart, "Album"),
|
||||||
|
ORDER(BottomNavigation.Order, "Заказы", Icons.Default.Menu, "Playlist")
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class BottomNavigation{
|
||||||
|
data object Home: BottomNavigation()
|
||||||
|
data object Cart: BottomNavigation()
|
||||||
|
data object Order: BottomNavigation()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureBottomNavigation(
|
||||||
|
onChangeBottomItem: (Destination) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
val startDestination = Destination.HOME
|
||||||
|
var selectedDestination by rememberSaveable { mutableIntStateOf(startDestination.ordinal) }
|
||||||
|
NavigationBar(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.wrapContentHeight(),
|
||||||
|
containerColor = Color.White
|
||||||
|
) {
|
||||||
|
Destination.entries.forEachIndexed { index, destination ->
|
||||||
|
NavigationBarItem(
|
||||||
|
selected = selectedDestination == index,
|
||||||
|
onClick = {
|
||||||
|
selectedDestination = index
|
||||||
|
onChangeBottomItem(destination)
|
||||||
|
},
|
||||||
|
icon = {
|
||||||
|
Icon(
|
||||||
|
destination.icon,
|
||||||
|
contentDescription = destination.contentDescription
|
||||||
|
)
|
||||||
|
},
|
||||||
|
label = { Text(destination.label) },
|
||||||
|
colors = NavigationBarItemColors(
|
||||||
|
selectedIconColor = Primary,
|
||||||
|
selectedTextColor = Primary,
|
||||||
|
selectedIndicatorColor = Color.Transparent,
|
||||||
|
unselectedIconColor = Neutral_40,
|
||||||
|
unselectedTextColor = Neutral_40,
|
||||||
|
disabledIconColor = Neutral_40,
|
||||||
|
disabledTextColor = Neutral_40
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import android.content.res.Resources.Theme
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.ui.theme.FurintitureTheme
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureButton(
|
||||||
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
onClick : () -> Unit,
|
||||||
|
){
|
||||||
|
Button(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(43.dp)
|
||||||
|
.clip(shape = RoundedCornerShape(8.dp))
|
||||||
|
,
|
||||||
|
colors = ButtonDefaults.buttonColors().copy(
|
||||||
|
containerColor = Primary,
|
||||||
|
contentColor = Color.White,
|
||||||
|
disabledContainerColor = Primary,
|
||||||
|
disabledContentColor = Color.White
|
||||||
|
),
|
||||||
|
onClick = onClick
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = TextStyle(
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun FurniturePreview(){
|
||||||
|
FurnitureButton(
|
||||||
|
"Войти"
|
||||||
|
){
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
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.layout.width
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.model.response.FurnitureCategoryResponse
|
||||||
|
import com.example.furintiture.model.response.FurnitureResponse
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_10
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureCard(
|
||||||
|
furnitureResponse: FurnitureResponse,
|
||||||
|
onFurnitureClick: (FurnitureResponse) -> Unit
|
||||||
|
){
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentHeight()
|
||||||
|
.width(150.dp)
|
||||||
|
.background(color = Neutral_10, shape = RoundedCornerShape(14.dp))
|
||||||
|
.padding(16.dp)
|
||||||
|
.clickable {
|
||||||
|
onFurnitureClick(furnitureResponse)
|
||||||
|
},
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.wrapContentSize()
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(120.dp, 120.dp)
|
||||||
|
,
|
||||||
|
model = ImageRequest
|
||||||
|
.Builder(LocalContext.current)
|
||||||
|
.data(furnitureResponse.url)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
|
placeholder = painterResource(R.drawable.images),
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${furnitureResponse.sale.multiply(BigDecimal(100)).toInt()}% OFF",
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomStart)
|
||||||
|
.background(
|
||||||
|
color = Color.Red,
|
||||||
|
shape = RoundedCornerShape(10.dp)
|
||||||
|
)
|
||||||
|
.padding(
|
||||||
|
vertical = 4.dp,
|
||||||
|
horizontal = 6.dp
|
||||||
|
),
|
||||||
|
style = TextStyle(
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 10.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.wrapContentHeight()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = furnitureResponse.name,
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 14.sp
|
||||||
|
),
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${furnitureResponse.price} ₽",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview
|
||||||
|
fun FurnitureCardPreview(){
|
||||||
|
val furnitureResponse = FurnitureResponse(
|
||||||
|
id = 1,
|
||||||
|
name = "Стул",
|
||||||
|
price = BigDecimal("1000.5"),
|
||||||
|
description = "!@3",
|
||||||
|
sale = BigDecimal("0.40"),
|
||||||
|
url = "https://laurendanger.com/wp-content/uploads/sites/33/2024/10/IMG_3826.jpeg",
|
||||||
|
category = FurnitureCategoryResponse(
|
||||||
|
id = 1,
|
||||||
|
name = "!@3"
|
||||||
|
),
|
||||||
|
shopCategories = emptyList()
|
||||||
|
)
|
||||||
|
FurnitureCard(furnitureResponse){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
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.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.model.response.CartResponse
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_10
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureCartCard(cartResponse: CartResponse) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(color = Neutral_10, shape = RoundedCornerShape(16.dp))
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(100.dp, 100.dp)
|
||||||
|
,
|
||||||
|
model = ImageRequest
|
||||||
|
.Builder(LocalContext.current)
|
||||||
|
.data(cartResponse.furnitureResponse.url)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
|
placeholder = painterResource(R.drawable.images),
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(16.dp))
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.fillMaxHeight()
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = cartResponse.furnitureResponse.name,
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 24.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${cartResponse.furnitureResponse.price} ₽",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 36.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "${cartResponse.furnitureResponse.sale.multiply(BigDecimal(100)).toInt()}% OFF",
|
||||||
|
modifier = Modifier
|
||||||
|
.background(
|
||||||
|
color = Color.Red,
|
||||||
|
shape = RoundedCornerShape(10.dp)
|
||||||
|
)
|
||||||
|
.padding(
|
||||||
|
vertical = 4.dp,
|
||||||
|
horizontal = 6.dp
|
||||||
|
),
|
||||||
|
style = TextStyle(
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Количество: ${cartResponse.count}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 24.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.capitalize
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.intl.Locale
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.model.response.FurnitureResponse
|
||||||
|
import com.example.furintiture.model.response.ShopCategoryResponse
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureHorizontalList(
|
||||||
|
categoryName: String, categoryFurnitureList: List<FurnitureResponse>,
|
||||||
|
onAllFurnitureClick: () -> Unit,
|
||||||
|
onFurnitureClick: (FurnitureResponse) -> Unit,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.height(300.dp).fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier.height(30.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
text = categoryName.replaceFirstChar { it.titlecaseChar() },
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
onAllFurnitureClick()
|
||||||
|
},
|
||||||
|
text = "Больше товаров",
|
||||||
|
style = TextStyle(
|
||||||
|
color = Primary,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(categoryFurnitureList.size){ item ->
|
||||||
|
FurnitureCard(categoryFurnitureList[item]){ furniture ->
|
||||||
|
onFurnitureClick(furniture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
package com.example.furintiture.ui.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.model.response.OrderResponse
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
import kotlinx.datetime.LocalDateTime
|
||||||
|
import kotlinx.datetime.format
|
||||||
|
import kotlinx.datetime.format.DateTimeComponents
|
||||||
|
import kotlinx.datetime.format.DateTimeFormat
|
||||||
|
import kotlinx.datetime.format.DateTimeFormatBuilder
|
||||||
|
import kotlinx.datetime.format.byUnicodePattern
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureOrderCard(orderResponse: OrderResponse) {
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier =
|
||||||
|
Modifier.height(30.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
text = "Номер заказа: ${orderResponse.id}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = FontWeight.Bold)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Статус заказа: ${orderResponse.orderStatus.name}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = Primary,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val format = LocalDateTime.Format {
|
||||||
|
byUnicodePattern("uuuu-MM-dd HH:mm")
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = "Дата заказа: ${format.format(orderResponse.dateTime)}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Итого: ${orderResponse.orderTotalSum}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "По адресу: ${orderResponse.addressResponse.address}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
LazyRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(orderResponse.orderSet){ orderSet ->
|
||||||
|
FurnitureCard(orderSet.furnitureResponse) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,132 @@
|
|||||||
|
package com.example.furintiture.ui.screen
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.toRoute
|
||||||
|
import com.example.furintiture.configure.UuidSerializer
|
||||||
|
import com.example.furintiture.data.DataStoreSettings
|
||||||
|
import com.example.furintiture.ui.screen.cart.CartScreen
|
||||||
|
import com.example.furintiture.ui.screen.detail.DetailScreen
|
||||||
|
import com.example.furintiture.ui.screen.furniture.FurnitureScreen
|
||||||
|
import com.example.furintiture.ui.screen.login.LoginScreen
|
||||||
|
import com.example.furintiture.ui.screen.main.MainScreen
|
||||||
|
import com.example.furintiture.ui.screen.order.OrderScreen
|
||||||
|
import com.example.furintiture.ui.screen.registration.RegistrationScreen
|
||||||
|
import com.example.furintiture.ui.screen.splashscreen.SplashScreen
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureGlobalNavigation(
|
||||||
|
dataStoreSettings: DataStoreSettings,
|
||||||
|
navHostController: NavHostController,
|
||||||
|
onBottomBarVisibleEvent: (Boolean) -> Unit
|
||||||
|
) {
|
||||||
|
NavHost(
|
||||||
|
modifier = Modifier,
|
||||||
|
navController = navHostController,
|
||||||
|
startDestination = Splash
|
||||||
|
){
|
||||||
|
composable<Splash> {
|
||||||
|
SplashScreen()
|
||||||
|
onBottomBarVisibleEvent(false)
|
||||||
|
}
|
||||||
|
composable<Registration> {
|
||||||
|
RegistrationScreen(dataStoreSettings){
|
||||||
|
navHostController.navigate(Login)
|
||||||
|
}
|
||||||
|
onBottomBarVisibleEvent(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
composable<Login> {
|
||||||
|
LoginScreen(dataStoreSettings){
|
||||||
|
navHostController.navigate(Registration)
|
||||||
|
}
|
||||||
|
onBottomBarVisibleEvent(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
composable<Detail>{ navBackStackEntry ->
|
||||||
|
val detail: Detail = navBackStackEntry.toRoute()
|
||||||
|
DetailScreen(detail){
|
||||||
|
navHostController.navigateUp()
|
||||||
|
}
|
||||||
|
onBottomBarVisibleEvent(false)
|
||||||
|
|
||||||
|
}
|
||||||
|
composable<Furniture>{navBackStackEntry ->
|
||||||
|
val furniture: Furniture = navBackStackEntry.toRoute()
|
||||||
|
|
||||||
|
FurnitureScreen(uuid = furniture.uuid, onClosePressed = {
|
||||||
|
navHostController.navigateUp()
|
||||||
|
}) {
|
||||||
|
navHostController.navigate(it)
|
||||||
|
}
|
||||||
|
onBottomBarVisibleEvent(false)
|
||||||
|
}
|
||||||
|
composable<Cart>{ navBackStackEntry ->
|
||||||
|
val cart: Cart = navBackStackEntry.toRoute()
|
||||||
|
CartScreen(cart.uuid)
|
||||||
|
}
|
||||||
|
composable<Order>{ navBackStackEntry ->
|
||||||
|
val order: Order = navBackStackEntry.toRoute()
|
||||||
|
OrderScreen(order.uuid)
|
||||||
|
}
|
||||||
|
composable<Main> { navBackStackEntry ->
|
||||||
|
val main: Main = navBackStackEntry.toRoute()
|
||||||
|
MainScreen(
|
||||||
|
main.uuid,
|
||||||
|
onNavigateToFurniture = { furniture ->
|
||||||
|
navHostController.navigate(furniture)
|
||||||
|
}
|
||||||
|
){ detail ->
|
||||||
|
navHostController.navigate(detail)
|
||||||
|
}
|
||||||
|
onBottomBarVisibleEvent(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Main(
|
||||||
|
val uuid: String
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Detail(
|
||||||
|
val furnitureId: Long,
|
||||||
|
val uuid: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Cart(
|
||||||
|
val uuid: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Order(
|
||||||
|
val uuid: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object Login
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Furniture(
|
||||||
|
val uuid: String
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object Registration
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object Splash
|
@ -0,0 +1,135 @@
|
|||||||
|
package com.example.furintiture.ui.screen.bottomsheet
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.model.request.AddAddressRequest
|
||||||
|
import com.example.furintiture.model.response.AddressResponse
|
||||||
|
import com.example.furintiture.ui.common.BasieFurnitureTextField
|
||||||
|
import com.example.furintiture.ui.common.FurnitureButton
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_90
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun FurnitureAddressBottomSheet(
|
||||||
|
addressResponse: AddressResponse?,
|
||||||
|
viewModel: FurnitureAddressBottomSheetViewModel,
|
||||||
|
createAddressCallback: (AddAddressRequest) -> Unit,
|
||||||
|
) {
|
||||||
|
val state = viewModel.furnitureAddressState.collectAsState()
|
||||||
|
LaunchedEffect(addressResponse) {
|
||||||
|
if (addressResponse != null){
|
||||||
|
viewModel.setAddress(address = addressResponse.address)
|
||||||
|
viewModel.setFloor(floor = addressResponse.floor.toString())
|
||||||
|
viewModel.setApartment(apartment = addressResponse.apartment.toString())
|
||||||
|
viewModel.setComment(comment = addressResponse.comment ?: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = {
|
||||||
|
createAddressCallback(state.value)
|
||||||
|
}
|
||||||
|
){
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
) {
|
||||||
|
BasieFurnitureTextField(
|
||||||
|
value = state.value.address,
|
||||||
|
onValueChange = {viewModel.setAddress(it)},
|
||||||
|
textColor = Neutral_90,
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
"Адрес",
|
||||||
|
color = Neutral_90,
|
||||||
|
style = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
){
|
||||||
|
BasieFurnitureTextField(
|
||||||
|
modifier = Modifier.weight(0.5f),
|
||||||
|
value = if(state.value.floor == null) "" else state.value.floor.toString(),
|
||||||
|
onValueChange = {viewModel.setFloor(it)},
|
||||||
|
textColor = Neutral_90,
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
"Этаж",
|
||||||
|
color = Neutral_90,
|
||||||
|
style = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
BasieFurnitureTextField(
|
||||||
|
modifier = Modifier.weight(0.5f),
|
||||||
|
value = if(state.value.apartment == null) "" else state.value.apartment.toString(),
|
||||||
|
onValueChange = {viewModel.setApartment(it)},
|
||||||
|
textColor = Neutral_90,
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
"Квартира",
|
||||||
|
color = Neutral_90,
|
||||||
|
style = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
BasieFurnitureTextField(
|
||||||
|
value = state.value.comment ?: "",
|
||||||
|
onValueChange = {viewModel.setComment(it)},
|
||||||
|
textColor = Neutral_90,
|
||||||
|
label = {
|
||||||
|
Text(
|
||||||
|
"Комментарий курьеру",
|
||||||
|
color = Neutral_90,
|
||||||
|
style = TextStyle(
|
||||||
|
color = Neutral_90,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
val text = if(addressResponse != null) "Изменить" else "Добавить"
|
||||||
|
FurnitureButton(
|
||||||
|
text = text
|
||||||
|
) {
|
||||||
|
createAddressCallback(state.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.example.furintiture.ui.screen.bottomsheet
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.example.furintiture.model.request.AddAddressRequest
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
|
class FurnitureAddressBottomSheetViewModel: ViewModel() {
|
||||||
|
private val _furnitureAddressState = MutableStateFlow(AddAddressRequest())
|
||||||
|
val furnitureAddressState = _furnitureAddressState.asStateFlow()
|
||||||
|
|
||||||
|
fun setAddress(address: String){
|
||||||
|
_furnitureAddressState.update {
|
||||||
|
it.copy(address = address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setFloor(floor: String){
|
||||||
|
_furnitureAddressState.update {
|
||||||
|
it.copy(floor = floor.toIntOrNull())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setApartment(apartment: String){
|
||||||
|
_furnitureAddressState.update {
|
||||||
|
it.copy(apartment = apartment.toIntOrNull())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setComment(comment: String){
|
||||||
|
_furnitureAddressState.update {
|
||||||
|
it.copy(comment = comment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,118 @@
|
|||||||
|
package com.example.furintiture.ui.screen.cart
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.lazy.LazyColumn
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.ui.common.FurnitureButton
|
||||||
|
import com.example.furintiture.ui.common.FurnitureCartCard
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CartScreen(
|
||||||
|
uuid: String
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel: CartScreenViewModel = viewModel { CartScreenViewModel(uuid) }
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
contentColor = Neutral_20
|
||||||
|
) { paddingValues ->
|
||||||
|
CartScreenContent(viewModel, paddingValues){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CartScreenContent(
|
||||||
|
viewModel: CartScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
onErrorCallback : (String) -> Unit
|
||||||
|
){
|
||||||
|
val state = viewModel.cartScreenState.collectAsState()
|
||||||
|
val totalSum = remember { derivedStateOf {
|
||||||
|
state.value.cartResponse.sumOf { cart ->
|
||||||
|
BigDecimal(cart.count) * cart.furnitureResponse.price
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let(onErrorCallback)
|
||||||
|
state.value.error = null
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = 100.dp)
|
||||||
|
|
||||||
|
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(state.value.cartResponse.size){ item ->
|
||||||
|
FurnitureCartCard(state.value.cartResponse[item])
|
||||||
|
}
|
||||||
|
if(totalSum.value > BigDecimal(0)){
|
||||||
|
item {
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
"Итого: ${totalSum.value}",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 36.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
FurnitureButton(
|
||||||
|
modifier = Modifier.height(60.dp),
|
||||||
|
text = "Оформить заказ"
|
||||||
|
) {
|
||||||
|
viewModel.createOrder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.example.furintiture.ui.screen.cart
|
||||||
|
|
||||||
|
import com.example.furintiture.model.response.CartResponse
|
||||||
|
|
||||||
|
data class CartScreenState(
|
||||||
|
var cartResponse: List<CartResponse> = emptyList(),
|
||||||
|
var error: String? = null
|
||||||
|
)
|
@ -0,0 +1,80 @@
|
|||||||
|
package com.example.furintiture.ui.screen.cart
|
||||||
|
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import com.example.furintiture.model.request.CreateOrderItemRequest
|
||||||
|
import com.example.furintiture.model.request.CreateOrderRequest
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.math.BigDecimal
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class CartScreenViewModel(private val uuid: String): ViewModel() {
|
||||||
|
private val _cartScreenState = MutableStateFlow(CartScreenState())
|
||||||
|
val cartScreenState = _cartScreenState.asStateFlow()
|
||||||
|
val totalSum = derivedStateOf { cartScreenState.value.cartResponse.sumOf { cart ->
|
||||||
|
BigDecimal(cart.count) * cart.furnitureResponse.price }
|
||||||
|
}
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_cartScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun createOrder(){
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val addresId = RetrofitClient.api.getProfileByUuid(UUID.fromString(uuid)).address?.addressId
|
||||||
|
if (addresId == null){
|
||||||
|
_cartScreenState.update {
|
||||||
|
it.copy(error = "Нельзя сделать заказ без адрес")
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
val createOrderRequest = CreateOrderRequest(
|
||||||
|
userUuid = UUID.fromString(uuid),
|
||||||
|
addressId = addresId.toLong(),
|
||||||
|
orderStatus = 1,
|
||||||
|
orderTotalSum = cartScreenState.value.cartResponse.sumOf { cart ->
|
||||||
|
BigDecimal(cart.count) * cart.furnitureResponse.price
|
||||||
|
},
|
||||||
|
orderSet = cartScreenState.value.cartResponse.map {
|
||||||
|
CreateOrderItemRequest(
|
||||||
|
furnitureId = it.furnitureResponse.id,
|
||||||
|
furniturePrice = it.furnitureResponse.price,
|
||||||
|
count = it.count
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
RetrofitClient.api.createOrder(createOrderRequest)
|
||||||
|
_cartScreenState.update {
|
||||||
|
it.copy(cartResponse = emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_cartScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun init(){
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
val cart = RetrofitClient.api.getCartByUuid(UUID.fromString(uuid))
|
||||||
|
_cartScreenState.update {
|
||||||
|
it.copy(cartResponse = cart)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,216 @@
|
|||||||
|
package com.example.furintiture.ui.screen.detail
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
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.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.ui.common.FurnitureButton
|
||||||
|
import com.example.furintiture.ui.screen.Detail
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_10
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_40
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun DetailScreen(
|
||||||
|
detail: Detail,
|
||||||
|
onClosePressed: () -> Unit
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel = viewModel {
|
||||||
|
DetailScreenViewModel(detail.furnitureId, detail.uuid)
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = {},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton (
|
||||||
|
onClick = {
|
||||||
|
onClosePressed()
|
||||||
|
}
|
||||||
|
){
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Close,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
bottomBar = {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(color = Neutral_20, shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
|
||||||
|
.padding(20.dp)
|
||||||
|
) {
|
||||||
|
FurnitureButton(
|
||||||
|
modifier = Modifier.height(60.dp),
|
||||||
|
text = "Добавить в корзину",
|
||||||
|
) {
|
||||||
|
viewModel.addToCart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentColor = Neutral_20
|
||||||
|
) { paddingValues ->
|
||||||
|
DetailScreenContent(viewModel, paddingValues){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(message = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DetailScreenContent(
|
||||||
|
detailScreenViewModel: DetailScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
errorCallback: (String) -> Unit
|
||||||
|
|
||||||
|
){
|
||||||
|
val state = detailScreenViewModel.detailScreenState.collectAsState()
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let(errorCallback)
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(color = Neutral_20)
|
||||||
|
,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(350.dp, 350.dp)
|
||||||
|
.align(Alignment.CenterHorizontally)
|
||||||
|
,
|
||||||
|
model = ImageRequest
|
||||||
|
.Builder(LocalContext.current)
|
||||||
|
.data(state.value.furnitureResponse.url)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
|
placeholder = painterResource(R.drawable.images),
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(Color.White, shape = RoundedCornerShape(
|
||||||
|
topStart = 16.dp,
|
||||||
|
topEnd = 16.dp)
|
||||||
|
)
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 20.dp, vertical = 10.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = state.value.furnitureResponse.name,
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 28.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "${state.value.furnitureResponse.price} ₽",
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 40.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "${state.value.furnitureResponse.sale.multiply(BigDecimal(100)).toInt()}% OFF",
|
||||||
|
modifier = Modifier
|
||||||
|
.background(
|
||||||
|
color = Color.Red,
|
||||||
|
shape = RoundedCornerShape(10.dp)
|
||||||
|
)
|
||||||
|
.padding(
|
||||||
|
vertical = 4.dp,
|
||||||
|
horizontal = 6.dp
|
||||||
|
),
|
||||||
|
style = TextStyle(
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 20.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = state.value.furnitureResponse.description,
|
||||||
|
style = TextStyle(
|
||||||
|
color = com.example.furintiture.ui.theme.Text,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontSize = 18.sp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.example.furintiture.ui.screen.detail
|
||||||
|
|
||||||
|
import com.example.furintiture.model.response.FurnitureCategoryResponse
|
||||||
|
import com.example.furintiture.model.response.FurnitureResponse
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
data class DetailScreenState(
|
||||||
|
val furnitureResponse: FurnitureResponse = FurnitureResponse(
|
||||||
|
id = 1,
|
||||||
|
name = "Стул",
|
||||||
|
price = BigDecimal("1000.5"),
|
||||||
|
description = "Этот стул выполнен из натурального дуба, что придает ему прочность и долговечность. Спинка украшена резными узорами, напоминающими старинные европейские интерьеры. Сиденье обито мягкой тканью с антикварным орнаментом, обеспечивая комфорт во время сидения. Ножки слегка изогнуты, что добавляет изделию элегантности. Такой стул идеально впишется в гостиную или столовую в классическом стиле, создавая атмосферу уюта и респектабельности.",
|
||||||
|
sale = BigDecimal("0.40"),
|
||||||
|
url = "https://laurendanger.com/wp-content/uploads/sites/33/2024/10/IMG_3826.jpeg",
|
||||||
|
category = FurnitureCategoryResponse(
|
||||||
|
id = 1,
|
||||||
|
name = "!@3"
|
||||||
|
),
|
||||||
|
shopCategories = emptyList()
|
||||||
|
),
|
||||||
|
var error: String? = null
|
||||||
|
)
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.example.furintiture.ui.screen.detail
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import com.example.furintiture.model.request.AddToCartRequest
|
||||||
|
import com.example.furintiture.model.request.ChangeCountFromCartRequest
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class DetailScreenViewModel(private val furnitureId: Long, private val uuidUser: String): ViewModel() {
|
||||||
|
private val _detailScreenState = MutableStateFlow(DetailScreenState())
|
||||||
|
val detailScreenState = _detailScreenState.asStateFlow()
|
||||||
|
init {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToCart(){
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
RetrofitClient.api.addToCartByUuid(
|
||||||
|
AddToCartRequest(
|
||||||
|
uuid = UUID.fromString(uuidUser),
|
||||||
|
furnitureId = furnitureId,
|
||||||
|
count = 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_detailScreenState.update {
|
||||||
|
it.copy(error = "Успешно добавлено")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_detailScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(){
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = RetrofitClient.api.getFurnitureById(furnitureId)
|
||||||
|
_detailScreenState.update {
|
||||||
|
it.copy(furnitureResponse = result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
println(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package com.example.furintiture.ui.screen.furniture
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.example.furintiture.ui.common.FurnitureCard
|
||||||
|
import com.example.furintiture.ui.screen.Detail
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun FurnitureScreen(
|
||||||
|
uuid: String,
|
||||||
|
onClosePressed: () -> Unit,
|
||||||
|
onNavigateToDetail: (Detail) -> Unit
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel = viewModel {
|
||||||
|
FurnitureScreenViewModel()
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = {},
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton (
|
||||||
|
onClick = {
|
||||||
|
onClosePressed()
|
||||||
|
}
|
||||||
|
){
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Close,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
) { paddingValues ->
|
||||||
|
FurnitureContent(uuid, viewModel, paddingValues, onNavigateToDetail){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(message = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurnitureContent(
|
||||||
|
uuid: String,
|
||||||
|
furnitureScreenViewModel: FurnitureScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
onNavigateToDetail: (Detail) -> Unit,
|
||||||
|
onErrorCallback: (String) -> Unit
|
||||||
|
){
|
||||||
|
val state = furnitureScreenViewModel.furnitureScreenState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let(onErrorCallback)
|
||||||
|
state.value.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
LazyVerticalGrid (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
columns = GridCells.Adaptive(minSize = 130.dp)
|
||||||
|
){
|
||||||
|
items(state.value.furnitureList){ it ->
|
||||||
|
FurnitureCard(it) {
|
||||||
|
onNavigateToDetail(Detail(uuid = uuid, furnitureId = it.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.example.furintiture.ui.screen.furniture
|
||||||
|
|
||||||
|
import com.example.furintiture.model.response.FurnitureResponse
|
||||||
|
|
||||||
|
data class FurnitureScreenState(
|
||||||
|
var furnitureList: List<FurnitureResponse> = emptyList(),
|
||||||
|
var error: String? = null
|
||||||
|
)
|
@ -0,0 +1,34 @@
|
|||||||
|
package com.example.furintiture.ui.screen.furniture
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class FurnitureScreenViewModel: ViewModel(){
|
||||||
|
private val _furnitureScreenState = MutableStateFlow(FurnitureScreenState())
|
||||||
|
val furnitureScreenState = _furnitureScreenState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_furnitureScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun init(){
|
||||||
|
viewModelScope.launch {
|
||||||
|
val result = RetrofitClient.api.getAllFurniture()
|
||||||
|
_furnitureScreenState.update {
|
||||||
|
it.copy(furnitureList = result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
package com.example.furintiture.ui.screen.login
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
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.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.data.DataStoreSettings
|
||||||
|
import com.example.furintiture.ui.common.FurnitureButton
|
||||||
|
import com.example.furintiture.ui.common.FurniturePasswordTextField
|
||||||
|
import com.example.furintiture.ui.common.FurnitureTextField
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_70
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoginScreen(
|
||||||
|
dataStoreSettings: DataStoreSettings,
|
||||||
|
onNavigateToRegistration: () -> Unit
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel = viewModel { LoginScreenViewModel(dataStoreSettings) }
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
contentColor = Neutral_20
|
||||||
|
) { paddingValues ->
|
||||||
|
LoginScreenContent(viewModel, paddingValues, onNavigateToRegistration){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(message = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoginScreenContent(
|
||||||
|
loginScreenViewModel: LoginScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
onNavigateToRegistration: () -> Unit,
|
||||||
|
errorCallback: (String) -> Unit
|
||||||
|
){
|
||||||
|
val state = loginScreenViewModel.loginScreenState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let {
|
||||||
|
errorCallback(it)
|
||||||
|
}
|
||||||
|
state.value.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
Text(
|
||||||
|
text = "C возвращением!",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope_extrabold))
|
||||||
|
),
|
||||||
|
color = Color.Black
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "Введите свой email, чтобы начать делать покупки и получать выгодные предложения уже сегодня!",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
color = Neutral_70
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
FurnitureTextField(
|
||||||
|
value = state.value.request.email,
|
||||||
|
onValueChange = { loginScreenViewModel.setEmail(it) },
|
||||||
|
textLabel = "Email",
|
||||||
|
leadingResourceId = R.drawable.mail_icon
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
FurniturePasswordTextField(
|
||||||
|
value = state.value.request.password,
|
||||||
|
onValueChange = { loginScreenViewModel.setPassword(it) }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
text = "Забыли пароль?",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
color = Primary
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
FurnitureButton(
|
||||||
|
text = "Войти"
|
||||||
|
) {
|
||||||
|
loginScreenViewModel.login()
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
onNavigateToRegistration()
|
||||||
|
},
|
||||||
|
text = "У вас нет учетной записи? Зарегистрируйтесь",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
color = Neutral_70
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.example.furintiture.ui.screen.login
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import com.example.furintiture.model.request.LoginRequest
|
||||||
|
|
||||||
|
data class LoginScreenState(
|
||||||
|
var request: LoginRequest = LoginRequest(email = "", password = ""),
|
||||||
|
var error: String? = null
|
||||||
|
)
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.example.furintiture.ui.screen.login
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.DataStoreSettings
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class LoginScreenViewModel(private val dataStoreSettings: DataStoreSettings): ViewModel() {
|
||||||
|
private val _loginScreenState = MutableStateFlow(LoginScreenState())
|
||||||
|
val loginScreenState = _loginScreenState.asStateFlow()
|
||||||
|
|
||||||
|
fun setPassword(password: String){
|
||||||
|
_loginScreenState.update {
|
||||||
|
it.copy(request = it.request.copy(password = password))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setEmail(email: String){
|
||||||
|
_loginScreenState.update {
|
||||||
|
it.copy(request = it.request.copy(email = email))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun login(){
|
||||||
|
if(!validate()) return
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = RetrofitClient.api.auth(loginScreenState.value.request)
|
||||||
|
dataStoreSettings.setUuid(result.userUuid)
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_loginScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun validate(): Boolean{
|
||||||
|
if (_loginScreenState.value.request.email.isEmpty()){
|
||||||
|
_loginScreenState.update {
|
||||||
|
it.copy(error = "Введите email")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (_loginScreenState.value.request.password.isEmpty()){
|
||||||
|
_loginScreenState.update {
|
||||||
|
it.copy(error = "Введите password")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,246 @@
|
|||||||
|
package com.example.furintiture.ui.screen.main
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
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.foundation.layout.wrapContentHeight
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.motionEventSpy
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import coil3.request.ImageRequest
|
||||||
|
import coil3.request.crossfade
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.ui.screen.bottomsheet.FurnitureAddressBottomSheet
|
||||||
|
import com.example.furintiture.ui.common.FurnitureHorizontalList
|
||||||
|
import com.example.furintiture.ui.screen.Detail
|
||||||
|
import com.example.furintiture.ui.screen.Furniture
|
||||||
|
import com.example.furintiture.ui.screen.bottomsheet.FurnitureAddressBottomSheetViewModel
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_40
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
import com.example.furintiture.ui.theme.gradientColors
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreen(
|
||||||
|
uuid: String,
|
||||||
|
onNavigateToFurniture: (Furniture) -> Unit,
|
||||||
|
onNavigateToDetail: (Detail) -> Unit,
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel: MainScreenViewModel = viewModel {
|
||||||
|
MainScreenViewModel(uuid)
|
||||||
|
}
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
contentColor = Neutral_20
|
||||||
|
) { paddingValues ->
|
||||||
|
MainScreenContent(uuid, viewModel, paddingValues, onNavigateToFurniture, onNavigateToDetail ){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(message = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreenContent(
|
||||||
|
userUuid: String,
|
||||||
|
mainScreenViewModel: MainScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
onNavigateToFurniture: (Furniture) -> Unit,
|
||||||
|
onNavigateToDetail: (Detail) -> Unit,
|
||||||
|
errorCallback: (String) -> Unit,
|
||||||
|
) {
|
||||||
|
val state = mainScreenViewModel.mainScreenState.collectAsState()
|
||||||
|
val pagerState = rememberPagerState(pageCount = {state.value.sales.size})
|
||||||
|
val showBottomSheet = remember { mutableStateOf(false) }
|
||||||
|
val viewModel: FurnitureAddressBottomSheetViewModel = viewModel()
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let {
|
||||||
|
errorCallback(it)
|
||||||
|
}
|
||||||
|
state.value.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(bottom = 100.dp,)
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
state.value.profile?.let {
|
||||||
|
val text = if(it.address != null) "Доставим по адресу: ${it.address.address}" else "Выберите адрес доставки"
|
||||||
|
Text(
|
||||||
|
modifier =
|
||||||
|
Modifier.padding(horizontal = 16.dp)
|
||||||
|
.clickable {
|
||||||
|
showBottomSheet.value = true
|
||||||
|
}
|
||||||
|
,
|
||||||
|
text = text,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
maxLines = 1,
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope_extrabold)),
|
||||||
|
color = com.example.furintiture.ui.theme.Text
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
}
|
||||||
|
if(showBottomSheet.value){
|
||||||
|
FurnitureAddressBottomSheet(
|
||||||
|
state.value.profile?.address, viewModel
|
||||||
|
) { requestAddress ->
|
||||||
|
mainScreenViewModel.addAddress(requestAddress)
|
||||||
|
showBottomSheet.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalPager(state = pagerState) { page ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(200.dp)
|
||||||
|
.padding(top = 20.dp)
|
||||||
|
,
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
,
|
||||||
|
model = ImageRequest
|
||||||
|
.Builder(LocalContext.current)
|
||||||
|
.data(state.value.sales[page].url)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
|
placeholder = painterResource(R.drawable.images),
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Brush.horizontalGradient(colorStops = gradientColors)))
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.Start,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(10.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = state.value.sales[page].name,
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope_extrabold))
|
||||||
|
),
|
||||||
|
color = Neutral_20
|
||||||
|
)
|
||||||
|
Button(
|
||||||
|
onClick = {},
|
||||||
|
colors = ButtonDefaults.buttonColors().copy(
|
||||||
|
contentColor = Color.White,
|
||||||
|
disabledContentColor = Color.White,
|
||||||
|
containerColor = Color.White,
|
||||||
|
disabledContainerColor = Color.White
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Купить сейчас",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope)),
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
),
|
||||||
|
color = Primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentHeight()
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
repeat(pagerState.pageCount) { iteration ->
|
||||||
|
val color = if (pagerState.currentPage == iteration) Primary else Neutral_40
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(2.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(color)
|
||||||
|
.size(12.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentPadding = PaddingValues(16.dp)
|
||||||
|
) {
|
||||||
|
items(state.value.shopCategories.size){ item ->
|
||||||
|
FurnitureHorizontalList(
|
||||||
|
categoryName = state.value.shopCategories[item].name,
|
||||||
|
categoryFurnitureList = state.value.shopCategories[item].furnitureList,
|
||||||
|
onAllFurnitureClick = { onNavigateToFurniture(Furniture(uuid = userUuid)) }
|
||||||
|
){
|
||||||
|
onNavigateToDetail(Detail(furnitureId = it.id, uuid = userUuid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.example.furintiture.ui.screen.main
|
||||||
|
|
||||||
|
import com.example.furintiture.model.response.AllShopCategoryResponse
|
||||||
|
import com.example.furintiture.model.response.FurnitureResponse
|
||||||
|
import com.example.furintiture.model.response.SaleResponse
|
||||||
|
import com.example.furintiture.model.response.ShopCategoryResponse
|
||||||
|
import com.example.furintiture.model.response.UserResponse
|
||||||
|
|
||||||
|
data class MainScreenState(
|
||||||
|
var sales: List<SaleResponse> = emptyList(),
|
||||||
|
var shopCategories: List<AllShopCategoryResponse> = emptyList(),
|
||||||
|
var addresses: List<String> = emptyList(),
|
||||||
|
var cartItems: List<FurnitureResponse> = emptyList(),
|
||||||
|
var error: String? = null,
|
||||||
|
val profile: UserResponse? = null
|
||||||
|
)
|
@ -0,0 +1,85 @@
|
|||||||
|
package com.example.furintiture.ui.screen.main
|
||||||
|
|
||||||
|
import androidx.compose.runtime.saveable.autoSaver
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import com.example.furintiture.model.request.AddAddressRequest
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class MainScreenViewModel(private val uuid: String): ViewModel() {
|
||||||
|
private val _mainScreenState = MutableStateFlow(MainScreenState())
|
||||||
|
val mainScreenState = _mainScreenState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
try {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init(){
|
||||||
|
viewModelScope.launch {
|
||||||
|
val sales = RetrofitClient.api.getAllSales()
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(sales = sales)
|
||||||
|
}
|
||||||
|
val allCategory = RetrofitClient.api.getAllShopCategory()
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(shopCategories = allCategory)
|
||||||
|
}
|
||||||
|
val profile = RetrofitClient.api.getProfileByUuid(UUID.fromString(uuid))
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(profile = profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAddress(addressRequest: AddAddressRequest){
|
||||||
|
viewModelScope.launch {
|
||||||
|
if (!validate(addressRequest)) return@launch
|
||||||
|
addressRequest.entrance = 10
|
||||||
|
try {
|
||||||
|
RetrofitClient.api.addAddressByUuid(uuid = UUID.fromString(uuid), addressRequest)
|
||||||
|
val profile = RetrofitClient.api.getProfileByUuid(UUID.fromString(uuid))
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(profile = profile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun validate(addressRequest: AddAddressRequest): Boolean{
|
||||||
|
if (addressRequest.address.isEmpty()){
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(error = "Адрес не может быть пустым")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(addressRequest.apartment == null){
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(error = "Укажите квартиру")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if(addressRequest.floor == null){
|
||||||
|
_mainScreenState.update {
|
||||||
|
it.copy(error = "Укажите этаж")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package com.example.furintiture.ui.screen.order
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.example.furintiture.ui.common.FurnitureOrderCard
|
||||||
|
import com.example.furintiture.ui.screen.cart.CartScreenContent
|
||||||
|
import com.example.furintiture.ui.screen.cart.CartScreenViewModel
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun OrderScreen(
|
||||||
|
uuid: String
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel: OrderScreenViewModel = viewModel { OrderScreenViewModel(uuid) }
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
contentColor = Neutral_20
|
||||||
|
) { paddingValues ->
|
||||||
|
OrderScreenContent(viewModel, paddingValues){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun OrderScreenContent(
|
||||||
|
viewModel: OrderScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
onErrorCallback : (String) -> Unit
|
||||||
|
) {
|
||||||
|
val state = viewModel.orderScreenState.collectAsState()
|
||||||
|
|
||||||
|
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let(onErrorCallback)
|
||||||
|
state.value.error = null
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(bottom = 100.dp)
|
||||||
|
|
||||||
|
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
items(state.value.orders){
|
||||||
|
FurnitureOrderCard(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.example.furintiture.ui.screen.order
|
||||||
|
|
||||||
|
import com.example.furintiture.model.response.OrderResponse
|
||||||
|
|
||||||
|
data class OrderScreenState(
|
||||||
|
var orders: List<OrderResponse> = emptyList(),
|
||||||
|
var error: String? = null
|
||||||
|
)
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.example.furintiture.ui.screen.order
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class OrderScreenViewModel(private val uuid: String): ViewModel() {
|
||||||
|
private val _orderScreenState = MutableStateFlow(OrderScreenState())
|
||||||
|
val orderScreenState = _orderScreenState.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
try{
|
||||||
|
viewModelScope.launch {
|
||||||
|
_orderScreenState.update {
|
||||||
|
it.copy(orders = RetrofitClient.api.getOrdersByUuid(UUID.fromString(uuid)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_orderScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,160 @@
|
|||||||
|
package com.example.furintiture.ui.screen.registration
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
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.material3.Scaffold
|
||||||
|
import androidx.compose.material3.SnackbarHost
|
||||||
|
import androidx.compose.material3.SnackbarHostState
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.data.DataStoreSettings
|
||||||
|
import com.example.furintiture.ui.common.FurnitureButton
|
||||||
|
import com.example.furintiture.ui.common.FurniturePasswordTextField
|
||||||
|
import com.example.furintiture.ui.common.FurnitureTextField
|
||||||
|
import com.example.furintiture.ui.screen.login.LoginScreenContent
|
||||||
|
import com.example.furintiture.ui.screen.login.LoginScreenViewModel
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_20
|
||||||
|
import com.example.furintiture.ui.theme.Neutral_70
|
||||||
|
import com.example.furintiture.ui.theme.Primary
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RegistrationScreen(
|
||||||
|
dataStoreSettings: DataStoreSettings,
|
||||||
|
onNavigateToLogin: () -> Unit
|
||||||
|
) {
|
||||||
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val viewModel: RegistrationScreenViewModel = viewModel { RegistrationScreenViewModel(dataStoreSettings) }
|
||||||
|
Scaffold(
|
||||||
|
snackbarHost = {
|
||||||
|
SnackbarHost(hostState = snackbarHostState)
|
||||||
|
},
|
||||||
|
contentColor = Neutral_20
|
||||||
|
) { paddingValues ->
|
||||||
|
RegisterScreenContent(viewModel, paddingValues, onNavigateToLogin){
|
||||||
|
coroutineScope.launch {
|
||||||
|
snackbarHostState.showSnackbar(message = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RegisterScreenContent(
|
||||||
|
registrationScreenViewModel: RegistrationScreenViewModel,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
onNavigateToLogin: () -> Unit,
|
||||||
|
errorCallback: (String) -> Unit
|
||||||
|
){
|
||||||
|
val state = registrationScreenViewModel.registrationScreenState.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(state.value.error) {
|
||||||
|
state.value.error?.let {
|
||||||
|
errorCallback(it)
|
||||||
|
}
|
||||||
|
state.value.error = null
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(paddingValues)
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(24.dp))
|
||||||
|
Text(
|
||||||
|
text = "Создать аккаунт",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 32.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope_extrabold))
|
||||||
|
),
|
||||||
|
color = Color.Black
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = "Введите свои данные ниже, чтобы начать совершать покупки.",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
color = Neutral_70
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
FurnitureTextField(
|
||||||
|
value = state.value.request.firstName,
|
||||||
|
onValueChange = { registrationScreenViewModel.setFirstName(it) },
|
||||||
|
textLabel = "Имя",
|
||||||
|
leadingResourceId = R.drawable.user_icon
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
FurnitureTextField(
|
||||||
|
value = state.value.request.lastName,
|
||||||
|
onValueChange = { registrationScreenViewModel.setLastName(it) },
|
||||||
|
textLabel = "Фамиля",
|
||||||
|
leadingResourceId = R.drawable.user_icon
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
FurnitureTextField(
|
||||||
|
value = state.value.request.email,
|
||||||
|
onValueChange = { registrationScreenViewModel.setEmail(it) },
|
||||||
|
textLabel = "Email",
|
||||||
|
leadingResourceId = R.drawable.mail_icon
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
FurniturePasswordTextField(
|
||||||
|
value = state.value.request.password,
|
||||||
|
onValueChange = { registrationScreenViewModel.setPassword(it) }
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
text = "Нажимая кнопку Зарегистрироваться, вы подтверждаете, что ознакомились и согласились с нашими Условиями использования и Политикой конфиденциальности.",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
color = Primary
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
FurnitureButton(
|
||||||
|
text = "Зарегистрироваться"
|
||||||
|
) {
|
||||||
|
registrationScreenViewModel.register()
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
onNavigateToLogin()
|
||||||
|
},
|
||||||
|
text = "Войти",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 20.sp,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope))
|
||||||
|
),
|
||||||
|
color = Neutral_70
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.furintiture.ui.screen.registration
|
||||||
|
|
||||||
|
import com.example.furintiture.model.request.RegisterRequest
|
||||||
|
|
||||||
|
data class RegistrationScreenState(
|
||||||
|
var error: String? = null,
|
||||||
|
var request: RegisterRequest = RegisterRequest(
|
||||||
|
firstName = "",
|
||||||
|
lastName = "",
|
||||||
|
email = "",
|
||||||
|
password = ""
|
||||||
|
)
|
||||||
|
)
|
@ -0,0 +1,79 @@
|
|||||||
|
package com.example.furintiture.ui.screen.registration
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.example.furintiture.data.DataStoreSettings
|
||||||
|
import com.example.furintiture.data.RetrofitClient
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class RegistrationScreenViewModel(private val dataStoreSettings: DataStoreSettings): ViewModel() {
|
||||||
|
private val _registrationScreenState = MutableStateFlow(RegistrationScreenState())
|
||||||
|
val registrationScreenState = _registrationScreenState.asStateFlow()
|
||||||
|
|
||||||
|
fun setPassword(password: String){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(request = it.request.copy(password = password))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setEmail(email: String){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(request = it.request.copy(email = email))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setLastName(lastName: String){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(request = it.request.copy(lastName = lastName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun setFirstName(firstName: String){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(request = it.request.copy(firstName = firstName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun register(){
|
||||||
|
if(!validate()) return
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = RetrofitClient.api.register(_registrationScreenState.value.request)
|
||||||
|
dataStoreSettings.setUuid(result.userUuid)
|
||||||
|
}
|
||||||
|
catch (e: Exception){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(error = e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validate(): Boolean{
|
||||||
|
if (_registrationScreenState.value.request.email.isEmpty()){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(error = "Введите email")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (_registrationScreenState.value.request.password.isEmpty()){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(error = "Введите password")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (_registrationScreenState.value.request.lastName.isEmpty()){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(error = "Введите фамилию")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (_registrationScreenState.value.request.firstName.isEmpty()){
|
||||||
|
_registrationScreenState.update {
|
||||||
|
it.copy(error = "Введите имя")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.example.furintiture.ui.screen.splashscreen
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Home
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.Font
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import com.example.furintiture.R
|
||||||
|
import com.example.furintiture.ui.theme.gradientColors
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SplashScreen() {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Brush.verticalGradient(colorStops = gradientColors))
|
||||||
|
,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(87.dp),
|
||||||
|
imageVector = Icons.Default.Home,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Дом мечты",
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 36.sp,
|
||||||
|
fontWeight = FontWeight.ExtraBold,
|
||||||
|
fontFamily = FontFamily(Font(R.font.manrope_extrabold)),
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
19
app/src/main/java/com/example/furintiture/ui/theme/Color.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.example.furintiture.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
val Primary = Color(0xFF5C6B67)
|
||||||
|
val Secondary = Color(0xFFEBB65B)
|
||||||
|
val Text = Color(0xFF404040)
|
||||||
|
val Neutral_40 = Color(0xFFE0E0E0)
|
||||||
|
val Green_Linear= Color(0xFF156651)
|
||||||
|
val Neutral_70 = Color(0xFF757575)
|
||||||
|
val Neutral_90 = Color(0xFF404040)
|
||||||
|
val Neutral_20 = Color(0xFFF5F5F5)
|
||||||
|
val Neutral_10 = Color(0xFFFFFFFF)
|
||||||
|
|
||||||
|
val gradientColors = arrayOf(
|
||||||
|
0.0f to Color(0xFF156651).copy(alpha = 0.94f),
|
||||||
|
0.47f to Color(0xFF156651).copy(alpha = 0.67f),
|
||||||
|
1f to Color(0xFF156651).copy(alpha = 0f)
|
||||||
|
)
|
58
app/src/main/java/com/example/furintiture/ui/theme/Theme.kt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package com.example.furintiture.ui.theme
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.darkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicDarkColorScheme
|
||||||
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
|
private val DarkColorScheme = darkColorScheme(
|
||||||
|
primary = Primary,
|
||||||
|
secondary = Secondary,
|
||||||
|
tertiary = Text
|
||||||
|
)
|
||||||
|
|
||||||
|
private val LightColorScheme = lightColorScheme(
|
||||||
|
primary = Primary,
|
||||||
|
secondary = Secondary,
|
||||||
|
tertiary = Text
|
||||||
|
|
||||||
|
/* Other default colors to override
|
||||||
|
background = Color(0xFFFFFBFE),
|
||||||
|
surface = Color(0xFFFFFBFE),
|
||||||
|
onPrimary = Color.White,
|
||||||
|
onSecondary = Color.White,
|
||||||
|
onTertiary = Color.White,
|
||||||
|
onBackground = Color(0xFF1C1B1F),
|
||||||
|
onSurface = Color(0xFF1C1B1F),
|
||||||
|
*/
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FurintitureTheme(
|
||||||
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
|
// Dynamic color is available on Android 12+
|
||||||
|
dynamicColor: Boolean = true,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val colorScheme = when {
|
||||||
|
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
darkTheme -> DarkColorScheme
|
||||||
|
else -> LightColorScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialTheme(
|
||||||
|
colorScheme = colorScheme,
|
||||||
|
typography = Typography,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
34
app/src/main/java/com/example/furintiture/ui/theme/Type.kt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.example.furintiture.ui.theme
|
||||||
|
|
||||||
|
import androidx.compose.material3.Typography
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
|
||||||
|
// Set of Material typography styles to start with
|
||||||
|
val Typography = Typography(
|
||||||
|
bodyLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 16.sp,
|
||||||
|
lineHeight = 24.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
)
|
||||||
|
/* Other default text styles to override
|
||||||
|
titleLarge = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
fontSize = 22.sp,
|
||||||
|
lineHeight = 28.sp,
|
||||||
|
letterSpacing = 0.sp
|
||||||
|
),
|
||||||
|
labelSmall = TextStyle(
|
||||||
|
fontFamily = FontFamily.Default,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
fontSize = 11.sp,
|
||||||
|
lineHeight = 16.sp,
|
||||||
|
letterSpacing = 0.5.sp
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
)
|
16
app/src/main/res/drawable/fi_sr_eye_1.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="21dp"
|
||||||
|
android:viewportWidth="20"
|
||||||
|
android:viewportHeight="21">
|
||||||
|
<group>
|
||||||
|
<clip-path
|
||||||
|
android:pathData="M0,0.5h20v20h-20z"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M10,13.833C11.841,13.833 13.333,12.341 13.333,10.5C13.333,8.659 11.841,7.167 10,7.167C8.159,7.167 6.667,8.659 6.667,10.5C6.667,12.341 8.159,13.833 10,13.833Z"
|
||||||
|
android:fillColor="#9E9E9E"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.392,8.349C18.1,6.244 15.16,2.712 10,2.712C4.84,2.712 1.9,6.244 0.608,8.349C0.208,8.995 -0.004,9.74 -0.004,10.5C-0.004,11.26 0.208,12.005 0.608,12.651C1.9,14.756 4.84,18.288 10,18.288C15.16,18.288 18.1,14.756 19.392,12.651C19.792,12.005 20.004,11.26 20.004,10.5C20.004,9.74 19.792,8.995 19.392,8.349ZM10,15.5C9.011,15.5 8.044,15.207 7.222,14.657C6.4,14.108 5.759,13.327 5.381,12.413C5.002,11.5 4.903,10.494 5.096,9.525C5.289,8.555 5.765,7.664 6.465,6.964C7.164,6.265 8.055,5.789 9.025,5.596C9.995,5.403 11,5.502 11.913,5.881C12.827,6.259 13.608,6.9 14.157,7.722C14.707,8.544 15,9.511 15,10.5C14.999,11.826 14.472,13.097 13.534,14.034C12.597,14.972 11.326,15.499 10,15.5Z"
|
||||||
|
android:fillColor="#9E9E9E"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
170
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
30
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
BIN
app/src/main/res/drawable/images.png
Normal file
After Width: | Height: | Size: 144 B |
10
app/src/main/res/drawable/lock_icon.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="25dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="25">
|
||||||
|
<path
|
||||||
|
android:pathData="M6.546,10.682V7.955C6.546,6.508 7.12,5.121 8.143,4.098C9.166,3.075 10.554,2.5 12,2.5C13.447,2.5 14.834,3.075 15.857,4.098C16.88,5.121 17.455,6.508 17.455,7.955V10.682H18.364C19.87,10.682 21.091,11.903 21.091,13.409V19.773C21.091,21.279 19.87,22.5 18.364,22.5H5.636C4.13,22.5 2.909,21.279 2.909,19.773V13.409C2.909,11.903 4.13,10.682 5.636,10.682H6.546ZM9.429,5.383C10.111,4.701 11.036,4.318 12,4.318C12.965,4.318 13.889,4.701 14.571,5.383C15.253,6.065 15.637,6.99 15.637,7.955V10.682H8.364V7.955C8.364,6.99 8.747,6.065 9.429,5.383ZM5.636,12.5C5.134,12.5 4.727,12.907 4.727,13.409V19.773C4.727,20.275 5.134,20.682 5.636,20.682H18.364C18.866,20.682 19.273,20.275 19.273,19.773V13.409C19.273,12.907 18.866,12.5 18.364,12.5H5.636Z"
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/mail_icon.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="25dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="25">
|
||||||
|
<path
|
||||||
|
android:pathData="M2,7.03C2,7.039 2,7.048 2,7.057V17.955C2,19.457 3.225,20.682 4.727,20.682H19.273C20.775,20.682 22,19.457 22,17.955V7.057C22,7.048 22,7.039 22,7.03C21.991,5.535 20.77,4.318 19.273,4.318H4.727C3.231,4.318 2.009,5.535 2,7.03ZM3.915,6.64C4.065,6.343 4.375,6.136 4.727,6.136H19.273C19.625,6.136 19.935,6.343 20.085,6.64L12,12.299L3.915,6.64ZM20.182,8.792V17.955C20.182,18.452 19.771,18.864 19.273,18.864H4.727C4.229,18.864 3.818,18.452 3.818,17.955V8.792L11.479,14.154C11.792,14.373 12.208,14.373 12.521,14.154L20.182,8.792Z"
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:fillType="evenOdd"/>
|
||||||
|
</vector>
|
13
app/src/main/res/drawable/user_icon.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="25dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="25">
|
||||||
|
<path
|
||||||
|
android:pathData="M19.784,21.263V19.263C19.784,18.203 19.363,17.185 18.613,16.435C17.862,15.685 16.845,15.263 15.784,15.263H7.784C6.723,15.263 5.706,15.685 4.956,16.435C4.206,17.185 3.784,18.203 3.784,19.263V21.263M15.784,7.263C15.784,9.473 13.993,11.263 11.784,11.263C9.575,11.263 7.784,9.473 7.784,7.263C7.784,5.054 9.575,3.263 11.784,3.263C13.993,3.263 15.784,5.054 15.784,7.263Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#757575"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
BIN
app/src/main/res/font/manrope.ttf
Normal file
BIN
app/src/main/res/font/manrope_extrabold.ttf
Normal file
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background" />
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||||
|
</adaptive-icon>
|
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Normal file
After Width: | Height: | Size: 7.6 KiB |
10
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="purple_200">#FFBB86FC</color>
|
||||||
|
<color name="purple_500">#FF6200EE</color>
|
||||||
|
<color name="purple_700">#FF3700B3</color>
|
||||||
|
<color name="teal_200">#FF03DAC5</color>
|
||||||
|
<color name="teal_700">#FF018786</color>
|
||||||
|
<color name="black">#FF000000</color>
|
||||||
|
<color name="white">#FFFFFFFF</color>
|
||||||
|
</resources>
|
3
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">furintiture</string>
|
||||||
|
</resources>
|