diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1e39c1e5..90b98000 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,9 +21,9 @@ android { applicationId = "com.eatssu.android" minSdk = 23 targetSdk = 34 - versionCode = 1 - versionName = "2.1.0" - + versionCode = 21 + versionName = "2.1.1" + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 50bbb7fb..ce54b951 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,11 +32,11 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@drawable/img_logo1_44" + android:icon="@drawable/img_logo_512" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" android:requestLegacyExternalStorage="true" - android:roundIcon="@drawable/img_logo1_44" + android:roundIcon="@drawable/img_logo_512" android:supportsRtl="true" android:theme="@style/Theme.EatSSUAndroid" android:usesCleartextTraffic="true" @@ -151,14 +151,7 @@ android:name="android.app.lib_name" android:value="" /> - - - + diff --git a/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt b/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt index e18d3373..87e2bb18 100644 --- a/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt +++ b/app/src/main/java/com/eatssu/android/data/repository/FirebaseRemoteConfigRepository.kt @@ -2,11 +2,9 @@ package com.eatssu.android.data.repository import com.eatssu.android.R import com.eatssu.android.data.enums.Restaurant -import com.eatssu.android.data.model.AndroidMessage import com.eatssu.android.data.model.RestaurantInfo import com.google.firebase.remoteconfig.FirebaseRemoteConfig import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings -import com.google.gson.Gson import org.json.JSONArray import timber.log.Timber @@ -41,25 +39,25 @@ class FirebaseRemoteConfigRepository { } } - fun getAndroidMessage(): AndroidMessage { - - // Gson을 사용하여 JSON 문자열을 DTO로 파싱 - val serverStatus: AndroidMessage = Gson().fromJson(instance.getString("android_message"), AndroidMessage::class.java) - - // 파싱된 결과 확인 - println("Dialog: ${serverStatus.dialog}") - println("Message: ${serverStatus.message}") - - return serverStatus - } - - fun getForceUpdate(): Boolean { - return instance.getBoolean("force_update") - } - - fun getAppVersion(): String { - return instance.getString("app_version") - } +// fun getAndroidMessage(): AndroidMessage { +// +// // Gson을 사용하여 JSON 문자열을 DTO로 파싱 +// val serverStatus: AndroidMessage = Gson().fromJson(instance.getString("android_message"), AndroidMessage::class.java) +// +// // 파싱된 결과 확인 +// println("Dialog: ${serverStatus.dialog}") +// println("Message: ${serverStatus.message}") +// +// return serverStatus +// } + +// fun getForceUpdate(): Boolean { +// return instance.getBoolean("force_update") +// } +// +// fun getAppVersion(): String { +// return instance.getString("app_version") +// } fun getVersionCode(): Long { return instance.getLong("android_version_code") diff --git a/app/src/main/java/com/eatssu/android/data/usecase/GetAccessTokenUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/GetAccessTokenUseCase.kt index 87975038..f54de06b 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/GetAccessTokenUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/GetAccessTokenUseCase.kt @@ -1,14 +1,14 @@ package com.eatssu.android.data.usecase -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class GetAccessTokenUseCase @Inject constructor( -// private val preferencesRepository: PreferencesRepository, + @ApplicationContext private val context: Context ) { suspend operator fun invoke(): String { - - return MySharedPreferences.getAccessToken(App.appContext) + return MySharedPreferences.getAccessToken(context) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/GetRefreshTokenUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/GetRefreshTokenUseCase.kt index aa69fbc0..635780b8 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/GetRefreshTokenUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/GetRefreshTokenUseCase.kt @@ -1,14 +1,15 @@ package com.eatssu.android.data.usecase -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class GetRefreshTokenUseCase @Inject constructor( + @ApplicationContext private val context: Context // private val preferencesRepository: PreferencesRepository, ) { suspend operator fun invoke(): String { - - return MySharedPreferences.getRefreshToken(App.appContext) + return MySharedPreferences.getRefreshToken(context) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/GetUserEmailUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/GetUserEmailUseCase.kt index 1ccdf66c..5bbabb1c 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/GetUserEmailUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/GetUserEmailUseCase.kt @@ -1,14 +1,15 @@ package com.eatssu.android.data.usecase -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class GetUserEmailUseCase @Inject constructor( -// private val preferencesRepository: PreferencesRepository, + @ApplicationContext private val context: Context ) { suspend operator fun invoke(): String { - return MySharedPreferences.getUserEmail(App.appContext) + return MySharedPreferences.getUserEmail(context) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/GetUserNameUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/GetUserNameUseCase.kt index 5d204fdf..e85a49d9 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/GetUserNameUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/GetUserNameUseCase.kt @@ -1,14 +1,15 @@ package com.eatssu.android.data.usecase -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class GetUserNameUseCase @Inject constructor( // private val preferencesRepository: PreferencesRepository, + @ApplicationContext private val context: Context ) { suspend operator fun invoke(): String { - - return MySharedPreferences.getUserName(App.appContext) + return MySharedPreferences.getUserName(context) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/LogoutUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/LogoutUseCase.kt index 4afc03de..7d1e5b7f 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/LogoutUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/LogoutUseCase.kt @@ -1,14 +1,16 @@ package com.eatssu.android.data.usecase -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class LogoutUseCase @Inject constructor( + @ApplicationContext private val context: Context ) { suspend operator fun invoke() { - MySharedPreferences.clearUser(App.appContext) + MySharedPreferences.clearUser(context) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/SetAccessTokenUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/SetAccessTokenUseCase.kt index 0474445d..2c5e06b4 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/SetAccessTokenUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/SetAccessTokenUseCase.kt @@ -1,17 +1,15 @@ package com.eatssu.android.data.usecase -import android.util.Log -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class SetAccessTokenUseCase @Inject constructor( // private val preferencesRepository: PreferencesRepository, + @ApplicationContext private val context: Context ) { suspend operator fun invoke(accessToken: String) { - MySharedPreferences.setAccessToken(App.appContext, accessToken) - - Log.d("SetAccessTokenUseCase", accessToken) - + MySharedPreferences.setAccessToken(context, accessToken) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/SetRefreshTokenUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/SetRefreshTokenUseCase.kt index 0674ff1b..d8863c33 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/SetRefreshTokenUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/SetRefreshTokenUseCase.kt @@ -1,17 +1,15 @@ package com.eatssu.android.data.usecase -import android.util.Log -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class SetRefreshTokenUseCase @Inject constructor( // private val preferencesRepository: PreferencesRepository, + @ApplicationContext private val context: Context ) { suspend operator fun invoke(refreshToken: String) { - MySharedPreferences.setRefreshToken(App.appContext, refreshToken) - - Log.d("SetRefreshTokenUseCase", refreshToken) - + MySharedPreferences.setRefreshToken(context, refreshToken) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/SetUserEmailUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/SetUserEmailUseCase.kt index ca88a2bc..2e27b2ae 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/SetUserEmailUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/SetUserEmailUseCase.kt @@ -1,16 +1,14 @@ package com.eatssu.android.data.usecase -import android.util.Log -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject class SetUserEmailUseCase @Inject constructor( + @ApplicationContext private val context: Context ) { suspend operator fun invoke(email: String) { - MySharedPreferences.setUserEmail(App.appContext, email) - - Log.d("SetUserEmailUseCase", email) - + MySharedPreferences.setUserEmail(context, email) } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/data/usecase/SetUserNameUseCase.kt b/app/src/main/java/com/eatssu/android/data/usecase/SetUserNameUseCase.kt index b2b62c88..18e80ca7 100644 --- a/app/src/main/java/com/eatssu/android/data/usecase/SetUserNameUseCase.kt +++ b/app/src/main/java/com/eatssu/android/data/usecase/SetUserNameUseCase.kt @@ -1,18 +1,20 @@ package com.eatssu.android.data.usecase -import com.eatssu.android.App +import android.content.Context import com.eatssu.android.base.BaseResponse import com.eatssu.android.data.dto.request.ChangeNicknameRequest import com.eatssu.android.data.repository.UserRepository import com.eatssu.android.util.MySharedPreferences +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.Flow import javax.inject.Inject class SetUserNameUseCase @Inject constructor( private val userRepository: UserRepository, + @ApplicationContext private val context: Context ) { suspend operator fun invoke(name: String): Flow> { - MySharedPreferences.setUserName(App.appContext, name) + MySharedPreferences.setUserName(context, name) //Todo 이게 최선일까? 로컬에 이름 Set과 리모트의 이름 change를 usecase를 따로 만들어야하나? return userRepository.updateUserName(ChangeNicknameRequest(name)) diff --git a/app/src/main/java/com/eatssu/android/di/AppModule.kt b/app/src/main/java/com/eatssu/android/di/AppModule.kt index 5b4f9e17..62988858 100644 --- a/app/src/main/java/com/eatssu/android/di/AppModule.kt +++ b/app/src/main/java/com/eatssu/android/di/AppModule.kt @@ -2,6 +2,7 @@ package com.eatssu.android.di import android.app.Application import android.content.Context +import com.eatssu.android.data.repository.FirebaseRemoteConfigRepository import com.eatssu.android.data.repository.PreferencesRepository import dagger.Module import dagger.Provides @@ -25,4 +26,11 @@ object AppModule { fun providePreferencesRepository(@ApplicationContext context: Context): PreferencesRepository { return PreferencesRepository(context) } + + @Provides + @Singleton + fun provideFirebaseRemoteConfigRepository(): FirebaseRemoteConfigRepository { + return FirebaseRemoteConfigRepository() + } + } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt b/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt index 034072a6..d1583cc6 100644 --- a/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt +++ b/app/src/main/java/com/eatssu/android/di/network/TokenInterceptor.kt @@ -1,12 +1,11 @@ package com.eatssu.android.di.network +import android.content.Context import android.content.Intent import android.os.Handler import android.os.Looper -import android.util.Log import android.widget.Toast -import com.eatssu.android.App import com.eatssu.android.BuildConfig.BASE_URL import com.eatssu.android.base.BaseResponse import com.eatssu.android.data.dto.response.TokenResponse @@ -18,6 +17,7 @@ import com.eatssu.android.data.usecase.SetRefreshTokenUseCase import com.eatssu.android.ui.login.LoginActivity import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Request @@ -33,6 +33,7 @@ class TokenInterceptor @Inject constructor( private val setAccessTokenUseCase: SetAccessTokenUseCase, private val setRefreshTokenUseCase: SetRefreshTokenUseCase, private val logoutUseCase: LogoutUseCase, + @ApplicationContext private val context: Context ) : Interceptor { companion object { @@ -68,7 +69,7 @@ class TokenInterceptor @Inject constructor( val response = chain.proceed(request) if (response.code == 401) { - Log.d(TAG, "토큰 퉤퉤") + Timber.d("토큰 퉤퉤") response.close() try { @@ -78,13 +79,13 @@ class TokenInterceptor @Inject constructor( .addHeader(HEADER_AUTHORIZATION, "Bearer $refreshToken") .build() - Log.d(TAG, "재발급 중") + Timber.d("재발급 중") val refreshTokenResponse = chain.proceed(refreshTokenRequest) - Log.d(TAG, "refreshTokenResponse : $refreshTokenResponse") + Timber.d("refreshTokenResponse : $refreshTokenResponse") if (refreshTokenResponse.isSuccessful) { - Log.d(TAG, "재발급 성공") + Timber.d("재발급 성공") val responseToken = parseRefreshTokenResponse(refreshTokenResponse) @@ -110,7 +111,6 @@ class TokenInterceptor @Inject constructor( Timber.e("재발급에서의 401") Handler(Looper.getMainLooper()).post { - val context = App.appContext Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) @@ -123,7 +123,6 @@ class TokenInterceptor @Inject constructor( Timber.e("재발급 실패 $e") Handler(Looper.getMainLooper()).post { - val context = App.appContext Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) @@ -137,7 +136,6 @@ class TokenInterceptor @Inject constructor( Timber.e("404 + 다른 유저!") Handler(Looper.getMainLooper()).post { - val context = App.appContext Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) @@ -150,7 +148,6 @@ class TokenInterceptor @Inject constructor( Timber.e("500 + 다른 유저") Handler(Looper.getMainLooper()).post { - val context = App.appContext Toast.makeText(context, "토큰이 만료되어 로그아웃 됩니다.", Toast.LENGTH_SHORT).show() val intent = Intent(context, LoginActivity::class.java) // 로그인 화면으로 이동 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) diff --git a/app/src/main/java/com/eatssu/android/ui/info/InfoActivity.kt b/app/src/main/java/com/eatssu/android/ui/info/InfoActivity.kt deleted file mode 100644 index de5c06d4..00000000 --- a/app/src/main/java/com/eatssu/android/ui/info/InfoActivity.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.eatssu.android.ui.info - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.ViewModelProvider -import com.eatssu.android.R -import com.eatssu.android.data.enums.Restaurant -import com.eatssu.android.data.repository.FirebaseRemoteConfigRepository -import com.eatssu.android.databinding.ActivityInfoBinding - -class InfoActivity : AppCompatActivity() { - - private lateinit var binding: ActivityInfoBinding - - private lateinit var infoViewModel: InfoViewModel - private lateinit var restaurantType: Restaurant - private lateinit var firebaseRemoteConfigRepository: FirebaseRemoteConfigRepository - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityInfoBinding.inflate(layoutInflater) - setContentView(binding.root) - window.setBackgroundDrawableResource(R.drawable.shape_round_corners_dialog) - - - firebaseRemoteConfigRepository = FirebaseRemoteConfigRepository() - infoViewModel = ViewModelProvider(this, InfoViewModelFactory(firebaseRemoteConfigRepository))[InfoViewModel::class.java] - - restaurantType = intent.getSerializableExtra("restaurantType") as Restaurant - binding.tvName.text = restaurantType.displayName - - infoViewModel.infoList.observe(this) { - - when (restaurantType) { - Restaurant.DODAM -> { - binding.tvLocation.text = infoViewModel.dodamEtc.value - binding.tvTime.text = infoViewModel.dodamTime.value - binding.tvEtc.text = infoViewModel.dodamEtc.value - } - - Restaurant.HAKSIK -> { - binding.tvLocation.text = infoViewModel.dodamLocation.value - binding.tvTime.text = infoViewModel.haksikTime.value - binding.tvEtc.text = infoViewModel.haksikEtc.value - } - - Restaurant.FOOD_COURT -> { - binding.tvLocation.text = infoViewModel.foodLocation.value - binding.tvTime.text = infoViewModel.foodTime.value - binding.tvEtc.text = infoViewModel.foodEtc.value - } - - Restaurant.SNACK_CORNER -> { - binding.tvLocation.text = infoViewModel.snackLocation.value - binding.tvTime.text = infoViewModel.snackTime.value - binding.tvEtc.text = infoViewModel.snackEtc.value - } - - Restaurant.DORMITORY -> { - binding.tvLocation.text = infoViewModel.dormitoryLocation.value - binding.tvTime.text = infoViewModel.dormitoryTime.value - binding.tvEtc.text = infoViewModel.dormitoryEtc.value - } - - else -> {} - } - } - } -} diff --git a/app/src/main/java/com/eatssu/android/ui/info/InfoBottomSheetFragment.kt b/app/src/main/java/com/eatssu/android/ui/info/InfoBottomSheetFragment.kt index bf583f48..9de27f41 100644 --- a/app/src/main/java/com/eatssu/android/ui/info/InfoBottomSheetFragment.kt +++ b/app/src/main/java/com/eatssu/android/ui/info/InfoBottomSheetFragment.kt @@ -1,117 +1,66 @@ package com.eatssu.android.ui.info import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.activityViewModels import com.bumptech.glide.Glide import com.eatssu.android.data.enums.Restaurant -import com.eatssu.android.data.repository.FirebaseRemoteConfigRepository import com.eatssu.android.databinding.FragmentBottomsheetInfoBinding import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import timber.log.Timber class InfoBottomSheetFragment : BottomSheetDialogFragment() { private var _binding: FragmentBottomsheetInfoBinding? = null private val binding get() = _binding!! - private lateinit var infoViewModel: InfoViewModel - private lateinit var firebaseRemoteConfigRepository: FirebaseRemoteConfigRepository - + private val infoViewModel: InfoViewModel by activityViewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { _binding = FragmentBottomsheetInfoBinding.inflate(inflater, container, false) - - firebaseRemoteConfigRepository = FirebaseRemoteConfigRepository() - infoViewModel = ViewModelProvider( - this, - InfoViewModelFactory(firebaseRemoteConfigRepository) - )[InfoViewModel::class.java] - return binding.root } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - // 전달된 데이터 받기 + // Retrieve passed data val name = arguments?.getString("name") val restaurantType = enumValues().find { it.name == name } ?: Restaurant.HAKSIK - Log.d("InfoBottomSheetFragment", "onViewCreated: $name $restaurantType") + Timber.d("onViewCreated: $name $restaurantType") binding.tvName.text = restaurantType.displayName - //TODO 방법 개선 - infoViewModel.infoList.observe(this) { + // Collect the infoList Flow + CoroutineScope(Dispatchers.Main).launch { + infoViewModel.infoList.collect { restaurantInfoList -> + val restaurantInfo = infoViewModel.getRestaurantInfo(restaurantType) - when (restaurantType) { - Restaurant.DODAM -> { - binding.tvLocation.text = infoViewModel.dodamLocation.value - binding.tvTime.text = infoViewModel.dodamTime.value - binding.tvEtc.text = infoViewModel.dodamEtc.value + restaurantInfo?.let { + binding.tvLocation.text = it.location + binding.tvTime.text = it.time + binding.tvEtc.text = it.etc - Glide.with(this) - .load(infoViewModel.dodamPhotoUrl.value) + Glide.with(this@InfoBottomSheetFragment) + .load(it.photoUrl) .into(binding.ivCafeteriaPhoto) } - - Restaurant.HAKSIK -> { - binding.tvLocation.text = infoViewModel.haksikLocation.value - binding.tvTime.text = infoViewModel.haksikTime.value - binding.tvEtc.text = infoViewModel.haksikEtc.value - - Glide.with(this) - .load(infoViewModel.haksikPhotoUrl.value) - .into(binding.ivCafeteriaPhoto) - } - - Restaurant.FOOD_COURT -> { - binding.tvLocation.text = infoViewModel.foodLocation.value - binding.tvTime.text = infoViewModel.foodTime.value - binding.tvEtc.text = infoViewModel.foodEtc.value - - Glide.with(this) - .load(infoViewModel.foodPhotoUrl.value) - .into(binding.ivCafeteriaPhoto) - } - - Restaurant.SNACK_CORNER -> { - binding.tvLocation.text = infoViewModel.snackLocation.value - binding.tvTime.text = infoViewModel.snackTime.value - binding.tvEtc.text = infoViewModel.snackEtc.value - - Glide.with(this) - .load(infoViewModel.snackPhotoUrl.value) - .into(binding.ivCafeteriaPhoto) - } - - Restaurant.DORMITORY -> { - binding.tvLocation.text = infoViewModel.dormitoryLocation.value - binding.tvTime.text = infoViewModel.dormitoryTime.value - binding.tvEtc.text = infoViewModel.dormitoryEtc.value - - Glide.with(this) - .load(infoViewModel.dormitoryPhotoUrl.value) - .into(binding.ivCafeteriaPhoto) - } - - else -> {} } } } companion object { - // newInstance 메서드를 통해 데이터를 전달하는 방법 fun newInstance(data: String): InfoBottomSheetFragment { val fragment = InfoBottomSheetFragment() - val args = Bundle() - args.putString("name", data) + val args = Bundle().apply { putString("name", data) } fragment.arguments = args return fragment } diff --git a/app/src/main/java/com/eatssu/android/ui/info/InfoViewModel.kt b/app/src/main/java/com/eatssu/android/ui/info/InfoViewModel.kt index 84598b55..b9f1f130 100644 --- a/app/src/main/java/com/eatssu/android/ui/info/InfoViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/info/InfoViewModel.kt @@ -1,73 +1,40 @@ package com.eatssu.android.ui.info import android.util.Log -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.eatssu.android.data.enums.Restaurant import com.eatssu.android.data.model.RestaurantInfo import com.eatssu.android.data.repository.FirebaseRemoteConfigRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject -class InfoViewModel(firebaseRemoteConfigRepository: FirebaseRemoteConfigRepository): ViewModel() { +@HiltViewModel +class InfoViewModel @Inject constructor( + private val firebaseRemoteConfigRepository: FirebaseRemoteConfigRepository +) : ViewModel() { - val infoList: MutableLiveData> = MutableLiveData() + // StateFlow to hold restaurant info list + private val _infoList = MutableStateFlow>(emptyList()) + val infoList: StateFlow> = _infoList.asStateFlow() - val dodamLocation = MutableLiveData() - val dodamPhotoUrl = MutableLiveData() - val dodamTime = MutableLiveData() - val dodamEtc = MutableLiveData() - - val foodLocation = MutableLiveData() - val foodPhotoUrl = MutableLiveData() - val foodTime = MutableLiveData() - val foodEtc = MutableLiveData() - - val dormitoryLocation = MutableLiveData() - val dormitoryPhotoUrl = MutableLiveData() - val dormitoryTime = MutableLiveData() - val dormitoryEtc = MutableLiveData() - - val snackLocation = MutableLiveData() - val snackPhotoUrl = MutableLiveData() - val snackTime = MutableLiveData() - val snackEtc = MutableLiveData() - - val haksikLocation = MutableLiveData() - val haksikPhotoUrl = MutableLiveData() - val haksikTime = MutableLiveData() - val haksikEtc = MutableLiveData() + // Map to hold restaurant info + private val restaurantInfoMap: MutableMap = mutableMapOf() init { - infoList.value = firebaseRemoteConfigRepository.getCafeteriaInfo() - Log.d("InfoViewModel",infoList.value.toString()) - val dodam = infoList.value!!.find { it.enum == Restaurant.DODAM } - dodamPhotoUrl.value = dodam?.photoUrl ?: "" - dodamTime.value = dodam?.time ?: "" - dodamLocation.value = dodam?.location ?: "" - dodamEtc.value = dodam?.etc ?: "" - - val food = infoList.value!!.find { it.enum == Restaurant.FOOD_COURT } - foodPhotoUrl.value = food?.photoUrl ?: "" - foodTime.value = food?.time ?: "" - foodLocation.value = food?.location ?: "" - foodEtc.value = food?.etc ?: "" - - val dormitory = infoList.value!!.find { it.enum == Restaurant.DORMITORY } - dormitoryPhotoUrl.value = dormitory?.photoUrl ?: "" - dormitoryTime.value = dormitory?.time ?: "" - dormitoryLocation.value = dormitory?.location ?: "" - dormitoryEtc.value = dormitory?.etc ?: "" - - val snack = infoList.value!!.find { it.enum == Restaurant.SNACK_CORNER } - snackPhotoUrl.value = snack?.photoUrl ?: "" - snackTime.value = snack?.time ?: "" - snackLocation.value = snack?.location ?: "" - snackEtc.value = snack?.etc ?: "" + // Load cafeteria info from repository and update the StateFlow + _infoList.value = firebaseRemoteConfigRepository.getCafeteriaInfo() + Log.d("InfoViewModel", _infoList.value.toString()) + _infoList.value.forEach { restaurantInfo -> + restaurantInfoMap[restaurantInfo.enum] = restaurantInfo + } + } - val haksik = infoList.value!!.find { it.enum == Restaurant.HAKSIK } - haksikPhotoUrl.value = haksik?.photoUrl ?: "" - haksikTime.value = haksik?.time ?: "" - haksikLocation.value = haksik?.location ?: "" - haksikEtc.value = haksik?.etc ?: "" + // Helper function to get restaurant details + fun getRestaurantInfo(restaurant: Restaurant): RestaurantInfo? { + return restaurantInfoMap[restaurant] } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/ui/info/InfoViewModelFactory.kt b/app/src/main/java/com/eatssu/android/ui/info/InfoViewModelFactory.kt deleted file mode 100644 index 1b982232..00000000 --- a/app/src/main/java/com/eatssu/android/ui/info/InfoViewModelFactory.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.eatssu.android.ui.info - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.eatssu.android.data.repository.FirebaseRemoteConfigRepository - -class InfoViewModelFactory(private val repository: FirebaseRemoteConfigRepository) : - ViewModelProvider.Factory { - - override fun create(modelClass: Class): T { - if (modelClass.isAssignableFrom(InfoViewModel::class.java)) { - @Suppress("UNCHECKED_CAST") - return InfoViewModel(repository) as T - } - throw IllegalArgumentException("Unknown ViewModel class") - } -} - diff --git a/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt b/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt index 8366b4be..435399ec 100644 --- a/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/login/LoginViewModel.kt @@ -1,8 +1,8 @@ package com.eatssu.android.ui.login +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.eatssu.android.App import com.eatssu.android.R import com.eatssu.android.data.dto.request.LoginWithKakaoRequest import com.eatssu.android.data.usecase.LoginUseCase @@ -10,6 +10,7 @@ import com.eatssu.android.data.usecase.SetAccessTokenUseCase import com.eatssu.android.data.usecase.SetRefreshTokenUseCase import com.eatssu.android.data.usecase.SetUserEmailUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -28,6 +29,7 @@ class LoginViewModel @Inject constructor( private val setAccessTokenUseCase: SetAccessTokenUseCase, private val setRefreshTokenUseCase: SetRefreshTokenUseCase, private val setUserEmailUseCase: SetUserEmailUseCase, + @ApplicationContext private val context: Context ) : ViewModel() { private val _uiState: MutableStateFlow = MutableStateFlow(LoginState()) @@ -46,7 +48,7 @@ class LoginViewModel @Inject constructor( _uiState.update { it.copy( loading = false, error = false, - toastMessage = App.appContext.getString(R.string.login_done) + toastMessage = context.getString(R.string.login_done) ) //Todo 로그인과 회원가입에 따른 토스트 메시지 구분하기 } diff --git a/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt b/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt index 16571d76..07acdaad 100644 --- a/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/eatssu/android/ui/main/MainViewModel.kt @@ -1,11 +1,12 @@ package com.eatssu.android.ui.main +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.eatssu.android.App import com.eatssu.android.R import com.eatssu.android.data.usecase.GetUserInfoUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -21,6 +22,7 @@ import javax.inject.Inject @HiltViewModel class MainViewModel @Inject constructor( private val getUserInfoUseCase: GetUserInfoUseCase, + @ApplicationContext private val context: Context ) : ViewModel() { private val _uiState: MutableStateFlow = MutableStateFlow(MainState()) @@ -40,7 +42,7 @@ class MainViewModel @Inject constructor( _uiState.update { it.copy( error = true, - toastMessage = App.appContext.getString(R.string.not_found) + toastMessage = context.getString(R.string.not_found) ) } Timber.e(e.toString()) @@ -51,7 +53,7 @@ class MainViewModel @Inject constructor( _uiState.update { it.copy( isNicknameNull = true, - toastMessage = App.appContext.getString(R.string.set_nickname) + toastMessage = context.getString(R.string.set_nickname) ) } } else { @@ -59,7 +61,7 @@ class MainViewModel @Inject constructor( it.copy( isNicknameNull = false, toastMessage = String.format( - App.appContext.getString(R.string.hello_user), + context.getString(R.string.hello_user), this.nickname ) ) diff --git a/app/src/main/java/com/eatssu/android/ui/main/menu/MenuAdapter.kt b/app/src/main/java/com/eatssu/android/ui/main/menu/MenuAdapter.kt index da7f67e6..a7e0c34a 100644 --- a/app/src/main/java/com/eatssu/android/ui/main/menu/MenuAdapter.kt +++ b/app/src/main/java/com/eatssu/android/ui/main/menu/MenuAdapter.kt @@ -27,8 +27,7 @@ class MenuAdapter( sectionModel: Section ) { - - binding.btnInfo.setOnClickListener { + binding.llCafeteriaInfo.setOnClickListener { val modalBottomSheet = InfoBottomSheetFragment.newInstance(sectionModel.cafeteria.name) diff --git a/app/src/main/java/com/eatssu/android/ui/main/menu/MenuFragment.kt b/app/src/main/java/com/eatssu/android/ui/main/menu/MenuFragment.kt index 9a96b5df..09686888 100644 --- a/app/src/main/java/com/eatssu/android/ui/main/menu/MenuFragment.kt +++ b/app/src/main/java/com/eatssu/android/ui/main/menu/MenuFragment.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.RequiresApi import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager @@ -17,12 +18,10 @@ import com.eatssu.android.data.enums.MenuType import com.eatssu.android.data.enums.Restaurant import com.eatssu.android.data.enums.Time import com.eatssu.android.data.model.Section -import com.eatssu.android.data.repository.FirebaseRemoteConfigRepository import com.eatssu.android.data.service.MealService import com.eatssu.android.data.service.MenuService import com.eatssu.android.databinding.FragmentMenuBinding import com.eatssu.android.ui.info.InfoViewModel -import com.eatssu.android.ui.info.InfoViewModelFactory import com.eatssu.android.ui.main.calendar.CalendarViewModel import com.eatssu.android.util.RetrofitImpl import java.time.DayOfWeek @@ -51,9 +50,9 @@ class MenuFragment : Fragment() { private val totalMenuList = ArrayList
() - private lateinit var infoViewModel: InfoViewModel private lateinit var restaurantType: Restaurant - private lateinit var firebaseRemoteConfigRepository: FirebaseRemoteConfigRepository + + private val infoViewModel: InfoViewModel by activityViewModels() companion object { @@ -75,13 +74,6 @@ class MenuFragment : Fragment() { savedInstanceState: Bundle?, ): View { _binding = FragmentMenuBinding.inflate(inflater, container, false) - - firebaseRemoteConfigRepository = FirebaseRemoteConfigRepository() - infoViewModel = ViewModelProvider( - this, - InfoViewModelFactory(firebaseRemoteConfigRepository) - )[InfoViewModel::class.java] - return binding.root } @@ -92,11 +84,6 @@ class MenuFragment : Fragment() { observeViewModel() } - override fun onResume() { - super.onResume() - - } - @RequiresApi(Build.VERSION_CODES.O) fun observeViewModel() { menuService = RetrofitImpl.retrofit.create(MenuService::class.java) @@ -145,7 +132,8 @@ class MenuFragment : Fragment() { MenuType.FIXED, Restaurant.FOOD_COURT, result.mapFixedMenuResponseToMenu(), - infoViewModel.foodLocation.value.toString() + infoViewModel.getRestaurantInfo(Restaurant.FOOD_COURT)?.location + ?: "" ) ) } @@ -163,7 +151,8 @@ class MenuFragment : Fragment() { MenuType.FIXED, Restaurant.SNACK_CORNER, result.mapFixedMenuResponseToMenu(), - infoViewModel.snackLocation.value.toString() + infoViewModel.getRestaurantInfo(Restaurant.SNACK_CORNER)?.location + ?: "" ) ) } @@ -198,7 +187,7 @@ class MenuFragment : Fragment() { MenuType.VARIABLE, Restaurant.HAKSIK, result.mapTodayMenuResponseToMenu(), - infoViewModel.haksikLocation.value.toString() + infoViewModel.getRestaurantInfo(Restaurant.HAKSIK)?.location ?: "" ) ) @@ -217,7 +206,7 @@ class MenuFragment : Fragment() { MenuType.VARIABLE, Restaurant.DODAM, result.mapTodayMenuResponseToMenu(), - infoViewModel.dodamLocation.value.toString() + infoViewModel.getRestaurantInfo(Restaurant.DODAM)?.location ?: "" ) ) } @@ -234,7 +223,7 @@ class MenuFragment : Fragment() { MenuType.VARIABLE, Restaurant.DORMITORY, result.mapTodayMenuResponseToMenu(), - infoViewModel.dormitoryLocation.value.toString() + infoViewModel.getRestaurantInfo(Restaurant.DORMITORY)?.location ?: "" ) ) } diff --git a/app/src/main/res/drawable/img_logo_512.png b/app/src/main/res/drawable/img_logo_512.png new file mode 100644 index 00000000..e70b5170 Binary files /dev/null and b/app/src/main/res/drawable/img_logo_512.png differ diff --git a/app/src/main/res/layout/item_cafeteria_section.xml b/app/src/main/res/layout/item_cafeteria_section.xml index 96912697..1bc900e1 100644 --- a/app/src/main/res/layout/item_cafeteria_section.xml +++ b/app/src/main/res/layout/item_cafeteria_section.xml @@ -31,6 +31,13 @@ android:text="@string/student_cafeteria" android:textColor="@color/black" /> + + + + + diff --git a/app/src/main/res/xml/firebase_remote_config.xml b/app/src/main/res/xml/firebase_remote_config.xml index 148997e5..af102769 100644 --- a/app/src/main/res/xml/firebase_remote_config.xml +++ b/app/src/main/res/xml/firebase_remote_config.xml @@ -18,6 +18,7 @@ cafeteria_information + [ { "enum": "DODAM", "name": "도담 식당", @@ -25,17 +26,16 @@ "time": "11:20~14:00(점심)\n17:00~18:30(저녁)", "etc": "2개 코너 운영\n대면 배식,웰빙코너\n토요일 11:30~13:30", "image": - "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/04f266a1-0649-430c-af8f-29e83e87472120240823074119%E1%84%83%E1%85%A9%E1%84%83%E1%85%A1%E1%86%B7%E1%84%89%E1%85%B5%E1%86%A8%E1%84%83%E1%85%A1%E1%86%BC.jpeg" + "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/298437cb-45b1-48cf-bace-ea84e8f23e1820240926041857%E1%84%83%E1%85%A9%E1%84%83%E1%85%A1%E1%86%B7IMG_8268%20%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A6.jpeg" }, { "enum": "HAKSIK", "name": "학생 식당", "location": "학생회관 3층", - "time": "08:10~09:20(천원의 아침밥, 100개 한정, - 24.03.11~06.14)\n11:20~14:00\n(점심)\n14:00~17:00(공간 개방)", + "time": "08:10~09:20(천원의 아침밥, 100개 한정)\n11:20~14:00\n(점심)\n14:00~17:00(공간 개방)", "etc": "3개 코너 운영\n뚝배기찌개, 덮밥, 양식", "image": - "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/f0fbf458-5f7e-49bb-97e7-c663c4ec8cc820240823074519%E1%84%92%E1%85%A1%E1%86%A8%E1%84%89%E1%85%A2%E1%86%BC%E1%84%89%E1%85%B5%E1%86%A8%E1%84%83%E1%85%A1%E1%86%BC.jpeg" + "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/fa802164-621d-4b6d-b44e-e9de4bab225520240926041948%E1%84%92%E1%85%A1%E1%86%A8%E1%84%89%E1%85%A2%E1%86%BC%20IMG_0394%20%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A6.jpeg" }, { "enum": "SNACK_CORNER", @@ -53,7 +53,7 @@ "time": "휴무", "etc": "준비 중", "image": - "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/2f8a9ae2-c2f9-4f20-bafe-28e0a9e414e320240823074451%E1%84%91%E1%85%AE%E1%84%83%E1%85%B3%E1%84%8F%E1%85%A9%E1%84%90%E1%85%B3.jpeg" + "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/09b487f5-e431-4ab2-a339-76e472ea8d8320240926041925%E1%84%91%E1%85%AE%E1%84%8F%E1%85%A9IMG_0396%20%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A6.jpeg" }, { "enum": "DORMITORY", @@ -62,8 +62,9 @@ "time": "08:00~09:30\n11:00~14:00\n17:00~18:30", "etc": "주말 조식은 운영되지 않습니다.", "image": - "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/2f8a9ae2-c2f9-4f20-bafe-28e0a9e414e320240823074451%E1%84%91%E1%85%AE%E1%84%83%E1%85%B3%E1%84%8F%E1%85%A9%E1%84%90%E1%85%B3.jpeg" + "https://eatssu-prod-bucket.s3.ap-northeast-2.amazonaws.com/reviewImg/364077d5-f09b-4e9c-b5e4-e1e08d098f1820240926041231%E1%84%80%E1%85%B5%E1%84%89%E1%85%AE%E1%86%A8%E1%84%89%E1%85%A1IMG_2480%20%E1%84%8F%E1%85%B3%E1%84%80%E1%85%A6.jpeg" } + ]