diff --git a/Neul-main/README.md b/Neul-main/README.md new file mode 100644 index 0000000..c72bdd5 --- /dev/null +++ b/Neul-main/README.md @@ -0,0 +1 @@ +# oop_project \ No newline at end of file diff --git a/Neul-main/build.gradle b/Neul-main/build.gradle new file mode 100644 index 0000000..45681ed --- /dev/null +++ b/Neul-main/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java' +} + +group 'com.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() // 라이브러리 받을 저장소 +} + +dependencies { + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.2' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2' // ✅ 이 줄 추가 +} diff --git a/Neul-main/build/classes/java/main/AnalysisPanel$1.class b/Neul-main/build/classes/java/main/AnalysisPanel$1.class new file mode 100644 index 0000000..37bbddd Binary files /dev/null and b/Neul-main/build/classes/java/main/AnalysisPanel$1.class differ diff --git a/Neul-main/build/classes/java/main/AnalysisPanel.class b/Neul-main/build/classes/java/main/AnalysisPanel.class new file mode 100644 index 0000000..8e9c358 Binary files /dev/null and b/Neul-main/build/classes/java/main/AnalysisPanel.class differ diff --git a/Neul-main/build/classes/java/main/AuthManager.class b/Neul-main/build/classes/java/main/AuthManager.class new file mode 100644 index 0000000..93f4587 Binary files /dev/null and b/Neul-main/build/classes/java/main/AuthManager.class differ diff --git a/Neul-main/build/classes/java/main/CurrentDateTime.class b/Neul-main/build/classes/java/main/CurrentDateTime.class new file mode 100644 index 0000000..b0b9903 Binary files /dev/null and b/Neul-main/build/classes/java/main/CurrentDateTime.class differ diff --git a/Neul-main/build/classes/java/main/DailyInfo.class b/Neul-main/build/classes/java/main/DailyInfo.class new file mode 100644 index 0000000..5d80c0f Binary files /dev/null and b/Neul-main/build/classes/java/main/DailyInfo.class differ diff --git a/Neul-main/build/classes/java/main/IntroPanel.class b/Neul-main/build/classes/java/main/IntroPanel.class new file mode 100644 index 0000000..284a350 Binary files /dev/null and b/Neul-main/build/classes/java/main/IntroPanel.class differ diff --git a/Neul-main/build/classes/java/main/LoginErrorPopup.class b/Neul-main/build/classes/java/main/LoginErrorPopup.class new file mode 100644 index 0000000..1f21530 Binary files /dev/null and b/Neul-main/build/classes/java/main/LoginErrorPopup.class differ diff --git a/Neul-main/build/classes/java/main/LoginPanel.class b/Neul-main/build/classes/java/main/LoginPanel.class new file mode 100644 index 0000000..18da855 Binary files /dev/null and b/Neul-main/build/classes/java/main/LoginPanel.class differ diff --git a/Neul-main/build/classes/java/main/MainMenuPanel.class b/Neul-main/build/classes/java/main/MainMenuPanel.class new file mode 100644 index 0000000..1295681 Binary files /dev/null and b/Neul-main/build/classes/java/main/MainMenuPanel.class differ diff --git a/Neul-main/build/classes/java/main/MoonCircle.class b/Neul-main/build/classes/java/main/MoonCircle.class new file mode 100644 index 0000000..1d0aab1 Binary files /dev/null and b/Neul-main/build/classes/java/main/MoonCircle.class differ diff --git a/Neul-main/build/classes/java/main/NEUL_MAIN.class b/Neul-main/build/classes/java/main/NEUL_MAIN.class new file mode 100644 index 0000000..e3f9c57 Binary files /dev/null and b/Neul-main/build/classes/java/main/NEUL_MAIN.class differ diff --git a/Neul-main/build/classes/java/main/PanelManager.class b/Neul-main/build/classes/java/main/PanelManager.class new file mode 100644 index 0000000..dedef65 Binary files /dev/null and b/Neul-main/build/classes/java/main/PanelManager.class differ diff --git a/Neul-main/build/classes/java/main/PasswordUtil.class b/Neul-main/build/classes/java/main/PasswordUtil.class new file mode 100644 index 0000000..880a890 Binary files /dev/null and b/Neul-main/build/classes/java/main/PasswordUtil.class differ diff --git a/Neul-main/build/classes/java/main/PwErrorPopup.class b/Neul-main/build/classes/java/main/PwErrorPopup.class new file mode 100644 index 0000000..21d1a76 Binary files /dev/null and b/Neul-main/build/classes/java/main/PwErrorPopup.class differ diff --git a/Neul-main/build/classes/java/main/PwLengErrorPopup.class b/Neul-main/build/classes/java/main/PwLengErrorPopup.class new file mode 100644 index 0000000..2176978 Binary files /dev/null and b/Neul-main/build/classes/java/main/PwLengErrorPopup.class differ diff --git a/Neul-main/build/classes/java/main/RandomSleepInformation.class b/Neul-main/build/classes/java/main/RandomSleepInformation.class new file mode 100644 index 0000000..9a3b53a Binary files /dev/null and b/Neul-main/build/classes/java/main/RandomSleepInformation.class differ diff --git a/Neul-main/build/classes/java/main/ScreenController.class b/Neul-main/build/classes/java/main/ScreenController.class new file mode 100644 index 0000000..76d19c7 Binary files /dev/null and b/Neul-main/build/classes/java/main/ScreenController.class differ diff --git a/Neul-main/build/classes/java/main/SignupPanel.class b/Neul-main/build/classes/java/main/SignupPanel.class new file mode 100644 index 0000000..b1f3925 Binary files /dev/null and b/Neul-main/build/classes/java/main/SignupPanel.class differ diff --git a/Neul-main/build/classes/java/main/SleepCalculator.class b/Neul-main/build/classes/java/main/SleepCalculator.class new file mode 100644 index 0000000..d4793b5 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepCalculator.class differ diff --git a/Neul-main/build/classes/java/main/SleepCyclePanel.class b/Neul-main/build/classes/java/main/SleepCyclePanel.class new file mode 100644 index 0000000..4c403d1 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepCyclePanel.class differ diff --git a/Neul-main/build/classes/java/main/SleepDataService$1.class b/Neul-main/build/classes/java/main/SleepDataService$1.class new file mode 100644 index 0000000..e59135c Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepDataService$1.class differ diff --git a/Neul-main/build/classes/java/main/SleepDataService.class b/Neul-main/build/classes/java/main/SleepDataService.class new file mode 100644 index 0000000..cf360a1 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepDataService.class differ diff --git a/Neul-main/build/classes/java/main/SleepDatabase.class b/Neul-main/build/classes/java/main/SleepDatabase.class new file mode 100644 index 0000000..b34c806 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepDatabase.class differ diff --git a/Neul-main/build/classes/java/main/SleepInputPanel$1.class b/Neul-main/build/classes/java/main/SleepInputPanel$1.class new file mode 100644 index 0000000..04d11c1 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepInputPanel$1.class differ diff --git a/Neul-main/build/classes/java/main/SleepInputPanel$AnalogClock.class b/Neul-main/build/classes/java/main/SleepInputPanel$AnalogClock.class new file mode 100644 index 0000000..88a80d3 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepInputPanel$AnalogClock.class differ diff --git a/Neul-main/build/classes/java/main/SleepInputPanel.class b/Neul-main/build/classes/java/main/SleepInputPanel.class new file mode 100644 index 0000000..d8635f0 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepInputPanel.class differ diff --git a/Neul-main/build/classes/java/main/SleepRecommendationPanel.class b/Neul-main/build/classes/java/main/SleepRecommendationPanel.class new file mode 100644 index 0000000..1097252 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepRecommendationPanel.class differ diff --git a/Neul-main/build/classes/java/main/SleepRecord.class b/Neul-main/build/classes/java/main/SleepRecord.class new file mode 100644 index 0000000..ea2e322 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepRecord.class differ diff --git a/Neul-main/build/classes/java/main/SleepTime.class b/Neul-main/build/classes/java/main/SleepTime.class new file mode 100644 index 0000000..568fa20 Binary files /dev/null and b/Neul-main/build/classes/java/main/SleepTime.class differ diff --git a/Neul-main/build/classes/java/main/TodayPanel$1.class b/Neul-main/build/classes/java/main/TodayPanel$1.class new file mode 100644 index 0000000..81fcdf5 Binary files /dev/null and b/Neul-main/build/classes/java/main/TodayPanel$1.class differ diff --git a/Neul-main/build/classes/java/main/TodayPanel.class b/Neul-main/build/classes/java/main/TodayPanel.class new file mode 100644 index 0000000..a12ffda Binary files /dev/null and b/Neul-main/build/classes/java/main/TodayPanel.class differ diff --git a/Neul-main/build/classes/java/main/User.class b/Neul-main/build/classes/java/main/User.class new file mode 100644 index 0000000..313d16f Binary files /dev/null and b/Neul-main/build/classes/java/main/User.class differ diff --git a/Neul-main/build/classes/java/main/UserService$1.class b/Neul-main/build/classes/java/main/UserService$1.class new file mode 100644 index 0000000..7bf9e3e Binary files /dev/null and b/Neul-main/build/classes/java/main/UserService$1.class differ diff --git a/Neul-main/build/classes/java/main/UserService.class b/Neul-main/build/classes/java/main/UserService.class new file mode 100644 index 0000000..d773690 Binary files /dev/null and b/Neul-main/build/classes/java/main/UserService.class differ diff --git a/Neul-main/build/classes/java/main/WakeRecommendationPanel.class b/Neul-main/build/classes/java/main/WakeRecommendationPanel.class new file mode 100644 index 0000000..f508c24 Binary files /dev/null and b/Neul-main/build/classes/java/main/WakeRecommendationPanel.class differ diff --git a/Neul-main/build/classes/java/main/WakeTime.class b/Neul-main/build/classes/java/main/WakeTime.class new file mode 100644 index 0000000..f80e8fa Binary files /dev/null and b/Neul-main/build/classes/java/main/WakeTime.class differ diff --git a/Neul-main/build/resources/main/images/Emotion (1).png b/Neul-main/build/resources/main/images/Emotion (1).png new file mode 100644 index 0000000..5432f09 Binary files /dev/null and b/Neul-main/build/resources/main/images/Emotion (1).png differ diff --git a/Neul-main/build/resources/main/images/Emotion (2).png b/Neul-main/build/resources/main/images/Emotion (2).png new file mode 100644 index 0000000..a692de5 Binary files /dev/null and b/Neul-main/build/resources/main/images/Emotion (2).png differ diff --git a/Neul-main/build/resources/main/images/Emotion.png b/Neul-main/build/resources/main/images/Emotion.png new file mode 100644 index 0000000..3aa1e53 Binary files /dev/null and b/Neul-main/build/resources/main/images/Emotion.png differ diff --git a/Neul-main/build/resources/main/images/img.png b/Neul-main/build/resources/main/images/img.png new file mode 100644 index 0000000..971a4c9 Binary files /dev/null and b/Neul-main/build/resources/main/images/img.png differ diff --git a/Neul-main/build/tmp/compileJava/previous-compilation-data.bin b/Neul-main/build/tmp/compileJava/previous-compilation-data.bin new file mode 100644 index 0000000..6cd5fe8 Binary files /dev/null and b/Neul-main/build/tmp/compileJava/previous-compilation-data.bin differ diff --git a/Neul-main/gradle/wrapper/gradle-wrapper.jar b/Neul-main/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/Neul-main/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Neul-main/gradle/wrapper/gradle-wrapper.properties b/Neul-main/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9355b41 --- /dev/null +++ b/Neul-main/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Neul-main/gradlew b/Neul-main/gradlew new file mode 100644 index 0000000..f5feea6 --- /dev/null +++ b/Neul-main/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Neul-main/gradlew.bat b/Neul-main/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/Neul-main/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Neul-main/settings.gradle b/Neul-main/settings.gradle new file mode 100644 index 0000000..236c7de --- /dev/null +++ b/Neul-main/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'Neul' diff --git a/Neul-main/sleep_data.json b/Neul-main/sleep_data.json new file mode 100644 index 0000000..dc8bff6 --- /dev/null +++ b/Neul-main/sleep_data.json @@ -0,0 +1,11 @@ +{ + "kimsubin" : [ { + "date" : "2025-05-24", + "mood" : "나쁨", + "sleepDuration" : "8시간 5분" + }, { + "date" : "2025-05-26", + "mood" : "보통", + "sleepDuration" : "8시간 0분" + } ] +} \ No newline at end of file diff --git a/Neul-main/src/main/java/AnalysisPanel.java b/Neul-main/src/main/java/AnalysisPanel.java new file mode 100644 index 0000000..e7876a8 --- /dev/null +++ b/Neul-main/src/main/java/AnalysisPanel.java @@ -0,0 +1,124 @@ +import javax.swing.*; +import java.awt.*; +import java.time.Duration; +import java.util.List; + +public class AnalysisPanel extends JPanel { + + private SleepDataService sleepDataService; + private RandomSleepInformation infoService; + public String userId = "default"; + private Duration averageDuration; // SleepCalculator에서 받아온 평균 + private ScreenController screenController; + private PanelManager panelManager; + + public AnalysisPanel(SleepDataService sleepDataService, + RandomSleepInformation infoService, + Duration averageDuration, + ScreenController screenController, + PanelManager panelManager) { + + this.sleepDataService = sleepDataService; + this.infoService = infoService; + this.averageDuration = averageDuration; + this.screenController = screenController; + this.panelManager = panelManager; + + //setTitle("NEUL - 수면 및 감정 분석"); + setSize(1440, 1024); + //setLocationRelativeTo(null); + //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLayout(null); + setBackground(Color.decode("#565686")); + + JLabel titleLabel = new JLabel("NEUL", SwingConstants.CENTER); + titleLabel.setFont(new Font("Serif", Font.PLAIN, 80)); + titleLabel.setForeground(Color.WHITE); + titleLabel.setBounds(0, 50, 1440, 80); + add(titleLabel); + + // Graph Panel (Left) + JPanel graphPanel = new JPanel() { + protected void paintComponent(Graphics g) { + super.paintComponent(g); + drawSleepGraph(g); + } + }; + graphPanel.setBounds(80, 150, 600, 750); + graphPanel.setBackground(Color.WHITE); + add(graphPanel); + + JLabel graphTitle = new JLabel("Sleep statistics", SwingConstants.CENTER); + graphTitle.setFont(new Font("Serif", Font.PLAIN, 26)); + graphTitle.setBounds(80, 120, 600, 30); + graphTitle.setForeground(Color.BLACK); + add(graphTitle); + + // Info Panel (Right) + JPanel infoPanel = new JPanel(null); + infoPanel.setBounds(760, 150, 600, 750); + infoPanel.setBackground(Color.WHITE); + add(infoPanel); + + JLabel infoTitle = new JLabel("Information about sleep"); + infoTitle.setFont(new Font("Serif", Font.PLAIN, 26)); + infoTitle.setBounds(20, 10, 500, 40); + infoPanel.add(infoTitle); + + JTextArea infoArea = new JTextArea(); + infoArea.setEditable(false); + infoArea.setFont(new Font("SansSerif", Font.PLAIN, 16)); + infoArea.setLineWrap(true); + infoArea.setWrapStyleWord(true); + + List goodThings = infoService.getRandomGoodThings(); + List badThings = infoService.getRandomBadThings(); + + StringBuilder infoText = new StringBuilder(); + infoText.append("좋은 요소\n"); + infoText.append(String.join(", ", goodThings)).append(" ...\n\n"); + infoText.append("나쁜 요소\n"); + infoText.append(String.join(", ", badThings)).append(" ..."); + + infoArea.setText(infoText.toString()); + + JScrollPane scrollPane = new JScrollPane(infoArea); + scrollPane.setBounds(20, 60, 550, 120); + infoPanel.add(scrollPane); + + JLabel commentLabel = new JLabel("Comment related to emotion"); + commentLabel.setFont(new Font("Serif", Font.PLAIN, 22)); + commentLabel.setBounds(20, 200, 500, 40); + infoPanel.add(commentLabel); + + JTextArea commentArea = new JTextArea(); + commentArea.setFont(new Font("SansSerif", Font.PLAIN, 16)); + commentArea.setEditable(false); + commentArea.setBackground(Color.WHITE); + commentArea.setBounds(20, 250, 550, 150); + infoPanel.add(commentArea); + + setVisible(true); + } + + private void drawSleepGraph(Graphics g) { + List weekly = sleepDataService.getLast7DaysSleepDurations(userId); // 월~일 리스트 + + int baseX = 60; + int baseY = 500; + int barWidth = 30; + int space = 60; + + g.setColor(Color.BLACK); + for (int i = 0; i < weekly.size(); i++) { + int barHeight = (int) (weekly.get(i).toMinutes() / 10); // scale + g.fillRect(baseX + i * (barWidth + space), baseY - barHeight, barWidth, barHeight); + } + + // 평균선 (SleepCalculator에서 받아온 값 사용) + g.setColor(new Color(160, 140, 255)); + int avgHeight = (int) (averageDuration.toMinutes() / 10); + g.drawLine(baseX, baseY - avgHeight, baseX + weekly.size() * (barWidth + space), baseY - avgHeight); + g.drawString("\uD3C9\uADE0", baseX + weekly.size() * (barWidth + space) + 5, baseY - avgHeight); + } +} diff --git a/Neul-main/src/main/java/AuthManager.java b/Neul-main/src/main/java/AuthManager.java new file mode 100644 index 0000000..b07d187 --- /dev/null +++ b/Neul-main/src/main/java/AuthManager.java @@ -0,0 +1,25 @@ +// 현재 로그인된 사용자 상태를 관리하는 클래스 + +public class AuthManager { + private static User currentUser; + + // 로그인된 사용자 저장 + public static void setCurrentUser(User user) { + currentUser = user; + } + + // 로그아웃 처리 + public static void logout() { + currentUser = null; + } + + // 로그인 여부 확인 + public static boolean isLoggedIn() { + return currentUser != null; + } + + // 현재 로그인된 사용자 반환 + public static User getCurrentUser() { + return currentUser; + } +} diff --git a/Neul-main/src/main/java/CurrentDateTime.java b/Neul-main/src/main/java/CurrentDateTime.java new file mode 100644 index 0000000..c4cf598 --- /dev/null +++ b/Neul-main/src/main/java/CurrentDateTime.java @@ -0,0 +1,33 @@ +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +public class CurrentDateTime { + //현재 시간 + public LocalTime now; + //MAX_TIME + public static int TIMES_IN_MINUTE = 90; + //MAX_CYCLE + public static int MAX_CYCLES = 6; + //시간 포맷 + public DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + + + //현재 시간 출력 + public String getCurrentTime() { + now = LocalTime.now(); + return now.format(FORMATTER); + } + + //현재 시간에서 1.5시간 사이클 계산 + public List recommendWakeTimes() { + now = LocalTime.now(); + List wakeTimes = new ArrayList<>(); + for (int i = 1; i <= MAX_CYCLES; i++) { + LocalTime wakeTime = now.plusMinutes(i * TIMES_IN_MINUTE); + wakeTimes.add(wakeTime.format(FORMATTER)); + } + return wakeTimes; + } +} diff --git a/Neul-main/src/main/java/DailyInfo.java b/Neul-main/src/main/java/DailyInfo.java new file mode 100644 index 0000000..91f9830 --- /dev/null +++ b/Neul-main/src/main/java/DailyInfo.java @@ -0,0 +1,37 @@ +import javax.swing.*; +import java.awt.*; + +public class DailyInfo extends JDialog { + public DailyInfo(String day, String sleepTime, String quality) { // 날짜, 취침시간, 수면의 질 넘겨받기 + + setTitle("Daily Information"); // 창 이름 + setSize(753, 418); // 창 크기 + setResizable(false); + setLocationRelativeTo(null); // 창을 화면 정중앙에 띄움 + setDefaultCloseOperation(DISPOSE_ON_CLOSE); // 창 닫기 + + setLayout(new BorderLayout()); + Color backgroundColor = new Color(200, 205, 225); // 창 전체에 사용할 색 + + // 날짜 + JLabel dateLabel = new JLabel(day); + dateLabel.setFont(new Font("SansSerif", Font.BOLD, 30)); // 글씨체 변경 + dateLabel.setHorizontalAlignment(SwingConstants.LEFT); // 왼쪽 + dateLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 0)); // 여백 + dateLabel.setOpaque(true); // ← 배경색 적용 가능하게 + dateLabel.setBackground(backgroundColor); // ← contentPanel과 색 통일 + add(dateLabel, BorderLayout.NORTH); // 위쪽 + + // 취침 시간, 수면의 질 + JPanel contentPanel = new JPanel(new GridLayout(2, 1)); + contentPanel.setBackground(backgroundColor); // 바탕색 설정 + + JLabel sleepLabel = new JLabel(sleepTime + " 취침", SwingConstants.RIGHT); + JLabel qualityLabel = new JLabel("수면의 질: " + quality, SwingConstants.RIGHT); + + contentPanel.add(sleepLabel); + contentPanel.add(qualityLabel); + + add(contentPanel, BorderLayout.CENTER); + } +} diff --git a/Neul-main/src/main/java/IntroPanel.java b/Neul-main/src/main/java/IntroPanel.java new file mode 100644 index 0000000..731f41a --- /dev/null +++ b/Neul-main/src/main/java/IntroPanel.java @@ -0,0 +1,86 @@ + +import javax.swing.*; +import java.awt.*; + +public class IntroPanel extends JPanel { + private ScreenController controller; + + public IntroPanel(ScreenController controller) { + this.controller = controller; + setLayout(new BorderLayout()); + setBackground(new Color(91, 89, 153)); + + + // 중앙 텍스트 + 버튼 패널 + JPanel centerPanel = new JPanel(); + centerPanel.setBackground(new Color(91, 89, 153)); + centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); + centerPanel.setBorder(BorderFactory.createEmptyBorder(300, 0, 0, 0)); + + // 부제목 + JLabel subtitle = new JLabel( + "
늘 곁에 있는,
나만의 감정 수면 파트너
", + SwingConstants.CENTER + ); + subtitle.setForeground(Color.WHITE); + subtitle.setFont(new Font("SansSerif", Font.PLAIN, 24)); + subtitle.setAlignmentX(Component.CENTER_ALIGNMENT); + + // 제목 + JLabel title = new JLabel("NEUL"); + title.setForeground(Color.WHITE); + title.setFont(new Font("Serif", Font.BOLD, 90)); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + + // 버튼 + JButton loginBtn = new JButton("로그인"); + JButton signupBtn = new JButton("회원가입"); + + customizeButton(loginBtn); + customizeButton(signupBtn); + + // 각 버튼 클릭 시 새 창 열기 + loginBtn.addActionListener(e -> { + controller.changeScreen("login"); // 기존 창 닫고 카드 전환 + }); + + signupBtn.addActionListener(e -> { + controller.changeScreen("signup"); // 기존 창 닫고 카드 전환 + }); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setBackground(new Color(91, 89, 153)); + buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 50, 0)); + buttonPanel.add(loginBtn); + buttonPanel.add(signupBtn); + buttonPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // 패널 구성 + centerPanel.add(subtitle); + centerPanel.add(Box.createVerticalStrut(40)); + centerPanel.add(title); + centerPanel.add(Box.createVerticalStrut(70)); + centerPanel.add(buttonPanel); + + add(centerPanel, BorderLayout.CENTER); + } + + private void customizeButton(JButton button) { + button.setPreferredSize(new Dimension(120, 40)); + button.setBackground(Color.WHITE); + button.setForeground(Color.BLACK); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY)); + button.setFont(new Font("SansSerif", Font.PLAIN, 16)); + } + + //public static void main(String[] args) { + //JFrame frame = new JFrame("Intro"); + //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + //frame.setSize(1440, 1024); + //frame.setLocationRelativeTo(null); + //frame.add(new IntroPanel()); + //frame.setVisible(true); + //} +} +//test 용 main임 주석처리하고 사용하기 diff --git a/Neul-main/src/main/java/LoginErrorPopup.java b/Neul-main/src/main/java/LoginErrorPopup.java new file mode 100644 index 0000000..b4c616c --- /dev/null +++ b/Neul-main/src/main/java/LoginErrorPopup.java @@ -0,0 +1,44 @@ +import javax.swing.*; +import java.awt.*; + +public class LoginErrorPopup { + + public static void showErrorDialog(JFrame parent) { + // 다이얼로그 생성 + JDialog dialog = new JDialog(parent, "오류", true); + dialog.setSize(400, 220); + dialog.setLayout(null); + dialog.setResizable(false); + dialog.getContentPane().setBackground(new Color(200, 205, 225)); + + // 중앙 정렬 + dialog.setLocationRelativeTo(parent); + + // 메시지 라벨 + JLabel message = new JLabel("
아이디나 비밀번호가 다릅니다.
다시 입력해주세요!
", SwingConstants.CENTER); + message.setFont(new Font("SansSerif", Font.BOLD, 16)); + message.setBounds(30, 30, 340, 60); + dialog.add(message); + + // 확인 버튼 + JButton okButton = new JButton("확인"); + okButton.setFont(new Font("SansSerif", Font.BOLD, 14)); + okButton.setBounds(160, 120, 80, 35); + dialog.add(okButton); + + // 버튼 클릭 시 창 닫기 + okButton.addActionListener(e -> dialog.dispose()); + + dialog.setVisible(true); + } + + // 테스트용 실행 + //public static void main(String[] args) { + //JFrame dummyParent = new JFrame(); + //dummyParent.setSize(400, 400); + //dummyParent.setLocationRelativeTo(null); + //dummyParent.setVisible(true); + + //showErrorDialog(dummyParent); + //} +} diff --git a/Neul-main/src/main/java/LoginPanel.java b/Neul-main/src/main/java/LoginPanel.java new file mode 100644 index 0000000..0034bfd --- /dev/null +++ b/Neul-main/src/main/java/LoginPanel.java @@ -0,0 +1,102 @@ +import javax.swing.*; +import java.awt.*; + +public class LoginPanel extends JPanel { + private ScreenController controller; + + public LoginPanel(ScreenController controller) { + this.controller = controller; + + setLayout(null); + setBackground(new Color(91, 89, 153)); // 짙은 보라 배경 + + // NEUL + 달 이미지 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 60)); + title.setForeground(new Color(182, 153, 40)); // 금색 + title.setBounds(620, 60, 200, 80); + add(title); + + // 달 이미지 (그림) + MoonCircle moon = new MoonCircle(); + moon.setBounds(800, 70, 50, 50); + add(moon); + + // "로그인" 타이틀 + JLabel loginLabel = new JLabel("로그인", SwingConstants.CENTER); + loginLabel.setFont(new Font("SansSerif", Font.BOLD, 36)); + loginLabel.setBounds(620, 180, 200, 50); + add(loginLabel); + + // 로그인 폼 박스 + JPanel formPanel = new JPanel(); + formPanel.setLayout(null); + formPanel.setBackground(Color.WHITE); + formPanel.setBounds(470, 290, 500, 500); + formPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1, true)); + add(formPanel); + + // 아이디 입력 필드 + JLabel idLabel = new JLabel("아이디"); + idLabel.setBounds(60, 50, 100, 30); + JTextField idField = new JTextField(); + idField.setBounds(60, 80, 380, 35); + + // 비밀번호 입력 필드 + JLabel pwLabel = new JLabel("비밀번호"); + pwLabel.setBounds(60, 150, 100, 30); + JPasswordField pwField = new JPasswordField(); + pwField.setBounds(60, 180, 380, 35); + + // 로그인 버튼 + JButton loginBtn = new JButton("로그인"); + loginBtn.setBounds(60, 350, 380, 40); + loginBtn.setBackground(new Color(40, 40, 40)); + loginBtn.setForeground(Color.WHITE); + + // UserService 인스턴스 + UserService userService = UserService.getInstance(); + + // 버튼 클릭 이벤트 처리 + loginBtn.addActionListener(e -> { + String username = idField.getText(); + String password = new String(pwField.getPassword()); + + String result = String.valueOf(userService.login(username, password)); + + if (!result.equals("로그인이 완료되었습니다.")) { + // 로그인 실패 팝업 + LoginErrorPopup.showErrorDialog((JFrame) SwingUtilities.getWindowAncestor(this)); + } else { + // 로그인 성공 처리 + JOptionPane.showMessageDialog(this, "로그인 성공!"); + + // 현재 창 닫기 + Window window = SwingUtilities.getWindowAncestor(this); + if (window != null) { + window.dispose(); + } + + // 카드 기반 화면 전환 + controller.changeScreen("main"); + } + }); + + // 컴포넌트들을 formPanel에 추가 + formPanel.add(idLabel); + formPanel.add(idField); + formPanel.add(pwLabel); + formPanel.add(pwField); + formPanel.add(loginBtn); + } + + // 테스트용 실행 + // public static void main(String[] args) { + // JFrame frame = new JFrame("Login - NEUL"); + // frame.setSize(1440, 1024); + // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // frame.setLocationRelativeTo(null); + // frame.add(new LoginPanel(null)); // 테스트용으로 null 전달 + // frame.setVisible(true); + // } +} diff --git a/Neul-main/src/main/java/MainMenuPanel.java b/Neul-main/src/main/java/MainMenuPanel.java new file mode 100644 index 0000000..afef905 --- /dev/null +++ b/Neul-main/src/main/java/MainMenuPanel.java @@ -0,0 +1,96 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +// 프로젝트 내의 클래스 (패키지 경로에 따라 조정) +//import SleepInputPanel; +//import IntroPanel; + + +public class MainMenuPanel extends JPanel { + private ScreenController controller; + + public MainMenuPanel(ScreenController controller) { + this.controller = controller; + setLayout(null); + setBackground(new Color(91, 89, 153)); // 배경색 + + // 상단 NEUL 로고 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 80)); + title.setForeground(Color.WHITE); + title.setBounds(610, 100, 400, 100); + add(title); + + // 왼쪽 상단 로그아웃 버튼 + JButton logoutButton = new JButton("Logout"); + logoutButton.setBounds(40, 30, 100, 40); + add(logoutButton); + + // 버튼 공통 스타일 + Font buttonFont = new Font("SansSerif", Font.BOLD, 24); + Color btnBackground = Color.WHITE; + int buttonWidth = 300; + int buttonHeight = 60; + int x = 580; + int yStart = 300; + int gap = 140; + + // Calculator 버튼 + JButton calculatorBtn = new JButton("Calculator"); + calculatorBtn.setFont(buttonFont); + calculatorBtn.setBackground(btnBackground); + calculatorBtn.setBounds(x, yStart, buttonWidth, buttonHeight); + add(calculatorBtn); + + // Today's 버튼 + JButton todayBtn = new JButton("Today's"); + todayBtn.setFont(buttonFont); + todayBtn.setBackground(btnBackground); + todayBtn.setBounds(x, yStart + gap, buttonWidth, buttonHeight); + add(todayBtn); + + // Analysis 버튼 + JButton analysisBtn = new JButton("Analysis"); + analysisBtn.setFont(buttonFont); + analysisBtn.setBackground(btnBackground); + analysisBtn.setBounds(x, yStart + 2 * gap, buttonWidth, buttonHeight); + add(analysisBtn); + + // 이벤트 리스너는 반드시 생성자 안에 있어야 함 + calculatorBtn.addActionListener(e -> { + JFrame sleepInputFrame = new JFrame("NEUL - Sleep Calculator"); + sleepInputFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + sleepInputFrame.setSize(1440, 1024); + sleepInputFrame.setLocationRelativeTo(null); + sleepInputFrame.setContentPane(new SleepInputPanel(controller)); + sleepInputFrame.setVisible(true); + }); + + logoutButton.addActionListener(e -> { + Window window = SwingUtilities.getWindowAncestor(this); + if (window != null) { + window.dispose(); + } + + JFrame introFrame = new JFrame("NEUL - Intro"); + introFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + introFrame.setSize(1440, 1024); + introFrame.setLocationRelativeTo(null); + introFrame.setContentPane(new IntroPanel(controller)); + introFrame.setVisible(true); + }); + } + +// public static void main(String[] args) { +// JFrame frame = new JFrame("Main Menu - NEUL"); +// frame.setSize(1440, 1024); +// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); +// frame.setLocationRelativeTo(null); +// frame.add(new MainMenuPanel()); +// frame.setVisible(true); +// } + + //테스트용 메인이라 나중에 주석처리하거나 지워야됨 +} diff --git a/Neul-main/src/main/java/MoonCircle.java b/Neul-main/src/main/java/MoonCircle.java new file mode 100644 index 0000000..5a3611e --- /dev/null +++ b/Neul-main/src/main/java/MoonCircle.java @@ -0,0 +1,30 @@ +import javax.swing.*; +import java.awt.*; +public class MoonCircle extends JPanel { + public MoonCircle() { + setPreferredSize(new Dimension(50, 50)); + setOpaque(false); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 그림자 + g2d.setColor(new Color(0, 0, 0, 50)); + g2d.fillOval(5, 5, 40, 40); + + // 달 본체 (그라데이션 음영) + GradientPaint gradient = new GradientPaint(0, 0, new Color(255, 223, 0), 40, 40, new Color(200, 170, 0)); + g2d.setPaint(gradient); + g2d.fillOval(0, 0, 40, 40); + + // 테두리 + g2d.setColor(new Color(160, 130, 0)); + g2d.setStroke(new BasicStroke(1f)); + g2d.drawOval(0, 0, 40, 40); + } +} + diff --git a/Neul-main/src/main/java/NEUL_MAIN.java b/Neul-main/src/main/java/NEUL_MAIN.java new file mode 100644 index 0000000..51c9c77 --- /dev/null +++ b/Neul-main/src/main/java/NEUL_MAIN.java @@ -0,0 +1,27 @@ +import javax.swing.SwingUtilities; + +public class NEUL_MAIN { + public static void main(String[] args) { + SwingUtilities.invokeLater(() -> { + SleepDataService dataService = new SleepDataService(); + RandomSleepInformation infoService = new RandomSleepInformation(); + + // dummy controller 먼저 생성 + ScreenController controller = new ScreenController(null); + + // PanelManager 생성 시 controller는 일단 전달 + PanelManager manager = new PanelManager(controller, dataService, infoService); + + // controller 내부에 manager 연결 + controller.setMainWindow(manager); // setMainWindow() 메서드 필요 + + // controller 다시 PanelManager에 주입 (선택 사항) + manager.setController(controller); + + // 이제 controller가 살아 있으니 패널 등록 진행 + manager.registerScreens(dataService, infoService); + + controller.navigateTo("intro"); + }); + } +} diff --git a/Neul-main/src/main/java/PanelManager.java b/Neul-main/src/main/java/PanelManager.java new file mode 100644 index 0000000..bbe8281 --- /dev/null +++ b/Neul-main/src/main/java/PanelManager.java @@ -0,0 +1,69 @@ +import javax.swing.*; +import java.awt.*; +import java.util.HashMap; + +public class PanelManager extends JFrame{ + private ScreenController controller; + private CardLayout cardLayout; + private SleepDataService dataService; + private RandomSleepInformation infoService; + private JPanel cardPanel; + private HashMap screens = new HashMap<>(); + + public PanelManager(ScreenController controller, SleepDataService dataService, RandomSleepInformation infoService) { + this.controller = controller; + this.dataService = dataService; + this.infoService = infoService; + + // 화면 전환 중앙 통제용도임 + setTitle("Sleep Tracker - NEUL"); + setSize(1440, 1024); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); // 화면 중앙 정렬 + + // CardLayout 기반 화면 전환용 패널 초기화 + cardLayout = new CardLayout(); + cardPanel = new JPanel(cardLayout); + add(cardPanel); + setVisible(true); + } + + public void registerScreens(SleepDataService dataService, RandomSleepInformation infoService) { + addScreen("intro", new IntroPanel(controller)); // 초기 화면 (로그인/회원가입 버튼있는 화면) + addScreen("login", new LoginPanel(controller)); // 로그인 화면 + addScreen("signup", new SignupPanel(controller)); // 회원가입 화면 + addScreen("main", new MainMenuPanel(controller)); // 메인(홈) 화면 + addScreen("calculator", new SleepInputPanel(controller)); // 시간계산기 화면 + //addScreen("sleepRecommendation", new SleepRecommendationPanel(controller)); + //addScreen("wakeRecommendation", new WakeRecommendationPanel(controller)); + //addScreen("sleepCycle", new SleepCyclePanel(controller)); + } + + public void setController(ScreenController controller) { + this.controller = controller; + } + + // 화면 등록 메서드: 이름과 패널을 매핑하여 cardPanel에 추가 + public void addScreen(String name, JPanel panel){ + if (screens.containsKey(name)) { + cardPanel.remove(screens.get(name)); // 이미 있는 패널 제거 + screens.remove(name); // 맵에서도 제거 + } + screens.put(name,panel); + cardPanel.add(panel,name); + } + + // 화면 전환 메서드: 이름을 기반으로 화면 전환 + public void showScreen(String name){ + System.out.println("🔁 전환 시도: " + name); + cardLayout.show(cardPanel, name); + } + + public SleepDataService getSleepDataService() { + return dataService; + } + + public RandomSleepInformation getInfoService() { + return infoService; + } +} diff --git a/Neul-main/src/main/java/PasswordUtil.java b/Neul-main/src/main/java/PasswordUtil.java new file mode 100644 index 0000000..235c4cc --- /dev/null +++ b/Neul-main/src/main/java/PasswordUtil.java @@ -0,0 +1,22 @@ +// 비밀번호 해시를 위한 유틸리티 클래스 + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class PasswordUtil { + + // SHA-256으로 비밀번호 해시 + public static String hashPassword(String password) { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] hashedBytes = md.digest(password.getBytes()); + StringBuilder sb = new StringBuilder(); + for (byte b : hashedBytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("SHA-256 알고리즘 오류 발생"); + } + } +} diff --git a/Neul-main/src/main/java/PwErrorPopup.java b/Neul-main/src/main/java/PwErrorPopup.java new file mode 100644 index 0000000..3733bcb --- /dev/null +++ b/Neul-main/src/main/java/PwErrorPopup.java @@ -0,0 +1,44 @@ +import javax.swing.*; +import java.awt.*; + +public class PwErrorPopup { + + public static void showErrorDialog(JFrame parent) { + // 다이얼로그 생성 + JDialog dialog = new JDialog(parent, "오류", true); + dialog.setSize(400, 220); + dialog.setLayout(null); + dialog.setResizable(false); + dialog.getContentPane().setBackground(new Color(200, 205, 225)); + + // 중앙 정렬 + dialog.setLocationRelativeTo(parent); + + // 메시지 라벨 + JLabel message = new JLabel("
비밀번호가 다릅니다.
다시 입력해주세요!
", SwingConstants.CENTER); + message.setFont(new Font("SansSerif", Font.BOLD, 16)); + message.setBounds(30, 30, 340, 60); + dialog.add(message); + + // 확인 버튼 + JButton okButton = new JButton("확인"); + okButton.setFont(new Font("SansSerif", Font.BOLD, 14)); + okButton.setBounds(160, 120, 80, 35); + dialog.add(okButton); + + // 버튼 클릭 시 창 닫기 + okButton.addActionListener(e -> dialog.dispose()); + + dialog.setVisible(true); + } + + // 테스트용 실행 + //public static void main(String[] args) { + //JFrame dummyParent = new JFrame(); + //dummyParent.setSize(400, 400); + //dummyParent.setLocationRelativeTo(null); + //dummyParent.setVisible(true); + + //showErrorDialog(dummyParent); + //} +} diff --git a/Neul-main/src/main/java/PwLengErrorPopup.java b/Neul-main/src/main/java/PwLengErrorPopup.java new file mode 100644 index 0000000..97a6156 --- /dev/null +++ b/Neul-main/src/main/java/PwLengErrorPopup.java @@ -0,0 +1,44 @@ +import javax.swing.*; +import java.awt.*; + +public class PwLengErrorPopup { + + public static void showErrorDialog(JFrame parent) { + // 다이얼로그 생성 + JDialog dialog = new JDialog(parent, "오류", true); + dialog.setSize(400, 220); + dialog.setLayout(null); + dialog.setResizable(false); + dialog.getContentPane().setBackground(new Color(200, 205, 225)); + + // 중앙 정렬 + dialog.setLocationRelativeTo(parent); + + // 메시지 라벨 + JLabel message = new JLabel("
비밀번호는 6글자
이상이어야 합니다!
", SwingConstants.CENTER); + message.setFont(new Font("SansSerif", Font.BOLD, 16)); + message.setBounds(30, 30, 340, 60); + dialog.add(message); + + // 확인 버튼 + JButton okButton = new JButton("확인"); + okButton.setFont(new Font("SansSerif", Font.BOLD, 14)); + okButton.setBounds(160, 120, 80, 35); + dialog.add(okButton); + + // 버튼 클릭 시 창 닫기 + okButton.addActionListener(e -> dialog.dispose()); + + dialog.setVisible(true); + } + + // 테스트용 실행 + //public static void main(String[] args) { + //JFrame dummyParent = new JFrame(); + //dummyParent.setSize(400, 400); + //dummyParent.setLocationRelativeTo(null); + //dummyParent.setVisible(true); + + //showErrorDialog(dummyParent); + //} +} diff --git a/Neul-main/src/main/java/RandomSleepInformation.java b/Neul-main/src/main/java/RandomSleepInformation.java new file mode 100644 index 0000000..e06d10c --- /dev/null +++ b/Neul-main/src/main/java/RandomSleepInformation.java @@ -0,0 +1,41 @@ +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class RandomSleepInformation { + private final String[] goodThings = { + "규칙적인 수면 시간", "자기 전 명상", "따뜻한 우유 한 잔", + "카페인과 니코틴 피하기", "낮에 햇빛 충분히 쬐기" + }; + + private final String[] badThings = { + "카페인 음료", "알코올 섭취", "취침 전 과한 운동", + "불규칙한 수면 패턴", "전자기기의 남용", "스트레스와 불안", "신체 활동량 부족" + }; + + private final String[] sleepWords = { + "잘 자야 마음도 편해져요.", "꿀잠은 좋은 감정의 시작!", "잠은 마음의 휴식 시간이에요.", + "잠이 부족하면 감정도 불안해져요.", "마음 편히 자면 감정도 안정돼요.", + "잘 자고 좋은 기분 만들기!", "좋은 잠이 좋은 하루를 만듭니다." + }; + + // 랜덤으로 좋은 요인 3개 + public List getRandomGoodThings() { + List list = new ArrayList<>(List.of(goodThings)); + Collections.shuffle(list); + return list.subList(0, Math.min(3, list.size())); + } + + // 랜덤으로 나쁜 요인 3개 + public List getRandomBadThings() { + List list = new ArrayList<>(List.of(badThings)); + Collections.shuffle(list); + return list.subList(0, Math.min(3, list.size())); + } + + // 랜덤 문구 1개 + public String getRandomSleepWord() { + int index = (int) (Math.random() * sleepWords.length); + return sleepWords[index]; + } +} diff --git a/Neul-main/src/main/java/ScreenController.java b/Neul-main/src/main/java/ScreenController.java new file mode 100644 index 0000000..4d8ae0c --- /dev/null +++ b/Neul-main/src/main/java/ScreenController.java @@ -0,0 +1,38 @@ +import javax.swing.*; + +public class ScreenController { + private final PanelManager panelManager; + + public ScreenController(PanelManager panelManager) { + this.panelManager = panelManager; + } + + // 화면 전환 요청 메서드 + public void changeScreen(String screenName) { + panelManager.showScreen(screenName); + } + + // 예시: 수면 추천 패널 열기 + public void showSleepRecommendation(SleepTime sleepTime) { + SleepRecommendationPanel panel = new SleepRecommendationPanel(this, sleepTime); // ✅ 이 부분은 생성자 수정된 상태일 때만 사용 + panelManager.addScreen("sleepRecommendation", panel); + changeScreen("sleepRecommendation"); + } + + // 예시: 기상 추천 패널 열기 + public void showWakeRecommendation(WakeTime wakeTime) { + WakeRecommendationPanel panel = new WakeRecommendationPanel(this, wakeTime); // ✅ 이 부분도 생성자 수정된 상태일 때만 사용 + panelManager.addScreen("wakeRecommendation", panel); + changeScreen("wakeRecommendation"); + } + + // 🔧 오류 해결된 부분: CurrentDateTime 기반 SleepCyclePanel + public void showSleepCycle() { + CurrentDateTime currentDateTime = new CurrentDateTime(); // ✅ 이 줄 추가 + SleepCyclePanel panel = new SleepCyclePanel(currentDateTime); // ✅ 고친 부분 + panelManager.addScreen("sleepCycle", panel); + changeScreen("sleepCycle"); + } + + // 필요시 다른 화면도 여기서 제어 가능 +} diff --git a/Neul-main/src/main/java/SignupPanel.java b/Neul-main/src/main/java/SignupPanel.java new file mode 100644 index 0000000..134a46d --- /dev/null +++ b/Neul-main/src/main/java/SignupPanel.java @@ -0,0 +1,110 @@ +import javax.swing.*; +import java.awt.*; + +public class SignupPanel extends JPanel { + private ScreenController controller; + + public SignupPanel(ScreenController controller) { + this.controller = controller; + + setLayout(null); + setBackground(new Color(91, 89, 153)); // 짙은 보라 배경 + + // NEUL 텍스트 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 60)); + title.setForeground(new Color(182, 153, 40)); // 금색 + title.setBounds(620, 60, 200, 80); + add(title); + + // 달 원 컴포넌트 + MoonCircle moon = new MoonCircle(); + moon.setBounds(800, 70, 50, 50); + add(moon); + + // "회원가입" 타이틀 + JLabel registerLabel = new JLabel("회원가입", SwingConstants.CENTER); + registerLabel.setFont(new Font("SansSerif", Font.BOLD, 36)); + registerLabel.setBounds(620, 180, 200, 50); + add(registerLabel); + + // 회원가입 폼 박스 + JPanel formPanel = new JPanel(); + formPanel.setLayout(null); + formPanel.setBackground(Color.WHITE); + formPanel.setBounds(470, 290, 500, 500); + formPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1, true)); + add(formPanel); + + // 아이디 + JLabel idLabel = new JLabel("아이디"); + idLabel.setBounds(60, 40, 100, 30); + JTextField idField = new JTextField(); + idField.setBounds(60, 70, 380, 35); + + // 비밀번호 + JLabel pwLabel = new JLabel("비밀번호"); + pwLabel.setBounds(60, 130, 100, 30); + JPasswordField pwField = new JPasswordField(); + pwField.setBounds(60, 160, 380, 35); + + // 비밀번호 확인 + JLabel pwConfirmLabel = new JLabel("비밀번호 확인"); + pwConfirmLabel.setBounds(60, 220, 150, 30); + JPasswordField pwConfirmField = new JPasswordField(); + pwConfirmField.setBounds(60, 250, 380, 35); + + // 가입하기 버튼 + JButton registerBtn = new JButton("가입하기"); + registerBtn.setBounds(60, 330, 380, 40); + registerBtn.setBackground(new Color(40, 40, 40)); + registerBtn.setForeground(Color.WHITE); + + // 👉 회원가입 버튼 클릭 이벤트 + registerBtn.addActionListener(e -> { + String id = idField.getText(); + String pw = new String(pwField.getPassword()); + String pwConfirm = new String(pwConfirmField.getPassword()); + + if (!pw.equals(pwConfirm)) { + JOptionPane.showMessageDialog(this, "비밀번호가 일치하지 않습니다."); + return; + } + + // 실제 회원가입 처리 로직은 여기에 추가할 수 있음 + JOptionPane.showMessageDialog(this, "회원가입 완료!"); + + // 현재 창 닫기 + Window window = SwingUtilities.getWindowAncestor(this); + if (window != null) { + window.dispose(); + } + + // 로그인 화면으로 전환 (새 JFrame 말고 카드 전환이 좋지만, 현재 구조 유지) + JFrame loginFrame = new JFrame("로그인"); + loginFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + loginFrame.setSize(1440, 1024); + loginFrame.setLocationRelativeTo(null); + loginFrame.setContentPane(new LoginPanel(controller)); // ✅ controller 전달 + loginFrame.setVisible(true); + }); + + formPanel.add(idLabel); + formPanel.add(idField); + formPanel.add(pwLabel); + formPanel.add(pwField); + formPanel.add(pwConfirmLabel); + formPanel.add(pwConfirmField); + formPanel.add(registerBtn); + } + + // 테스트용 실행 + // public static void main(String[] args) { + // JFrame frame = new JFrame("Register - NEUL"); + // frame.setSize(1440, 1024); + // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // frame.setLocationRelativeTo(null); + // frame.add(new SignupPanel(null)); // 테스트 시 null 전달 + // frame.setVisible(true); + // } +} diff --git a/Neul-main/src/main/java/SleepCalculator.java b/Neul-main/src/main/java/SleepCalculator.java new file mode 100644 index 0000000..cce0434 --- /dev/null +++ b/Neul-main/src/main/java/SleepCalculator.java @@ -0,0 +1,37 @@ +import java.time.Duration; +import java.util.List; + +public class SleepCalculator { + + /** + * 수면 시간 목록(Duration 리스트)을 받아서 평균 수면 시간을 계산합니다. + * + * @param durations 수면 시간 리스트 + * @return 평균 수면 시간 (Duration 형태), 비어있으면 Duration.ZERO 반환 + */ + public static Duration calculateAverage(List durations) { + if (durations == null || durations.isEmpty()) { + return Duration.ZERO; + } + + long totalSeconds = 0; + for (Duration d : durations) { + totalSeconds += d.getSeconds(); + } + + long averageSeconds = totalSeconds / durations.size(); + return Duration.ofSeconds(averageSeconds); + } + + /** + * Duration을 사람이 읽기 좋은 문자열로 변환합니다. (예: "7시간 30분") + * + * @param duration Duration 객체 + * @return 포맷된 문자열 + */ + public static String formatDuration(Duration duration) { + long hours = duration.toHours(); + long minutes = duration.toMinutesPart(); // Java 9 이상에서 사용 가능 + return hours + "시간 " + minutes + "분"; + } +} diff --git a/Neul-main/src/main/java/SleepCyclePanel.java b/Neul-main/src/main/java/SleepCyclePanel.java new file mode 100644 index 0000000..e187255 --- /dev/null +++ b/Neul-main/src/main/java/SleepCyclePanel.java @@ -0,0 +1,140 @@ + +import javax.swing.*; +import java.awt.*; +import java.util.List; +import java.util.Objects; +import java.awt.event.ActionListener; +import java.awt.event.ActionEvent; + + + +public class SleepCyclePanel extends JPanel { + + public SleepCyclePanel(CurrentDateTime backend) { + setLayout(null); + setBackground(new Color(91, 89, 153)); + + // 백엔드에서 시간 받아오기 + String currentTime = backend.getCurrentTime(); + List recommendedTimes = backend.recommendWakeTimes(); + + // NEUL 제목 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 80)); + title.setForeground(Color.WHITE); + title.setBounds(640, 50, 300, 100); + add(title); + + // 홈 버튼 + System.out.println("이미지 경로: " + getClass().getResource("/res/images/img.png")); + + + // 홈 버튼 + ImageIcon imgIcon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/res/images/img.png"))); + Image scaled = imgIcon.getImage().getScaledInstance(48, 48, Image.SCALE_SMOOTH); + ImageIcon resizedIcon = new ImageIcon(scaled); + + JButton imgButton = new JButton(resizedIcon); + imgButton.setBounds(1360, 20, 48, 48); + imgButton.setBorderPainted(false); + imgButton.setContentAreaFilled(false); + imgButton.setFocusPainted(false); + imgButton.setOpaque(false); + add(imgButton); + + // 🟡 홈 버튼 클릭 시 메인 메뉴로 이동 + imgButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Window window = SwingUtilities.getWindowAncestor(SleepCyclePanel.this); + if (window instanceof JFrame) { + JFrame frame = (JFrame) window; + frame.setContentPane(new MainMenuPanel()); // 메인 메뉴 패널로 전환 + frame.revalidate(); // 새로고침 + frame.repaint(); // 다시 그리기 + } + } + }); + + + + // 현재 시간 패널 + JPanel currentTimePanel = new JPanel(new BorderLayout()); + currentTimePanel.setBackground(Color.WHITE); + currentTimePanel.setBounds(640, 160, 200, 50); + JLabel timeText = new JLabel(currentTime, SwingConstants.CENTER); + timeText.setFont(new Font("Digital-7 Mono", Font.BOLD, 28)); + currentTimePanel.add(timeText, BorderLayout.CENTER); + add(currentTimePanel); + + // 안내 문구 + JLabel desc = new JLabel("
You should wake up at:
(1 cycle = 1.5hrs)
", SwingConstants.CENTER); + desc.setFont(new Font("SansSerif", Font.PLAIN, 18)); + desc.setForeground(Color.WHITE); + desc.setBounds(600, 250, 300, 60); + add(desc); + + // 수면 사이클 패널 + int x = 400, y = 380; + for (int i = 0; i < 6; i++) { + JPanel cyclePanel = new JPanel(); + cyclePanel.setLayout(new BoxLayout(cyclePanel, BoxLayout.Y_AXIS)); + cyclePanel.setBackground(Color.WHITE); + cyclePanel.setBounds(x, y, 200, 120); + + JLabel cycleLabel = new JLabel((i + 1) + " cycle" + (i == 0 ? "" : "s"), SwingConstants.CENTER); + JLabel hourLabel = new JLabel((1.5 * (i + 1)) + " hrs", SwingConstants.CENTER); + + cycleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + hourLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // 디지털 시계 스타일 시간 박스 + JPanel digitalPanel = new JPanel(); + digitalPanel.setBackground(Color.WHITE); + digitalPanel.setMaximumSize(new Dimension(160, 40)); + JLabel timeLabel = new JLabel(recommendedTimes.get(i), SwingConstants.CENTER); + timeLabel.setFont(new Font("Digital-7 Mono", Font.BOLD, 20)); + timeLabel.setForeground(new Color(66, 133, 244)); + digitalPanel.add(timeLabel); + + digitalPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + cyclePanel.add(Box.createVerticalStrut(10)); + cyclePanel.add(cycleLabel); + cyclePanel.add(hourLabel); + cyclePanel.add(Box.createVerticalStrut(5)); + cyclePanel.add(digitalPanel); + + Color borderColor = (i < 2) ? Color.RED : (i < 4) ? Color.YELLOW : Color.GREEN; + cyclePanel.setBorder(BorderFactory.createLineBorder(borderColor, 3)); + add(cyclePanel); + + x += 240; + if (i == 2) { + x = 400; + y += 150; + } + } + + // 하단 추천 문구 + JLabel bottomText = new JLabel("We recommend to sleep for 5–6 cycles!!", SwingConstants.CENTER); + bottomText.setFont(new Font("SansSerif", Font.BOLD, 20)); + bottomText.setForeground(Color.WHITE); + bottomText.setBounds(440, 700, 600, 50); + add(bottomText); + } + // 테스트용 메인 + // public static void main(String[] args) { + //JFrame frame = new JFrame("Sleep Cycle Recommendation"); + //frame.setSize(1440, 1024); + //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + //frame.setLocationRelativeTo(null); + + // 백엔드 객체 생성 + //CurrentDateTime backend = new CurrentDateTime(); + //SleepCyclePanel panel = new SleepCyclePanel(backend); + + //frame.add(new SleepCyclePanel(backend)); + //frame.setVisible(true); + //} +} \ No newline at end of file diff --git a/Neul-main/src/main/java/SleepDataService.java b/Neul-main/src/main/java/SleepDataService.java new file mode 100644 index 0000000..7b5b0ce --- /dev/null +++ b/Neul-main/src/main/java/SleepDataService.java @@ -0,0 +1,126 @@ +// 각 클래스에서 필요한 데이터를 저장/조회하는 클래스 + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.databind.SerializationFeature; + +import java.io.File; +import java.io.IOException; +import java.time.Duration; +import java.time.LocalDate; +import java.util.*; + +public class SleepDataService { + + private static final String DATA_FILE = "sleep_data.json"; + private Map> userSleepMap; + private ObjectMapper objectMapper; + + public SleepDataService() { + objectMapper = new ObjectMapper(); + objectMapper.registerModule(new JavaTimeModule()); + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + loadData(); + } + + // JSON 파일에서 사용자별 수면 기록 로드 + private void loadData() { + try { + File file = new File(DATA_FILE); + if (file.exists()) { + userSleepMap = objectMapper.readValue(file, + new TypeReference>>() {}); + } else { + userSleepMap = new HashMap<>(); + } + } catch (IOException e) { + e.printStackTrace(); + userSleepMap = new HashMap<>(); + } + } + + // 수면 기록을 JSON 파일에 저장 + private void saveData() { + try { + objectMapper + .writerWithDefaultPrettyPrinter() + .writeValue(new File(DATA_FILE), userSleepMap); + } catch (IOException e) { + e.printStackTrace(); + } + } + + // 오늘 날짜로 수면기록 저장 (중복 날짜는 덮어쓰기) + public void saveRecord(String userId, SleepRecord record) { + List records = userSleepMap.computeIfAbsent(userId, k -> new ArrayList<>()); + records.removeIf(r -> r.getDate().equals(record.getDate())); + records.add(record); + saveData(); + } + + // SleepRecord에서 특정 날짜 기록 조회 -> SleepRecord 클래스 전체로 반환 (RecordPanel 팝업용) + public SleepRecord getRecordByDate(String userId, LocalDate date) { + List records = userSleepMap.getOrDefault(userId, new ArrayList<>()); + return records.stream() + .filter(r -> r.getDate().equals(date)) + .findFirst() + .orElse(null); + } + + // SleepRecord에서 수면 시간 조회 -> sleepDuration 리스트 형태로 반환 (SleepCalculation 평균 수면 시간 계산 용) + public List getLast7DaysSleepDurations(String userId) { + List records = userSleepMap.getOrDefault(userId, new ArrayList<>()); + LocalDate now = LocalDate.now(); + List durations = new ArrayList<>(); + + records.stream() + .filter(r -> !r.getDate().isAfter(now) && !r.getDate().isBefore(now.minusDays(6))) + .sorted(Comparator.comparing(SleepRecord::getDate)) + .forEach(r -> durations.add(r.getSleepDuration())); + + return durations; + } + + // 평균 계산 + public Duration getWeeklyAverage(String userId) { + List durations = getLast7DaysSleepDurations(userId); // 기존 메서드 재사용! + return SleepCalculator.calculateAverage(durations); + } + + // SleepRecord에서 수면 시간 조회 -> 그래프용 요일-수면시간 Map 형태로 반환 (AnalysisPanel 수면시간 그래프 용) + public Map getWeeklySleepGraph(String userId) { + Map graphData = new LinkedHashMap<>(); + List records = userSleepMap.getOrDefault(userId, new ArrayList<>()); + LocalDate now = LocalDate.now(); + + for (int i = 0; i < 7; i++) { + LocalDate targetDate = now.minusDays(6 - i); + String day = targetDate.getDayOfWeek().toString().substring(0, 3); // "MON", "TUE", ... + Duration duration = records.stream() + .filter(r -> r.getDate().equals(targetDate)) + .map(SleepRecord::getSleepDuration) + .findFirst() + .orElse(Duration.ZERO); + graphData.put(day, duration); + } + + return graphData; + } + + // SleepRrcord에서 수면 시간 조회 -> duration 객체로 반환 (SleepDataManager 수면의 질 계산 용) + public Duration getSleepDurationByDate(String userId, LocalDate date) { + SleepRecord r = getRecordByDate(userId, date); + return r != null ? r.getSleepDuration() : Duration.ZERO; + } + + public String getMoodByDate(String userId, LocalDate date) { + List records = userSleepMap.getOrDefault(userId, new ArrayList<>()); + for (SleepRecord record : records) { + if (record.getDate().equals(date)) { + return record.getMood(); + } + } + return null; + } +} diff --git a/Neul-main/src/main/java/SleepDatabase.java b/Neul-main/src/main/java/SleepDatabase.java new file mode 100644 index 0000000..b120126 --- /dev/null +++ b/Neul-main/src/main/java/SleepDatabase.java @@ -0,0 +1,51 @@ +// 사용자 정보가 앱 재시작 후에도 유지되도록 sleep_data.json을 관리하는 통합 클래스 + +import java.time.Duration; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + +public class SleepDatabase { + + private SleepDataService dataService; + + public SleepDatabase() { + this.dataService = new SleepDataService(); + } + + // 날짜 지정 기록 + public void addRecord(String userId, LocalDate date, Duration sleepDuration, String mood) { + SleepRecord record = new SleepRecord(date, sleepDuration, mood); + dataService.saveRecord(userId, record); + } + + // 오늘 날짜 자동 기록 (메서도 오버로딩) + public void addRecord(String userId, Duration sleepDuration, String mood) { + addRecord(userId, LocalDate.now(), sleepDuration, mood); + } + + // 특정 날짜 기록 조회 + public SleepRecord getRecordByDate(String userId, LocalDate date) { + return dataService.getRecordByDate(userId, date); + } + + // 최근 7일 수면 시간 리스트 반환 → SleepCalculator 용도 + public List getLast7DaysDurations(String userId) { + return dataService.getLast7DaysSleepDurations(userId); + } + + // 날짜별 수면 시간 → SleepDataManager 용도 + public Duration getDurationByDate(String userId, LocalDate date) { + return dataService.getSleepDurationByDate(userId, date); + } + + // 날짜별 감정 상태 → 이모티콘 표시용 + public String getMoodByDate(String userId, LocalDate date) { + return dataService.getMoodByDate(userId, date); + } + + // AnalysisPanel 그래프용 수면시간 데이터 + public Map getWeeklyGraph(String userId) { + return dataService.getWeeklySleepGraph(userId); + } +} diff --git a/Neul-main/src/main/java/SleepInputPanel.java b/Neul-main/src/main/java/SleepInputPanel.java new file mode 100644 index 0000000..55ee275 --- /dev/null +++ b/Neul-main/src/main/java/SleepInputPanel.java @@ -0,0 +1,229 @@ +import javax.swing.*; +import java.awt.*; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Timer; +import java.util.TimerTask; + +public class SleepInputPanel extends JPanel { + + private JComboBox sleepHourCombo; + private JComboBox sleepMinuteCombo; + private AnalogClock clock; + private final JLabel digitalClockLabel; + private Timer timer; + private ScreenController controller; + + public SleepInputPanel(ScreenController controller) { + this.controller = controller; + setLayout(null); + setBackground(new Color(91, 89, 153)); + + // NEUL 제목 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 80)); + title.setForeground(Color.WHITE); + title.setBounds(620, 40, 300, 100); + add(title); + + // 홈 버튼 + try { + ImageIcon homeIcon = new ImageIcon(getClass().getResource("/res/images/img.png")); + Image scaled = homeIcon.getImage().getScaledInstance(48, 48, Image.SCALE_SMOOTH); + ImageIcon resizedIcon = new ImageIcon(scaled); + JButton homeButton = new JButton(resizedIcon); + homeButton.setBounds(1360, 20, 48, 48); + homeButton.setBorderPainted(false); + homeButton.setContentAreaFilled(false); + homeButton.setFocusPainted(false); + homeButton.setOpaque(false); + add(homeButton); + + homeButton.addActionListener(e -> { + Window window = SwingUtilities.getWindowAncestor(SleepInputPanel.this); + if (window instanceof JFrame frame) { + frame.setContentPane(new MainMenuPanel()); + frame.revalidate(); + frame.repaint(); + } + }); + } catch (Exception e) { + System.err.println("홈 버튼 이미지 로드 실패: " + e.getMessage()); + } + + String[] hours = new String[24]; + for (int i = 0; i < 24; i++) hours[i] = String.valueOf(i); + String[] minutes = new String[60]; + for (int i = 0; i < 60; i++) minutes[i] = String.format("%02d", i); + + // 수면 입력 패널 (왼쪽) + JPanel sleepAtPanel = new JPanel(); + sleepAtPanel.setLayout(null); + sleepAtPanel.setBackground(Color.WHITE); + sleepAtPanel.setBounds(230, 180, 320, 600); + + JLabel sleepLabel = new JLabel("I will sleep at..", SwingConstants.CENTER); + sleepLabel.setFont(new Font("SansSerif", Font.BOLD, 20)); + sleepLabel.setBounds(30, 20, 260, 40); + sleepAtPanel.add(sleepLabel); + + sleepHourCombo = new JComboBox<>(hours); + sleepHourCombo.setBounds(40, 200, 100, 40); + sleepAtPanel.add(sleepHourCombo); + + sleepMinuteCombo = new JComboBox<>(minutes); + sleepMinuteCombo.setBounds(160, 200, 100, 40); + sleepAtPanel.add(sleepMinuteCombo); + + JButton sleepNextBtn = new JButton("▶ NEXT"); + sleepNextBtn.setBounds(110, 500, 100, 40); + sleepNextBtn.setFont(new Font("SansSerif", Font.PLAIN, 14)); + sleepAtPanel.add(sleepNextBtn); + + sleepNextBtn.addActionListener(e -> { + String hour = (String) sleepHourCombo.getSelectedItem(); + String minute = (String) sleepMinuteCombo.getSelectedItem(); + String timeStr = String.format("%02d:%02d", Integer.parseInt(hour), Integer.parseInt(minute)); + SleepTime sleepTime = new SleepTime(timeStr); + + Window window = SwingUtilities.getWindowAncestor(SleepInputPanel.this); + if (window instanceof JFrame frame) { + frame.setContentPane(new SleepRecommendationPanel(sleepTime)); + frame.revalidate(); + frame.repaint(); + } + }); + add(sleepAtPanel); + + // wake up at 패널 (중간) + JPanel wakeUpPanel = new JPanel(); + wakeUpPanel.setLayout(null); + wakeUpPanel.setBackground(Color.WHITE); + wakeUpPanel.setBounds(580, 180, 320, 600); + + JLabel wakeLabel = new JLabel("I will wake up at..", SwingConstants.CENTER); + wakeLabel.setFont(new Font("SansSerif", Font.BOLD, 20)); + wakeLabel.setBounds(30, 20, 260, 40); + wakeUpPanel.add(wakeLabel); + + JComboBox wakeHourCombo = new JComboBox<>(hours); + wakeHourCombo.setBounds(40, 200, 100, 40); + wakeUpPanel.add(wakeHourCombo); + + JComboBox wakeMinuteCombo = new JComboBox<>(minutes); + wakeMinuteCombo.setBounds(160, 200, 100, 40); + wakeUpPanel.add(wakeMinuteCombo); + + JButton wakeNextBtn = new JButton("▶ NEXT"); + wakeNextBtn.setBounds(110, 500, 100, 40); + wakeNextBtn.setFont(new Font("SansSerif", Font.PLAIN, 14)); + wakeUpPanel.add(wakeNextBtn); + + wakeNextBtn.addActionListener(e -> { + String hour = (String) wakeHourCombo.getSelectedItem(); + String minute = (String) wakeMinuteCombo.getSelectedItem(); + String timeStr = String.format("%02d:%02d", Integer.parseInt(hour), Integer.parseInt(minute)); + WakeTime wakeTime = new WakeTime(timeStr); + + Window window = SwingUtilities.getWindowAncestor(SleepInputPanel.this); + if (window instanceof JFrame frame) { + frame.setContentPane(new WakeRecommendationPanel(wakeTime)); + frame.revalidate(); + frame.repaint(); + } + }); + add(wakeUpPanel); + + // 시계 패널 (오른쪽) + JPanel clockPanel = new JPanel(); + clockPanel.setLayout(null); + clockPanel.setBackground(Color.WHITE); + clockPanel.setBounds(930, 180, 320, 600); + + JLabel clockLabel = new JLabel("Sleep now", SwingConstants.CENTER); + clockLabel.setFont(new Font("SansSerif", Font.BOLD, 20)); + clockLabel.setBounds(30, 20, 260, 40); + clockPanel.add(clockLabel); + + JPanel clockContainer = new JPanel(new BorderLayout()); + clockContainer.setOpaque(false); + clockContainer.setBounds(10, 70, 300, 400); + + clock = new AnalogClock(); + digitalClockLabel = new JLabel("", SwingConstants.CENTER); + digitalClockLabel.setFont(new Font("SansSerif", Font.BOLD, 20)); + digitalClockLabel.setForeground(Color.BLACK); + + clockContainer.add(clock, BorderLayout.CENTER); + clockContainer.add(digitalClockLabel, BorderLayout.SOUTH); + clockPanel.add(clockContainer); + + JButton playNow = new JButton("▶ NEXT"); + playNow.setBounds(110, 500, 100, 40); + playNow.setFont(new Font("SansSerif", Font.PLAIN, 14)); + clockPanel.add(playNow); + + playNow.addActionListener(e -> { + CurrentDateTime currentTimeBackend = new CurrentDateTime(); + Window window = SwingUtilities.getWindowAncestor(SleepInputPanel.this); + if (window instanceof JFrame frame) { + frame.setContentPane(new SleepCyclePanel(currentTimeBackend)); + frame.revalidate(); + frame.repaint(); + } + }); + + add(clockPanel); + startClock(); + } + + private void startClock() { + timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + SwingUtilities.invokeLater(() -> { + clock.repaint(); + digitalClockLabel.setText(LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm"))); + }); + } + }, 0, 1000); + } + + static class AnalogClock extends JPanel { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + int width = getWidth(); + int height = getHeight(); + int radius = Math.min(width, height) / 2 - 10; + int centerX = width / 2; + int centerY = height / 2; + + g2.setColor(Color.WHITE); + g2.fillRect(0, 0, width, height); + + g2.setColor(Color.BLACK); + g2.drawOval(centerX - radius, centerY - radius, radius * 2, radius * 2); + + LocalTime time = LocalTime.now(); + double minute = time.getMinute() * 6; + double hour = (time.getHour() % 12 + time.getMinute() / 60.0) * 30; + + drawHand(g2, centerX, centerY, hour, radius * 0.5f, 6); + drawHand(g2, centerX, centerY, minute, radius * 0.75f, 4); + } + + private void drawHand(Graphics2D g2, int cx, int cy, double angle, double length, int thickness) { + double rad = Math.toRadians(angle - 90); + int x = (int) (cx + length * Math.cos(rad)); + int y = (int) (cy + length * Math.sin(rad)); + g2.setStroke(new BasicStroke(thickness)); + g2.drawLine(cx, cy, x, y); + } + } + +} diff --git a/Neul-main/src/main/java/SleepRecommendationPanel.java b/Neul-main/src/main/java/SleepRecommendationPanel.java new file mode 100644 index 0000000..59fca85 --- /dev/null +++ b/Neul-main/src/main/java/SleepRecommendationPanel.java @@ -0,0 +1,139 @@ +import javax.swing.*; +import java.awt.*; +import java.text.MessageFormat; +import java.util.List; +import java.util.Objects; + +public class SleepRecommendationPanel extends JPanel { + private ScreenController controller; + + public SleepRecommendationPanel(ScreenController controller, SleepTime sleepTime) { + this.controller = controller; + setLayout(null); + setBackground(new Color(91, 89, 153)); + + // 백엔드에서 시간 받아오기 + String currentTime = sleepTime.getSleepTime().format(SleepTime.FORMATTER); + List recommendedTimes = sleepTime.recommendWakeTimes(); + + // NEUL 제목 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 80)); + title.setForeground(Color.WHITE); + title.setBounds(640, 50, 300, 100); + add(title); + + // 홈 버튼 + try { + // 정확한 리소스 경로 사용 + System.out.println("이미지 경로: " + getClass().getResource("/res/images/img.png")); // ✅ 경로 확인용 로그 + + ImageIcon imgIcon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/res/images/img.png"))); + Image scaled = imgIcon.getImage().getScaledInstance(48, 48, Image.SCALE_SMOOTH); + ImageIcon resizedIcon = new ImageIcon(scaled); + + JButton imgButton = new JButton(resizedIcon); + imgButton.setBounds(1360, 20, 48, 48); + imgButton.setBorderPainted(false); + imgButton.setContentAreaFilled(false); + imgButton.setFocusPainted(false); + imgButton.setOpaque(false); + add(imgButton); + + // 홈 버튼 클릭 시 MainMenuPanel로 이동 + imgButton.addActionListener(e -> { + Window window = SwingUtilities.getWindowAncestor(SleepRecommendationPanel.this); + if (window instanceof JFrame) { + JFrame frame = (JFrame) window; + frame.setContentPane(new MainMenuPanel()); + frame.revalidate(); + frame.repaint(); + } + }); + + } catch (Exception e) { + System.err.println("이미지 로드 실패: " + e.getMessage()); + } + + + // 현재 시간 패널 + JPanel currentTimePanel = new JPanel(new BorderLayout()); + currentTimePanel.setBackground(Color.WHITE); + currentTimePanel.setBounds(640, 160, 200, 50); + JLabel timeText = new JLabel(currentTime, SwingConstants.CENTER); + timeText.setFont(new Font("Digital-7 Mono", Font.BOLD, 28)); + currentTimePanel.add(timeText, BorderLayout.CENTER); + add(currentTimePanel); + + // 안내 문구 + JLabel desc = new JLabel("
You should wake up at:
(1 cycle = 1.5hrs)
", SwingConstants.CENTER); + desc.setFont(new Font("SansSerif", Font.PLAIN, 18)); + desc.setForeground(Color.WHITE); + desc.setBounds(600, 250, 300, 60); + add(desc); + + // 수면 사이클 패널 + int x = 400, y = 380; + for (int i = 0; i < 6; i++) { + JPanel cyclePanel = new JPanel(); + cyclePanel.setLayout(new BoxLayout(cyclePanel, BoxLayout.Y_AXIS)); + cyclePanel.setBackground(Color.WHITE); + cyclePanel.setBounds(x, y, 200, 120); + + JLabel cycleLabel = new JLabel((i + 1) + " cycle" + (i == 0 ? "" : "s"), SwingConstants.CENTER); + JLabel hourLabel = new JLabel((1.5 * (i + 1)) + " hrs", SwingConstants.CENTER); + + cycleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + hourLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // 디지털 시계 스타일 시간 박스 + JPanel digitalPanel = new JPanel(); + digitalPanel.setBackground(Color.WHITE); + digitalPanel.setMaximumSize(new Dimension(160, 40)); + JLabel timeLabel = new JLabel(recommendedTimes.get(i), SwingConstants.CENTER); + timeLabel.setFont(new Font("Digital-7 Mono", Font.BOLD, 20)); + timeLabel.setForeground(new Color(66, 133, 244)); + digitalPanel.add(timeLabel); + + digitalPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + cyclePanel.add(Box.createVerticalStrut(10)); + cyclePanel.add(cycleLabel); + cyclePanel.add(hourLabel); + cyclePanel.add(Box.createVerticalStrut(5)); + cyclePanel.add(digitalPanel); + + Color borderColor = (i < 2) ? Color.RED : (i < 4) ? Color.YELLOW : Color.GREEN; + cyclePanel.setBorder(BorderFactory.createLineBorder(borderColor, 3)); + add(cyclePanel); + + x += 240; + if (i == 2) { + x = 400; + y += 150; + } + } + + // 하단 추천 문구 + JLabel bottomText = new JLabel("We recommend to sleep for 5–6 cycles!!", SwingConstants.CENTER); + bottomText.setFont(new Font("SansSerif", Font.BOLD, 20)); + bottomText.setForeground(Color.WHITE); + bottomText.setBounds(440, 700, 600, 50); + add(bottomText); + } + + // 테스트용 메인 + //public static void main(String[] args) { + //JFrame frame = new JFrame("Sleep Cycle Recommendation"); + //frame.setSize(1440, 1024); + //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + //frame.setLocationRelativeTo(null); + + // 수면 시간 설정 예: "22:00" + //SleepTime sleepTime = new SleepTime("22:00"); + //SleepRecommendationPanel panel = new SleepRecommendationPanel(); + + //frame.add(panel); + //frame.setVisible(true); + //} +} diff --git a/Neul-main/src/main/java/SleepRecord.java b/Neul-main/src/main/java/SleepRecord.java new file mode 100644 index 0000000..d57e85e --- /dev/null +++ b/Neul-main/src/main/java/SleepRecord.java @@ -0,0 +1,90 @@ +// 사용자가 TodayPanel에서 입력한 수면 정보와 감정을 날짜에 따라 저장하는 클래스 + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.time.Duration; +import java.time.LocalDate; + +public class SleepRecord { + private LocalDate date; + private Duration sleepDuration; + private String mood; + + public SleepRecord() {} // 기본 생성자 (필수) + + // 오늘 날짜로 기록 + public SleepRecord(Duration sleepDuration, String mood) { + this(LocalDate.now(), sleepDuration, mood); + } + + // 날짜 지정해서 기록 + public SleepRecord(LocalDate date, Duration sleepDuration, String mood) { + this.date = date; + this.sleepDuration = sleepDuration; + this.mood = mood; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + // 내부 로직에서 사용할 Duration 객체 반환용 + @JsonIgnore + public Duration getSleepDuration() { + return sleepDuration; + } + + // 문자열 → Duration 역변환용 + @JsonProperty("sleepDuration") + public void setSleepDurationRaw(Duration duration) { + this.sleepDuration = duration; + } + + // 사용자에게 보여줄 문자열 반환용 + @JsonProperty("sleepDuration") + public String getSleepDurationFormatted() { + if (sleepDuration == null) return ""; + long hours = sleepDuration.toHours(); + long minutes = sleepDuration.toMinutesPart(); + return String.format("%d시간 %d분", hours, minutes); + } + + @JsonProperty("sleepDuration") + public void setSleepDurationFormatted(String durationStr) { + try { + // 예: "2시간 30분" → 시간과 분 추출 + long hours = 0; + long minutes = 0; + if (durationStr.contains("시간")) { + String[] split = durationStr.split("시간"); + hours = Long.parseLong(split[0].trim()); + if (split.length > 1 && split[1].contains("분")) { + minutes = Long.parseLong(split[1].replace("분", "").trim()); + } + } else if (durationStr.contains("분")) { + minutes = Long.parseLong(durationStr.replace("분", "").trim()); + } + this.sleepDuration = Duration.ofHours(hours).plusMinutes(minutes); + } catch (Exception e) { + // 파싱 실패 시 기본값 설정 (혹은 null 처리) + this.sleepDuration = Duration.ZERO; + } + } + + public String getMood() { + return mood; + } + + public void setMood(String mood) { + this.mood = mood; + } + + @Override + public String toString() { + return date + " | " + getSleepDurationFormatted() + " | 감정: " + mood; + } +} diff --git a/Neul-main/src/main/java/SleepTime.java b/Neul-main/src/main/java/SleepTime.java new file mode 100644 index 0000000..f2c50de --- /dev/null +++ b/Neul-main/src/main/java/SleepTime.java @@ -0,0 +1,30 @@ +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +public class SleepTime { + private LocalTime sleepTime; + private static final int CYCLE_MINUTES = 90; + private static final int MAX_CYCLES = 6; + public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + + public SleepTime(String timeStr) { + this.sleepTime = LocalTime.parse(timeStr, FORMATTER); + } + + // 1.5시간 단위로 기상 시간 추천 + public List recommendWakeTimes() { + List wakeTimes = new ArrayList<>(); + for (int i = 1; i <= MAX_CYCLES; i++) { + LocalTime wakeTime = sleepTime.plusMinutes(i * CYCLE_MINUTES); + wakeTimes.add(wakeTime.format(FORMATTER)); + } + return wakeTimes; + } + + public LocalTime getSleepTime() { + return sleepTime; + } +} + diff --git a/Neul-main/src/main/java/TodayPanel.java b/Neul-main/src/main/java/TodayPanel.java new file mode 100644 index 0000000..3bcc4a4 --- /dev/null +++ b/Neul-main/src/main/java/TodayPanel.java @@ -0,0 +1,213 @@ +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.time.*; + +public class TodayPanel extends JPanel { + private ScreenController controller; + private final User user; + + public TodayPanel(ScreenController controller) { + this.controller = controller; + + this.user = AuthManager.getCurrentUser(); + + if (this.user == null) { + JOptionPane.showMessageDialog(this, "로그인 정보가 없습니다. 다시 로그인해주세요."); + controller.navigateTo("login"); // 또는 적절한 초기 화면 + return; + } + + setBackground(Color.decode("#565686")); + setLayout(null); + + JLabel titleLabel = new JLabel("NEUL", SwingConstants.CENTER); + titleLabel.setFont(new Font("Serif", Font.PLAIN, 80)); + titleLabel.setForeground(Color.WHITE); + titleLabel.setBounds(0, 50, 1440, 80); + add(titleLabel); + + ImageIcon originalIcon = new ImageIcon(getClass().getResource("/images/img.png")); + Image resizedImage = originalIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); + ImageIcon resizedIcon = new ImageIcon(resizedImage); + + JButton homeButton = new JButton(resizedIcon); + homeButton.setBounds(1375, 30, 32, 32); // 오른쪽 상단에 딱 맞게 + homeButton.setContentAreaFilled(false); + homeButton.setBorderPainted(false); + homeButton.setFocusPainted(false); + homeButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + homeButton.addActionListener(e -> { + controller.navigateTo("main"); + }); + add(homeButton); + + JPanel sleepPanel = new JPanel(null); + sleepPanel.setBounds(100, 200, 600, 550); + sleepPanel.setBackground(Color.WHITE); + sleepPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); + add(sleepPanel); + + JLabel sleepLabel = new JLabel("오늘의 수면시간 입력", SwingConstants.CENTER); + sleepLabel.setBounds(150, 70, 300, 50); + sleepLabel.setFont(new Font("SansSerif", Font.PLAIN, 30)); + sleepPanel.add(sleepLabel); + + SpinnerNumberModel hourModel = new SpinnerNumberModel(8, 0, 23, 1); + JSpinner hourSpinner = new JSpinner(hourModel); + hourSpinner.setFont(new Font("SansSerif", Font.BOLD, 24)); + hourSpinner.setBounds(150, 200, 100, 60); + ((JSpinner.DefaultEditor) hourSpinner.getEditor()).getTextField().setEditable(false); + sleepPanel.add(hourSpinner); + + JLabel colon = new JLabel(":", SwingConstants.CENTER); + colon.setFont(new Font("SansSerif", Font.BOLD, 28)); + colon.setBounds(260, 200, 20, 60); + sleepPanel.add(colon); + + SpinnerNumberModel minModel = new SpinnerNumberModel(0, 0, 59, 1); + JSpinner minSpinner = new JSpinner(minModel); + minSpinner.setFont(new Font("SansSerif", Font.BOLD, 24)); + minSpinner.setBounds(290, 200, 100, 60); + ((JSpinner.DefaultEditor) minSpinner.getEditor()).getTextField().setEditable(false); + sleepPanel.add(minSpinner); + + JLabel hourLabel = new JLabel("Hour"); + hourLabel.setBounds(180, 270, 50, 20); + sleepPanel.add(hourLabel); + + JLabel minLabel = new JLabel("Minute"); + minLabel.setBounds(325, 270, 60, 20); + sleepPanel.add(minLabel); + + JPanel moodPanel = new JPanel(null); + moodPanel.setBounds(740, 200, 600, 550); + moodPanel.setBackground(Color.WHITE); + moodPanel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); + add(moodPanel); + + JLabel moodLabel = new JLabel("오늘의 기분", SwingConstants.CENTER); + moodLabel.setBounds(200, 70, 200, 30); + moodLabel.setFont(new Font("SansSerif", Font.PLAIN, 30)); + moodPanel.add(moodLabel); + + ImageIcon goodIcon = resizeIcon("images/Emotion.png"); + ImageIcon normalIcon = resizeIcon("images/Emotion (1).png"); + ImageIcon badIcon = resizeIcon("images/Emotion (2).png"); + + JToggleButton goodBtn = new JToggleButton(goodIcon); + JToggleButton normalBtn = new JToggleButton(normalIcon); + JToggleButton badBtn = new JToggleButton(badIcon); + + goodBtn.setBounds(50, 200, 140, 140); + normalBtn.setBounds(230, 200, 140, 140); + badBtn.setBounds(410, 200, 140, 140); + + Font moodFont = new Font("SansSerif", Font.BOLD, 18); + JLabel goodLabel = new JLabel("좋음", SwingConstants.CENTER); + goodLabel.setBounds(50, 350, 140, 30); + goodLabel.setFont(moodFont); + + JLabel normalLabel = new JLabel("보통", SwingConstants.CENTER); + normalLabel.setBounds(230, 350, 140, 30); + normalLabel.setFont(moodFont); + + JLabel badLabel = new JLabel("나쁨", SwingConstants.CENTER); + badLabel.setBounds(410, 350, 140, 30); + badLabel.setFont(moodFont); + + ButtonGroup moodGroup = new ButtonGroup(); + moodGroup.add(goodBtn); + moodGroup.add(normalBtn); + moodGroup.add(badBtn); + + for (JToggleButton btn : new JToggleButton[]{goodBtn, normalBtn, badBtn}) { + btn.setContentAreaFilled(false); + btn.setFocusPainted(false); + btn.setBorder(BorderFactory.createLineBorder(Color.GRAY, 3)); + } + + goodBtn.addActionListener(e -> { + goodBtn.setBorder(BorderFactory.createLineBorder(Color.GREEN, 3)); + normalBtn.setBorder(BorderFactory.createEmptyBorder()); + badBtn.setBorder(BorderFactory.createEmptyBorder()); + }); + + normalBtn.addActionListener(e -> { + goodBtn.setBorder(BorderFactory.createEmptyBorder()); + normalBtn.setBorder(BorderFactory.createLineBorder(Color.GREEN, 3)); + badBtn.setBorder(BorderFactory.createEmptyBorder()); + }); + + badBtn.addActionListener(e -> { + goodBtn.setBorder(BorderFactory.createEmptyBorder()); + normalBtn.setBorder(BorderFactory.createEmptyBorder()); + badBtn.setBorder(BorderFactory.createLineBorder(Color.GREEN, 3)); + }); + + moodPanel.add(goodBtn); + moodPanel.add(normalBtn); + moodPanel.add(badBtn); + moodPanel.add(goodLabel); + moodPanel.add(normalLabel); + moodPanel.add(badLabel); + + JButton saveBtn = new JButton("Save"); + saveBtn.setFont(new Font("SansSerif", Font.BOLD, 20)); + saveBtn.setBounds(637, 800, 166, 50); + saveBtn.setEnabled(true); + add(saveBtn); + + Action updateSaveState = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + boolean moodSelected = goodBtn.isSelected() || normalBtn.isSelected() || badBtn.isSelected(); + boolean timeValid = true; // 시/분은 항상 유효한 Spinner 값이라 true로 설정 + saveBtn.setEnabled(moodSelected && timeValid); + } + }; + + goodBtn.addActionListener(updateSaveState); + normalBtn.addActionListener(updateSaveState); + badBtn.addActionListener(updateSaveState); + + saveBtn.addActionListener((ActionEvent e) -> { + int hour = (int) hourSpinner.getValue(); + int minute = (int) minSpinner.getValue(); + Duration duration = Duration.ofHours(hour).plusMinutes(minute); + + String mood = goodBtn.isSelected() ? "좋음" : + normalBtn.isSelected() ? "보통" : + badBtn.isSelected() ? "나쁨" : null; + + if (mood == null) { + JOptionPane.showMessageDialog(this, "기분을 선택해주세요."); + return; + } + + SleepRecord record = new SleepRecord(duration, mood); + SleepDataService dataService = new SleepDataService(); + dataService.saveRecord(user.getUsername(), record); + + JOptionPane.showMessageDialog(this, "저장되었습니다!"); + }); + + setVisible(true); + } + + private ImageIcon resizeIcon(String filePath) { + java.net.URL imageUrl = getClass().getResource("/" + filePath); // 리소스 경로 불러오기 + if (imageUrl == null) { + System.err.println("이미지 로딩 실패: " + filePath); + return null; + } + + Image img = new ImageIcon(imageUrl).getImage().getScaledInstance(140, 140, Image.SCALE_SMOOTH); + return new ImageIcon(img); + } + + //public static void main(String[] args) { + //SleepDataService service = new SleepDataService(); + //SwingUtilities.invokeLater(() -> new TodayPanel()); + //} +} \ No newline at end of file diff --git a/Neul-main/src/main/java/User.java b/Neul-main/src/main/java/User.java new file mode 100644 index 0000000..d7608d6 --- /dev/null +++ b/Neul-main/src/main/java/User.java @@ -0,0 +1,26 @@ +// 사용자 정보 클래스 (Jackson 직렬화용 POJO) +public class User { + private String username; + private String password; // 해시된 비밀번호 저장 + + public User() {} // Jackson 역직렬화를 위한 기본 생성자 + + public User(String username, String password) { + this.username = username; + this.password = password; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + // 입력받은 비밀번호가 저장된 해시와 일치하는지 확인 + public boolean checkPassword(String inputPassword) { + String hashedInput = PasswordUtil.hashPassword(inputPassword); + return this.password.equals(hashedInput); + } +} diff --git a/Neul-main/src/main/java/UserService.java b/Neul-main/src/main/java/UserService.java new file mode 100644 index 0000000..4a44f32 --- /dev/null +++ b/Neul-main/src/main/java/UserService.java @@ -0,0 +1,51 @@ +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class UserService { + private static final UserService instance = new UserService(); + private final Map userMap = new HashMap<>(); + private final ObjectMapper objectMapper = new ObjectMapper(); + private final File file = new File("users.json"); + + private UserService() { + loadFromFile(); + } + + public static UserService getInstance() { + return instance; + } + + public boolean register(String id, String password) { + if (userMap.containsKey(id)) return false; + userMap.put(id, password); + saveToFile(); + return true; + } + + public boolean login(String id, String pw) { + return userMap.containsKey(id) && userMap.get(id).equals(pw); + } + + private void saveToFile() { + try { + objectMapper.writeValue(file, userMap); // ✅ 한 줄로 JSON 저장 + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void loadFromFile() { + if (!file.exists()) return; + try { + Map loaded = objectMapper.readValue(file, new TypeReference<>() {}); + userMap.putAll(loaded); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/Neul-main/src/main/java/WakeRecommendationPanel.java b/Neul-main/src/main/java/WakeRecommendationPanel.java new file mode 100644 index 0000000..3b0feff --- /dev/null +++ b/Neul-main/src/main/java/WakeRecommendationPanel.java @@ -0,0 +1,141 @@ +import javax.swing.*; +import java.awt.*; +import java.util.List; +import java.time.format.DateTimeFormatter; +import java.util.Objects; + + +public class WakeRecommendationPanel extends JPanel { + private ScreenController controller; + + public WakeRecommendationPanel(ScreenController controller, WakeTime wakeTime) { + this.controller = controller; + setLayout(null); + setBackground(new Color(91, 89, 153)); + + // 백엔드에서 시간 받아오기 + String currentTime = wakeTime.getWakeTime().format(DateTimeFormatter.ofPattern("HH:mm")); + List recommendedTimes = wakeTime.recommendSleepTimes(); + + // NEUL 제목 + JLabel title = new JLabel("NEUL"); + title.setFont(new Font("Serif", Font.BOLD, 80)); + title.setForeground(Color.WHITE); + title.setBounds(640, 50, 300, 100); + add(title); + + // 홈 버튼 + try { + // 정확한 리소스 경로 사용 + System.out.println("이미지 경로: " + getClass().getResource("/res/images/img.png")); // ✅ 경로 확인용 로그 + + ImageIcon imgIcon = new ImageIcon(Objects.requireNonNull(getClass().getResource("/res/images/img.png"))); + Image scaled = imgIcon.getImage().getScaledInstance(48, 48, Image.SCALE_SMOOTH); + ImageIcon resizedIcon = new ImageIcon(scaled); + + JButton imgButton = new JButton(resizedIcon); + imgButton.setBounds(1360, 20, 48, 48); + imgButton.setBorderPainted(false); + imgButton.setContentAreaFilled(false); + imgButton.setFocusPainted(false); + imgButton.setOpaque(false); + add(imgButton); + + // ✅ 홈 버튼 클릭 시 MainMenuPanel로 이동 + imgButton.addActionListener(e -> { + Window window = SwingUtilities.getWindowAncestor(WakeRecommendationPanel.this); + if (window instanceof JFrame) { + JFrame frame = (JFrame) window; + frame.setContentPane(new MainMenuPanel()); + frame.revalidate(); + frame.repaint(); + } + }); + + } catch (Exception e) { + System.err.println("이미지 로드 실패: " + e.getMessage()); + } + + + // 현재 시간 패널 + JPanel currentTimePanel = new JPanel(new BorderLayout()); + currentTimePanel.setBackground(Color.WHITE); + currentTimePanel.setBounds(640, 160, 200, 50); + JLabel timeText = new JLabel(currentTime, SwingConstants.CENTER); + timeText.setFont(new Font("Digital-7 Mono", Font.BOLD, 28)); + currentTimePanel.add(timeText, BorderLayout.CENTER); + add(currentTimePanel); + + // 안내 문구 + JLabel desc = new JLabel("
You should sleep at:
(1 cycle = 1.5hrs)
", SwingConstants.CENTER); + desc.setFont(new Font("SansSerif", Font.PLAIN, 18)); + desc.setForeground(Color.WHITE); + desc.setBounds(600, 250, 300, 60); + add(desc); + + // 수면 사이클 패널 + int x = 400, y = 380; + for (int i = 0; i < 6; i++) { + JPanel cyclePanel = new JPanel(); + cyclePanel.setLayout(new BoxLayout(cyclePanel, BoxLayout.Y_AXIS)); + cyclePanel.setBackground(Color.WHITE); + cyclePanel.setBounds(x, y, 200, 120); + + JLabel cycleLabel = new JLabel((i + 1) + " cycle" + (i == 0 ? "" : "s"), SwingConstants.CENTER); + JLabel hourLabel = new JLabel((1.5 * (i + 1)) + " hrs", SwingConstants.CENTER); + + cycleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + hourLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + + // 디지털 시계 스타일 시간 박스 + JPanel digitalPanel = new JPanel(); + digitalPanel.setBackground(Color.WHITE); + digitalPanel.setMaximumSize(new Dimension(160, 40)); + JLabel timeLabel = new JLabel(recommendedTimes.get(i), SwingConstants.CENTER); + timeLabel.setFont(new Font("Digital-7 Mono", Font.BOLD, 20)); + timeLabel.setForeground(new Color(66, 133, 244)); + digitalPanel.add(timeLabel); + + digitalPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + cyclePanel.add(Box.createVerticalStrut(10)); + cyclePanel.add(cycleLabel); + cyclePanel.add(hourLabel); + cyclePanel.add(Box.createVerticalStrut(5)); + cyclePanel.add(digitalPanel); + + Color borderColor = (i < 2) ? Color.RED : (i < 4) ? Color.YELLOW : Color.GREEN; + cyclePanel.setBorder(BorderFactory.createLineBorder(borderColor, 3)); + add(cyclePanel); + + x += 240; + if (i == 2) { + x = 400; + y += 150; + } + } + + // 하단 추천 문구 + JLabel bottomText = new JLabel("We recommend to sleep for 5–6 cycles!!", SwingConstants.CENTER); + bottomText.setFont(new Font("SansSerif", Font.BOLD, 20)); + bottomText.setForeground(Color.WHITE); + bottomText.setBounds(440, 700, 600, 50); + add(bottomText); + } +} + + // 테스트용 메인 +// public static void main(String[] args) { +// JFrame frame = new JFrame("Sleep Time Recommendation"); +// frame.setSize(1440, 1024); +// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); +// frame.setLocationRelativeTo(null); +// +// // 기상 시간 설정 예: "07:00" +// WakeTime wakeTime = new WakeTime("07:00"); +// WakeRecommendationPanel panel = new WakeRecommendationPanel(wakeTime); +// +// frame.add(panel); +// frame.setVisible(true); +// } +//} diff --git a/Neul-main/src/main/java/WakeTime.java b/Neul-main/src/main/java/WakeTime.java new file mode 100644 index 0000000..ed91e33 --- /dev/null +++ b/Neul-main/src/main/java/WakeTime.java @@ -0,0 +1,30 @@ +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +public class WakeTime { + private LocalTime wakeTime; + private static final int CYCLE_MINUTES = 90; + private static final int MAX_CYCLES = 6; + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + + public WakeTime(String timeStr) { + this.wakeTime = LocalTime.parse(timeStr, FORMATTER); + } + + // 1.5시간 단위로 수면 시간 추천 + public List recommendSleepTimes() { + List sleepTimes = new ArrayList<>(); + for (int i = 1; i <= MAX_CYCLES; i++) { + LocalTime sleepTime = wakeTime.minusMinutes(i * CYCLE_MINUTES); + sleepTimes.add(sleepTime.format(FORMATTER)); + } + return sleepTimes; + } + + public LocalTime getWakeTime() { + return wakeTime; + } +} + diff --git a/Neul-main/src/main/resources/images/Emotion (1).png b/Neul-main/src/main/resources/images/Emotion (1).png new file mode 100644 index 0000000..5432f09 Binary files /dev/null and b/Neul-main/src/main/resources/images/Emotion (1).png differ diff --git a/Neul-main/src/main/resources/images/Emotion (2).png b/Neul-main/src/main/resources/images/Emotion (2).png new file mode 100644 index 0000000..a692de5 Binary files /dev/null and b/Neul-main/src/main/resources/images/Emotion (2).png differ diff --git a/Neul-main/src/main/resources/images/Emotion.png b/Neul-main/src/main/resources/images/Emotion.png new file mode 100644 index 0000000..3aa1e53 Binary files /dev/null and b/Neul-main/src/main/resources/images/Emotion.png differ diff --git a/Neul-main/src/main/resources/images/img.png b/Neul-main/src/main/resources/images/img.png new file mode 100644 index 0000000..971a4c9 Binary files /dev/null and b/Neul-main/src/main/resources/images/img.png differ diff --git a/Neul-main/users.json b/Neul-main/users.json new file mode 100644 index 0000000..171a56c --- /dev/null +++ b/Neul-main/users.json @@ -0,0 +1 @@ +{"kimsubin":"rlatnqls","7777":"777777"} \ No newline at end of file