diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index 2c13bf80..00000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.gitignore b/.gitignore
index e1b27a2c..3fc46f9a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ local.properties
# Log/OS Files
*.log
+.DS_Store
# Android Studio generated files and folders
captures/
diff --git a/app/.DS_Store b/app/.DS_Store
deleted file mode 100644
index f79eae64..00000000
Binary files a/app/.DS_Store and /dev/null differ
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 18756fe7..589cd8ed 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,4 +1,4 @@
-import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
+import java.util.Properties
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
@@ -83,5 +83,11 @@ dependencies {
}
fun getApiKey(propertyKey: String): String {
- return gradleLocalProperties(rootDir).getProperty(propertyKey)
+ val localProperties = Properties().apply {
+ rootProject.file("local.properties")
+ .takeIf(File::exists)
+ ?.inputStream()
+ ?.use(::load)
+ }
+ return localProperties.getProperty(propertyKey)
}
diff --git a/app/release/.DS_Store b/app/release/.DS_Store
deleted file mode 100644
index 52d08aed..00000000
Binary files a/app/release/.DS_Store and /dev/null differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7eb85d80..23b29fd8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -22,6 +22,15 @@
android:theme="@style/Theme.NextRoom"
android:usesCleartextTraffic="true"
tools:targetApi="31">
+
+
+
+
\(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+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
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# 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"'
+# 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"
+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
- ;;
- MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+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
@@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
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"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+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
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-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" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+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
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ 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
- i=`expr $i + 1`
+ # 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
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+# 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/gradlew.bat b/gradlew.bat
index 107acd32..9d21a218 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,8 +13,10 @@
@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
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+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
@@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+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
@@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+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!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+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
diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts
index a62c1673..f7a28346 100644
--- a/presentation/build.gradle.kts
+++ b/presentation/build.gradle.kts
@@ -3,6 +3,7 @@ plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt)
+ alias(libs.plugins.compose.compiler)
id("kotlin-kapt")
id("kotlin-parcelize")
id("androidx.navigation.safeargs.kotlin")
@@ -40,9 +41,6 @@ android {
buildConfig = true
compose = true
}
- composeOptions {
- kotlinCompilerExtensionVersion = "1.5.3" // Kotlin 1.9.10과 호환
- }
}
dependencies {
diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/common/NRSnackbar.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/common/NRSnackbar.kt
index ef37c504..6d0c090a 100644
--- a/presentation/src/main/java/com/nextroom/nextroom/presentation/common/NRSnackbar.kt
+++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/common/NRSnackbar.kt
@@ -1,5 +1,6 @@
package com.nextroom.nextroom.presentation.common
+import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import androidx.annotation.StringRes
@@ -21,6 +22,7 @@ class NRSnackbar private constructor(
private val context = view.context
private val snackbar = Snackbar.make(view, message, duration)
+ @SuppressLint("RestrictedApi") // 커스텀 레이아웃 적용을 위해 SnackbarLayout 접근이 필요하다
private val snackbarLayout = snackbar.view as Snackbar.SnackbarLayout
private val binding: CommonSnackbarBinding = CommonSnackbarBinding.inflate(
LayoutInflater.from(context),
diff --git a/presentation/src/main/java/com/nextroom/nextroom/presentation/extension/Fragment.kt b/presentation/src/main/java/com/nextroom/nextroom/presentation/extension/Fragment.kt
index 6046c28b..543dca13 100644
--- a/presentation/src/main/java/com/nextroom/nextroom/presentation/extension/Fragment.kt
+++ b/presentation/src/main/java/com/nextroom/nextroom/presentation/extension/Fragment.kt
@@ -50,40 +50,51 @@ fun Fragment.snackbar(@StringRes messageId: Int, duration: Int = Snackbar.LENGTH
/**
* ## 시스템 영역 확보
*
- * 상태바와 하단 내비게이션바가 노출되는 경우 [systemBar]를 `true`로 설정하여 컨텐츠와 겹치지 않도록 시스템 영역을 확보한다.
+ * 상태바와 하단 내비게이션바, 키보드가 노출되는 경우 [systemBar]를 `true`로 설정하여 컨텐츠와 겹치지 않도록 시스템 영역을 확보한다.
*
* 반면 상태바와 하단 내비게이션바가 노출되지 않는 경우 [systemBar]를 `false`로 설정하여 컨텐츠를 표시할 공간을 확보한다.
*
* _[systemBar]가 `true`인 경우 `WindowCompat.setDecorFitsSystemWindows(window, true)`와 비슷한 동작을 한다._
- * @param systemBar 상태바와 하단 내비게이션바 영역 확보 여부
+ * @param systemBar 상태바와 하단 내비게이션바, 키보드 영역 확보 여부
*/
fun Fragment.updateSystemPadding(
systemBar: Boolean = true,
) {
- updateSystemPadding(systemBar, systemBar)
+ updateSystemPadding(statusBar = systemBar, navigationBar = systemBar, ime = systemBar)
}
/**
* ## 시스템 영역 확보
*
- * 상태바나 하단 내비게이션바가 노출되는 경우 [statusBar]나 [navigationBar]를 `true`로 설정하여 컨텐츠와 겹치지 않도록 시스템 영역을 확보한다.
+ * 상태바나 하단 내비게이션바, 키보드가 노출되는 경우 [statusBar]나 [navigationBar], [ime]를 `true`로 설정하여
+ * 컨텐츠와 겹치지 않도록 시스템 영역을 확보한다.
*
- * 반면 상태바나 하단 내비게이션바가 노출되지 않는 경우 [statusBar]나 [navigationBar]를 `false`로 설정하여 컨텐츠를 표시할 공간을 확보한다.
+ * 반면 노출되지 않는 경우 `false`로 설정하여 컨텐츠를 표시할 공간을 확보한다.
*
- * @param statusBar 상태바 영역 확보 여부
+ * edge-to-edge 환경에서는 `android:windowSoftInputMode="adjustResize"`가 더 이상 윈도우 크기를 줄이지 않으므로,
+ * 키보드에 컨텐츠가 가려지지 않으려면 [ime] 인셋을 직접 반영해야 한다.
+ *
+ * @param statusBar 상태바(디스플레이 컷아웃 포함) 영역 확보 여부
* @param navigationBar 하단 내비게이션바 영역 확보 여부
+ * @param ime 키보드 영역 확보 여부
*/
fun Fragment.updateSystemPadding(
statusBar: Boolean = true,
navigationBar: Boolean = true,
+ ime: Boolean = true,
) {
ViewCompat.setOnApplyWindowInsetsListener(requireView()) { view, windowInsets ->
- val typeMask = when {
- statusBar && navigationBar -> WindowInsetsCompat.Type.systemBars()
- statusBar -> WindowInsetsCompat.Type.statusBars()
- navigationBar -> WindowInsetsCompat.Type.navigationBars()
- else -> return@setOnApplyWindowInsetsListener windowInsets
+ var typeMask = 0
+ if (statusBar) {
+ typeMask = typeMask or WindowInsetsCompat.Type.statusBars() or WindowInsetsCompat.Type.displayCutout()
}
+ if (navigationBar) typeMask = typeMask or WindowInsetsCompat.Type.navigationBars()
+ if (ime) typeMask = typeMask or WindowInsetsCompat.Type.ime()
+
+ if (typeMask == 0) return@setOnApplyWindowInsetsListener windowInsets
+
+ // 여러 타입을 함께 조회하면 각 방향별로 가장 큰 인셋이 반환된다.
+ // 즉 키보드가 올라온 경우 하단 패딩은 max(내비게이션바, 키보드)가 된다.
val insets = windowInsets.getInsets(typeMask)
view.updatePadding(
left = insets.left,