diff --git a/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt b/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt index e973b2ea5..435b31938 100644 --- a/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt +++ b/app/src/main/java/com/eatssu/android/alarm/EatSsuFirebaseMessagingService.kt @@ -7,6 +7,7 @@ import android.content.Intent import androidx.core.app.NotificationCompat import com.eatssu.android.R import com.eatssu.android.presentation.intro.IntroActivity +import com.eatssu.common.enums.LaunchPath.REMOTE_NOTIFICATION import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import timber.log.Timber @@ -44,8 +45,7 @@ class EatSsuFirebaseMessagingService : FirebaseMessagingService() { } notificationManager.createNotificationChannel(channel) - val intent = Intent(this, IntroActivity::class.java).apply { - putExtra("launch_path", "remote_notification") + val intent = IntroActivity.intent(this, IntroActivity.Args(REMOTE_NOTIFICATION)) { flags = Intent.FLAG_ACTIVITY_CLEAR_TOP } diff --git a/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt b/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt index d153249fa..4002c183f 100644 --- a/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt +++ b/app/src/main/java/com/eatssu/android/alarm/NotificationReceiver.kt @@ -9,6 +9,7 @@ import android.content.Intent import androidx.core.app.NotificationCompat import com.eatssu.android.R import com.eatssu.android.presentation.intro.IntroActivity +import com.eatssu.common.enums.LaunchPath import java.time.DayOfWeek import java.time.LocalDateTime @@ -39,14 +40,17 @@ class NotificationReceiver : BroadcastReceiver() { notificationManager.createNotificationChannel(channel) - val intent = Intent(context, IntroActivity::class.java).apply { + val intent = IntroActivity.intent( + context, + IntroActivity.Args(LaunchPath.LOCAL_NOTIFICATION) + ) { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent = PendingIntent.getActivity( context, 0, - intent.putExtra("launch_path", "local_notification"), + intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) diff --git a/app/src/main/java/com/eatssu/android/presentation/MainActivity.kt b/app/src/main/java/com/eatssu/android/presentation/MainActivity.kt index d5fab44bf..438df610a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/MainActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/MainActivity.kt @@ -16,12 +16,12 @@ import androidx.navigation.fragment.NavHostFragment import androidx.work.WorkManager import com.eatssu.android.R import com.eatssu.android.databinding.ActivityMainBinding +import com.eatssu.android.presentation.base.ActivityCompanion import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.login.LoginActivity import com.eatssu.android.presentation.mypage.MyPageViewModel import com.eatssu.android.presentation.mypage.userinfo.UserInfoActivity import com.eatssu.android.presentation.util.showToast -import com.eatssu.android.presentation.util.startActivity import com.eatssu.common.enums.ScreenId import com.google.android.material.bottomnavigation.BottomNavigationView import dagger.hilt.android.AndroidEntryPoint @@ -38,6 +38,8 @@ class MainActivity : BaseActivity( ScreenId.HOME_MAIN ) { + companion object : ActivityCompanion(MainActivity::class) + @Inject lateinit var workManager: WorkManager @@ -149,12 +151,14 @@ class MainActivity : BaseActivity( if (state is UiState.Success) { when (state.data) { is MainState.NicknameNull -> { - intent.putExtra("force", true) - startActivity() + UserInfoActivity.start( + this@MainActivity, + UserInfoActivity.Args(force = true) + ) } is MainState.LoggedOut -> { - startActivity() + LoginActivity.start(this@MainActivity) finishAffinity() } diff --git a/app/src/main/java/com/eatssu/android/presentation/base/ActivityCompanion.kt b/app/src/main/java/com/eatssu/android/presentation/base/ActivityCompanion.kt new file mode 100644 index 000000000..069d3fea8 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/base/ActivityCompanion.kt @@ -0,0 +1,200 @@ +package com.eatssu.android.presentation.base + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Parcelable +import androidx.core.content.IntentCompat.getParcelableExtra +import kotlin.reflect.KClass + +private const val INTENT_ARGS_KEY = "intent_args" + +/** + * ActivityCompanion 패턴: Extra 정보가 필요한 Intent를 type-safe 하게 생성하고 한 가지 공통된 방법으로 Activity를 실행하기 위해 사용합니다. + * + * @see ActivityCompanion 인자가 없는 기본 타입 + * @see ActivityCompanionWithArgs 인자가 필요한 타입 + * @see ActivityCompanionWithArgsDefault 기본 인자를 제공하는 타입 + */ + +/** + * 인자가 없는 Activity를 위한 Companion 클래스 + * + * ## 사용 예시 + * ```kotlin + * class MainActivity : AppCompatActivity() { + * companion object : ActivityCompanion(MainActivity::class) + * } + * + * // 다른 곳에서 사용 + * MainActivity.start(context) + * val intent = MainActivity.intent(context) + * ``` + * + * @property activityClass 실행할 Activity의 KClass + */ +abstract class ActivityCompanion( + protected val activityClass: KClass, +) { + /** + * Intent를 생성합니다. + * + * @param context Context + * @param intentBuilder Intent에 추가 설정을 할 수 있는 람다 (예: flags 설정) + * @return 생성된 Intent + */ + fun intent( + context: Context, intentBuilder: Intent.() -> Unit = {} + ): Intent = Intent(context, activityClass.java).apply { + intentBuilder() + } + + /** + * Activity를 실행합니다. + * + * @param context Context + * @param intentBuilder Intent에 추가 설정을 할 수 있는 람다 + */ + fun start(context: Context, intentBuilder: Intent.() -> Unit = {}) { + context.startActivity(intent(context, intentBuilder)) + } + +} + +/** + * 타입 안전한 인자를 받는 Activity를 위한 Companion 클래스 + * Args 타입은 Activity 내에 선언해 DetailActivity.Args 처럼 사용하는 것을 권장합니다. + * 필요하다면 intentOptions의 필드를 lazy 프로퍼티로 감싸 사용하세요. + * + * ## 사용 예시 + * ```kotlin + * class DetailActivity : AppCompatActivity() { + * @Parcelize + * data class Args(val id: Int, val title: String) : Parcelable + * + * companion object : ActivityCompanionWithArgs( + * DetailActivity::class, + * Args::class + * ) + * + * private val args by lazy { intentOptions } + * + * override fun onCreate(savedInstanceState: Bundle?) { + * super.onCreate(savedInstanceState) + * val id = args?.id // type-safe하게 인자 접근 + * } + * } + * + * // 다른 곳에서 사용 + * DetailActivity.start(context, DetailActivity.Args(id = 1, title = "제목")) + * ``` + * + * @property activityClass 실행할 Activity의 KClass + * @property argsClass 인자로 전달할 Parcelable 데이터 클래스의 KClass + * @param TArgs Parcelable을 구현한 인자 타입 + */ +abstract class ActivityCompanionWithArgs( + protected val activityClass: KClass, + protected val argsClass: KClass, +) where TArgs : Parcelable { + /** + * 타입 안전한 인자를 포함한 Intent를 생성합니다. + * + * @param context Context + * @param args Activity에 전달할 Parcelable 인자 + * @param intentBuilder Intent에 추가 설정을 할 수 있는 람다 + * @return 인자가 포함된 Intent + */ + fun intent( + context: Context, args: TArgs, intentBuilder: Intent.() -> Unit = {} + ): Intent = Intent(context, activityClass.java).apply { + putExtra(INTENT_ARGS_KEY, args) + intentBuilder() + } + + /** + * 타입 안전한 인자와 함께 Activity를 실행합니다. + * + * @param context Context + * @param args Activity에 전달할 Parcelable 인자 + * @param intentBuilder Intent에 추가 설정을 할 수 있는 람다 + */ + fun start(context: Context, args: TArgs, intentBuilder: Intent.() -> Unit = {}) { + context.startActivity(intent(context, args, intentBuilder)) + } + + /** + * Activity 내부에서 전달받은 인자를 타입 안전하게 꺼내는 확장 프로퍼티 + * + * 사용 예시: + * ```kotlin + * private val postId by lazy { intentOptions?.postId } + * ``` + */ + val Activity.intentOptions: TArgs? + get() = getParcelableExtra( + this.intent, INTENT_ARGS_KEY, argsClass.java + ) +} + +/** + * 커스텀 인자와 기본 인자가 필요한 Activity를 위한 Companion 클래스 + * + * ActivityCompanionWithArgs를 상속하여 기본 인자로 실행하는 오버로드를 추가로 제공합니다. + * - `start(context)` - 기본 인자로 실행 + * - `start(context, customArgs)` - 커스텀 인자로 실행 + * + * ## 사용 예시 + * ```kotlin + * class SettingsActivity : AppCompatActivity() { + * @Parcelize + * data class Args(val section: String = "general") : Parcelable + * + * companion object : ActivityCompanionWithArgsDefault( + * SettingsActivity::class, + * Args::class, + * { Args() } + * ) + * } + * + * // 기본 인자로 실행: SettingsArgs(section = "general") + * SettingsActivity.start(context) + * + * // 커스텀 인자로 실행: SettingsArgs(section = "privacy") + * SettingsActivity.start(context, SettingsActivity.Args(section = "privacy")) + * ``` + * + * @property defaultArgs Context를 받아 기본 인자를 생성하는 람다 + * @param TArgs Parcelable을 구현한 인자 타입 + */ +abstract class ActivityCompanionWithArgsDefault( + activityClass: KClass, + argsClass: KClass, + private val defaultArgs: (Context) -> TArgs, +) : ActivityCompanionWithArgs(activityClass, argsClass) where TArgs : Parcelable { + + /** + * 기본 인자로 Intent를 생성합니다. + * + * @param context Context + * @param intentBuilder Intent에 추가 설정을 할 수 있는 람다 + * @return 기본 인자가 포함된 Intent + */ + fun intent( + context: Context, intentBuilder: Intent.() -> Unit = {} + ): Intent = Intent(context, activityClass.java).apply { + putExtra(INTENT_ARGS_KEY, defaultArgs(context)) + intentBuilder() + } + + /** + * 기본 인자로 Activity를 실행합니다. + * + * @param context Context + * @param intentBuilder Intent에 추가 설정을 할 수 있는 람다 + */ + fun start(context: Context, intentBuilder: Intent.() -> Unit = {}) { + context.startActivity(intent(context, intentBuilder)) + } + +} diff --git a/app/src/main/java/com/eatssu/android/presentation/base/BaseActivity.kt b/app/src/main/java/com/eatssu/android/presentation/base/BaseActivity.kt index 3f1760db6..287b82650 100644 --- a/app/src/main/java/com/eatssu/android/presentation/base/BaseActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/base/BaseActivity.kt @@ -136,9 +136,9 @@ abstract class BaseActivity( } private fun navigateToLogin() { - startActivity(Intent(this, LoginActivity::class.java).apply { + LoginActivity.start(this) { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - }) + } finishAffinity() } @@ -169,8 +169,7 @@ abstract class BaseActivity( } private fun showForceUpdateDialog() { - val intent = Intent(this, ForceUpdateDialogActivity::class.java) - startActivity(intent) + ForceUpdateDialogActivity.start(this) } override fun onResume() { diff --git a/app/src/main/java/com/eatssu/android/presentation/base/FragmentCompanion.kt b/app/src/main/java/com/eatssu/android/presentation/base/FragmentCompanion.kt new file mode 100644 index 000000000..a436c9c45 --- /dev/null +++ b/app/src/main/java/com/eatssu/android/presentation/base/FragmentCompanion.kt @@ -0,0 +1,145 @@ +package com.eatssu.android.presentation.base + +import android.os.Bundle +import android.os.Parcelable +import androidx.core.os.BundleCompat +import androidx.fragment.app.Fragment +import kotlin.reflect.KClass + +private const val FRAGMENT_ARGS_KEY = "fragment_args" + +/** + * FragmentCompanion 패턴: Arguments 정보가 필요한 Fragment를 type-safe 하게 생성하고 한 가지 공통된 방법으로 인스턴스를 만들기 위해 사용합니다. + * + * @see FragmentCompanion 인자가 없는 기본 타입 + * @see FragmentCompanionWithArgs 인자가 필요한 타입 + */ + +/** + * 인자가 없는 Fragment를 위한 Companion 클래스 + * + * ## 사용 예시 + * ```kotlin + * class HomeFragment : Fragment() { + * companion object : FragmentCompanion(::HomeFragment) + * } + * + * // 다른 곳에서 사용 + * val fragment = HomeFragment.newInstance() + * ``` + * + * @property fragmentBuilder Fragment 인스턴스를 생성하는 람다 + */ +abstract class FragmentCompanion( + private val fragmentBuilder: () -> Fragment, +) { + fun newInstance(): Fragment { + return fragmentBuilder() + } +} + +/** + * 타입 안전한 인자를 받는 Fragment를 위한 Companion 클래스 + * Args 타입은 Fragment 내에 선언해 DetailFragment.Args 처럼 사용하는 것을 권장합니다. + * 필요하다면 fragmentOptions의 필드를 lazy 프로퍼티로 감싸 사용하세요. + * + * ## 사용 예시 + * ```kotlin + * class DetailFragment : Fragment() { + * @Parcelize + * data class Args(val id: Int, val title: String) : Parcelable + * + * companion object : FragmentCompanionWithArgs( + * ::DetailFragment, + * Args::class + * ) + * + * private val args by lazy { fragmentOptions } + * + * override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + * super.onViewCreated(view, savedInstanceState) + * val id = args?.id // type-safe하게 인자 접근 + * } + * } + * + * // 다른 곳에서 사용 + * val fragment = DetailFragment.newInstance(DetailFragment.Args(id = 1, title = "제목")) + * ``` + * + * @property fragmentBuilder Fragment 인스턴스를 생성하는 람다 + * @property argsClass 인자로 전달할 Parcelable 데이터 클래스의 KClass + * @param TArgs Parcelable을 구현한 인자 타입 + */ +abstract class FragmentCompanionWithArgs( + private val fragmentBuilder: () -> Fragment, + private val argsClass: KClass +) where TArgs : Parcelable { + + fun newInstance(args: TArgs): Fragment { + return fragmentBuilder().apply { + arguments = Bundle().apply { + putParcelable(FRAGMENT_ARGS_KEY, args) + } + } + } + + /** + * Fragment 내부에서 전달받은 인자를 타입 안전하게 꺼내는 확장 프로퍼티 + * + * 사용 예시: + * ```kotlin + * private val postId by lazy { fragmentOptions?.postId } + * ``` + */ + val Fragment.fragmentOptions: TArgs? + get() = arguments?.let { + BundleCompat.getParcelable(it, FRAGMENT_ARGS_KEY, argsClass.java) + } +} + +/** + * 커스텀 인자와 기본 인자가 필요한 Fragment를 위한 Companion 클래스 + * + * FragmentCompanionWithArgs를 상속하여 기본 인자로 실행하는 오버로드를 추가로 제공합니다. + * - `newInstance()` - 기본 인자로 생성 + * - `newInstance(customArgs)` - 커스텀 인자로 생성 + * + * ## 사용 예시 + * ```kotlin + * class SettingsFragment : Fragment() { + * @Parcelize + * data class Args(val section: String = "general") : Parcelable + * + * companion object : FragmentCompanionWithArgsDefault( + * ::SettingsFragment, + * Args::class, + * { Args() } + * ) + * } + * + * // 기본 인자로 생성: Args(section = "general") + * SettingsFragment.newInstance() + * + * // 커스텀 인자로 생성: Args(section = "privacy") + * SettingsFragment.newInstance(SettingsFragment.Args(section = "privacy")) + * ``` + * + * @property defaultArgs 기본 인자를 생성하는 람다 + * @param TArgs Parcelable을 구현한 인자 타입 + */ +abstract class FragmentCompanionWithArgsDefault( + fragmentBuilder: () -> Fragment, + argsClass: KClass, + private val defaultArgs: () -> TArgs, +) : FragmentCompanionWithArgs(fragmentBuilder, argsClass) where TArgs : Parcelable { + + /** + * 기본 인자로 Fragment의 새 인스턴스를 생성합니다. + * + * @return 기본 인자가 포함된 Fragment 인스턴스 + */ + fun newInstance(): Fragment { + return newInstance(defaultArgs()) + } + +} diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuSubAdapter.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuSubAdapter.kt index ef2092677..b4ab78e81 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuSubAdapter.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/menu/MenuSubAdapter.kt @@ -1,17 +1,14 @@ package com.eatssu.android.presentation.cafeteria.menu -import android.content.Intent -import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup -import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.eatssu.android.databinding.ItemMenuBinding import com.eatssu.android.domain.model.Menu import com.eatssu.android.presentation.cafeteria.review.list.ReviewActivity import com.eatssu.common.EventLogger -import com.eatssu.common.enums.MenuType import com.eatssu.common.enums.Restaurant +import timber.log.Timber class MenuSubAdapter( @@ -47,24 +44,17 @@ class MenuSubAdapter( //intent 사용 holder.itemView.setOnClickListener { - val intent = Intent(holder.itemView.context, ReviewActivity::class.java) - - when (restaurant.menuType) { - MenuType.FIXED -> { - Log.d("SubMenuAdapter", "고정메뉴${dataList[position].name}") - intent.putExtra("itemId", dataList[position].id) - intent.putExtra("itemName", dataList[position].name) - intent.putExtra("menuType", MenuType.FIXED.toString()) - } - - MenuType.VARIABLE -> { - Log.d("SubMenuAdapter", "변동메뉴${dataList[position].name}") - intent.putExtra("itemId", dataList[position].id) - intent.putExtra("itemName", dataList[position].name) - intent.putExtra("menuType", MenuType.VARIABLE.toString()) - } - } - ContextCompat.startActivity(holder.itemView.context, intent, null) + val menuType = restaurant.menuType.toString() + Timber.d("SubMenuAdapter - ${restaurant.menuType}메뉴${dataList[position].name}") + + ReviewActivity.start( + holder.itemView.context, + ReviewActivity.Args( + menuType = menuType, + itemId = dataList[position].id, + itemName = dataList[position].name + ) + ) EventLogger.clickMenu(restaurant) } } diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewActivity.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewActivity.kt index f6f6f38a5..0d1ee0390 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewActivity.kt @@ -1,7 +1,7 @@ package com.eatssu.android.presentation.cafeteria.review.list -import android.content.Intent import android.os.Bundle +import android.os.Parcelable import android.view.View import androidx.activity.viewModels import androidx.fragment.app.DialogFragment @@ -10,6 +10,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.eatssu.android.R import com.eatssu.android.databinding.ActivityReviewBinding import com.eatssu.android.domain.model.Review +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.cafeteria.review.write.ReviewWriteRateActivity import com.eatssu.android.presentation.cafeteria.review.write.menu.ReviewWriteMenuActivity @@ -21,26 +22,34 @@ import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import timber.log.Timber -import kotlin.properties.Delegates @AndroidEntryPoint class ReviewActivity : BaseActivity(ActivityReviewBinding::inflate, ScreenId.REVIEW_V1_VIEW), MyReviewBottomSheetFragment.OnReviewDeletedListener { - private val reviewViewModel: ReviewViewModel by viewModels() + @Parcelize + data class Args( + val menuType: String, + val itemId: Long, + val itemName: String + ) : Parcelable + + companion object : ActivityCompanionWithArgs(ReviewActivity::class, Args::class) - private lateinit var menuType: String - private var itemId by Delegates.notNull() + private val reviewViewModel: ReviewViewModel by viewModels() - private lateinit var itemName: String + private val menuType by lazy { intentOptions?.menuType ?: "" } + private val itemId by lazy { intentOptions?.itemId ?: 0 } + private val itemName by lazy { intentOptions?.itemName?.replace(Regex("[\\[\\]]"), "") ?: "" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) toolbarTitle.text = "리뷰" // 툴바 제목 설정 - getIndex() + Timber.d("메뉴는 $itemName $menuType $itemId") lodeData() bindData() setClickListener() @@ -56,15 +65,6 @@ class ReviewActivity : } - private fun getIndex() { - //get menuId - menuType = intent.getStringExtra("menuType").toString() - itemId = intent.getLongExtra("itemId", 0) - itemName = intent.getStringExtra("itemName").toString().replace(Regex("[\\[\\]]"), "") - - Timber.d("메뉴는 $itemName $menuType $itemId") - } - private fun lodeData() { //Todo 여기서는 메뉴 타입이 뭔지 몰라도 됨. 추상화 해도 됨 @@ -150,21 +150,27 @@ class ReviewActivity : when (menuType) { MenuType.FIXED.name -> { binding.btnNextReview.setOnClickListener { - val intent = Intent(this, ReviewWriteRateActivity::class.java) - intent.putExtra("itemId", itemId) - intent.putExtra("itemName", itemName) - intent.putExtra("menuType", menuType) - startActivity(intent) + ReviewWriteRateActivity.start( + this, + ReviewWriteRateActivity.Args( + itemName = itemName, + itemId = itemId, + menuType = menuType + ) + ) EventLogger.writeReview() } } MenuType.VARIABLE.name -> { binding.btnNextReview.setOnClickListener { - val intent = Intent(this, ReviewWriteMenuActivity::class.java) - intent.putExtra("itemId", itemId) - intent.putExtra("menuType", menuType) - startActivity(intent) + ReviewWriteMenuActivity.start( + this, + ReviewWriteMenuActivity.Args( + itemId = itemId, + menuType = menuType + ) + ) EventLogger.writeReview() } } @@ -177,17 +183,18 @@ class ReviewActivity : fun onMyReviewClicked(review: Review) { - val modalBottomSheet = MyReviewBottomSheetFragment().apply { - arguments = Bundle().apply { - putLong("reviewId", review.reviewId) - putString("menu", review.menu) - putString("content", review.content) - putInt("mainGrade", review.mainGrade) - putInt("amountGrade", review.amountGrade) - putInt("tasteGrade", review.tasteGrade) - } - onReviewDeletedListener = this@ReviewActivity - } + val modalBottomSheet = MyReviewBottomSheetFragment.newInstance( + MyReviewBottomSheetFragment.Args( + reviewId = review.reviewId, + menu = review.menu, + content = review.content, + mainGrade = review.mainGrade, + amountGrade = review.amountGrade, + tasteGrade = review.tasteGrade + ) + ) as MyReviewBottomSheetFragment + + modalBottomSheet.onReviewDeletedListener = this@ReviewActivity modalBottomSheet.setStyle( DialogFragment.STYLE_NORMAL, R.style.RoundCornerBottomSheetDialogTheme @@ -196,17 +203,17 @@ class ReviewActivity : } fun onOthersReviewClicked(reviewData: Review) { - val modalBottomSheet = OthersBottomSheetFragment() + val modalBottomSheet = OthersBottomSheetFragment.newInstance( + OthersBottomSheetFragment.Args( + reviewId = reviewData.reviewId, + menu = reviewData.menu + ) + ) as OthersBottomSheetFragment + modalBottomSheet.setStyle( DialogFragment.STYLE_NORMAL, R.style.RoundCornerBottomSheetDialogTheme ) - - modalBottomSheet.arguments = Bundle().apply { - putLong("reviewId", reviewData.reviewId) - putString("menu", reviewData.menu) - } - modalBottomSheet.show(supportFragmentManager, "Open Bottom Sheet") } diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewActivity.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewActivity.kt index 82c14586f..073e08fb9 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/modify/ModifyReviewActivity.kt @@ -1,16 +1,19 @@ package com.eatssu.android.presentation.cafeteria.review.modify import android.os.Bundle +import android.os.Parcelable import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import com.eatssu.android.data.dto.request.ModifyReviewRequest import com.eatssu.android.databinding.ActivityFixMenuBinding +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.util.showToast import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import timber.log.Timber @AndroidEntryPoint @@ -19,6 +22,18 @@ class ModifyReviewActivity : BaseActivity( ScreenId.REVIEW_V1_MODIFY ) { + @Parcelize + data class Args( + val reviewId: Long, + val menu: String, + val content: String, + val mainGrade: Int, + val amountGrade: Int, + val tasteGrade: Int + ) : Parcelable + + companion object : ActivityCompanionWithArgs(ModifyReviewActivity::class, Args::class) + private val modifyViewModel: ModifyViewModel by viewModels() private var reviewId = -1L @@ -48,14 +63,14 @@ class ModifyReviewActivity : BaseActivity( private fun getIndex() { + val args = intentOptions + reviewId = args?.reviewId ?: -1L + menu = args?.menu ?: "" + content = args?.content ?: "" - reviewId = intent.getLongExtra("reviewId", -1L) - menu = intent.getStringExtra("menu").toString() - content = intent.getStringExtra("content").toString() - - main = intent.getIntExtra("mainGrade", 0) - amount = intent.getIntExtra("amountGrade", 0) - taste = intent.getIntExtra("tasteGrade", 0) + main = args?.mainGrade ?: 0 + amount = args?.amountGrade ?: 0 + taste = args?.tasteGrade ?: 0 Timber.tag("ReviewFixedActivity") .d("reviewID: %s, menu: %s, content: %s", reviewId.toString(), menu, content) @@ -65,8 +80,8 @@ class ModifyReviewActivity : BaseActivity( binding.menu.text = menu binding.etReview2Comment.setText(content) binding.rbMain.rating = main.toFloat() - binding.rbAmount.rating = intent.getIntExtra("amountGrade", 0).toFloat() - binding.rbTaste.rating = intent.getIntExtra("tasteGrade", 0).toFloat() + binding.rbAmount.rating = amount.toFloat() + binding.rbTaste.rating = taste.toFloat() } private fun postData(reviewId: Long) { diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt index dec710a88..fbbb28623 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/report/ReportActivity.kt @@ -1,11 +1,13 @@ package com.eatssu.android.presentation.cafeteria.review.report import android.os.Bundle +import android.os.Parcelable import android.text.Editable import android.text.TextWatcher import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import com.eatssu.android.databinding.ActivityReportBinding +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.util.showToast import com.eatssu.common.enums.ReportType @@ -13,6 +15,7 @@ import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize @AndroidEntryPoint @@ -20,6 +23,11 @@ class ReportActivity : BaseActivity( ActivityReportBinding::inflate, ScreenId.REVIEW_REPORT ) { + @Parcelize + data class Args(val reviewId: Long) : Parcelable + + companion object : ActivityCompanionWithArgs(ReportActivity::class, Args::class) + private val reportViewModel: ReportViewModel by viewModels() private var reviewId = -1L @@ -34,7 +42,7 @@ class ReportActivity : BaseActivity( super.onCreate(savedInstanceState) toolbarTitle.text = "신고하기" // 툴바 제목 설정 - reviewId = intent.getLongExtra("reviewId", -1L) + reviewId = intentOptions?.reviewId ?: -1L binding.etReportComment.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {} diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/ReviewWriteRateActivity.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/ReviewWriteRateActivity.kt index 53dc53a0b..ccdde25b6 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/ReviewWriteRateActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/ReviewWriteRateActivity.kt @@ -2,6 +2,7 @@ package com.eatssu.android.presentation.cafeteria.review.write import android.net.Uri import android.os.Bundle +import android.os.Parcelable import android.text.Editable import android.text.TextWatcher import android.view.View @@ -18,6 +19,7 @@ import com.eatssu.android.data.dto.request.WriteReviewRequest import com.eatssu.android.databinding.ActivityReviewWriteRateBinding import com.eatssu.android.presentation.UiEvent import com.eatssu.android.presentation.UiState +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.util.showToast import com.eatssu.common.EventLogger @@ -25,6 +27,7 @@ import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import id.zelory.compressor.Compressor import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import timber.log.Timber import java.io.File @@ -35,11 +38,21 @@ class ReviewWriteRateActivity : ScreenId.REVIEW_V1_WRITE_RATE ) { + @Parcelize + data class Args( + val itemName: String, + val itemId: Long, + val itemCount: Long = 1L, + val menuType: String? = null + ) : Parcelable + + companion object : ActivityCompanionWithArgs(ReviewWriteRateActivity::class, Args::class) + private val viewModel: UploadReviewViewModel by viewModels() - private var itemId: Long = 0 - private lateinit var itemName: String - private var itemCount = 1L + private val itemId by lazy { intentOptions?.itemId ?: -1L } + private val itemName by lazy { intentOptions?.itemName ?: "" } + private val itemCount by lazy { intentOptions?.itemCount ?: 1L } private var comment: String? = "" private var imageFile: File? = null @@ -49,12 +62,8 @@ class ReviewWriteRateActivity : toolbarTitle.text = "리뷰 남기기" // 툴바 제목 설정 binding.viewModel = viewModel - itemName = intent.getStringExtra("itemName").toString() Timber.d("고정메뉴 $itemName") - itemId = intent.getLongExtra("itemId", -1) - itemCount = intent.getLongExtra("itemCount", 1) - // 현재 메뉴명을 표시합니다. binding.menu.text = itemName diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/menu/ReviewWriteMenuActivity.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/menu/ReviewWriteMenuActivity.kt index a1267f073..2e264867f 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/menu/ReviewWriteMenuActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/write/menu/ReviewWriteMenuActivity.kt @@ -1,17 +1,19 @@ package com.eatssu.android.presentation.cafeteria.review.write.menu -import android.content.Intent import android.os.Bundle +import android.os.Parcelable import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.eatssu.android.databinding.ActivityReviewWriteMenuBinding +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.cafeteria.review.write.ReviewWriteRateActivity import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import timber.log.Timber @AndroidEntryPoint @@ -21,8 +23,16 @@ class ReviewWriteMenuActivity : ScreenId.REVIEW_V1_WRITE ) { + @Parcelize + data class Args( + val itemId: Long, + val menuType: String? = null + ) : Parcelable + + companion object : ActivityCompanionWithArgs(ReviewWriteMenuActivity::class, Args::class) + private val viewModel: VariableMenuViewModel by viewModels() - private var mealId: Long = -1 + private val mealId by lazy { intentOptions?.itemId ?: -1 } private lateinit var variableMenuPickAdapter: VariableMenuPickAdapter @@ -30,16 +40,11 @@ class ReviewWriteMenuActivity : super.onCreate(savedInstanceState) toolbarTitle.text = "리뷰 남기기" // 툴바 제목 설정 - getIndex() loadData() bindData() setClickListener() } - fun getIndex() { - mealId = intent.getLongExtra("itemId", -1) - } - fun loadData() { viewModel.findMenuItemByMealId(mealId) } @@ -76,14 +81,15 @@ class ReviewWriteMenuActivity : val currentItem = items[i] - // 다음 아이템을 전달하기 위해 Intent 생성 - val intent = Intent(this, ReviewWriteRateActivity::class.java) - intent.putExtra("itemName", currentItem.first) - intent.putExtra("itemId", currentItem.second) - intent.putExtra("itemCount", items.size.toLong()) - - // BActivity 실행 - startActivity(intent) + // 다음 아이템을 전달하기 위해 새로운 companion 패턴 사용 + ReviewWriteRateActivity.start( + this, + ReviewWriteRateActivity.Args( + itemName = currentItem.first, + itemId = currentItem.second, + itemCount = items.size.toLong() + ) + ) // 만약 마지막 아이템이면 현재 액티비티 종료 if (i == items.size - 1) { diff --git a/app/src/main/java/com/eatssu/android/presentation/common/AndroidMessageDialogActivity.kt b/app/src/main/java/com/eatssu/android/presentation/common/AndroidMessageDialogActivity.kt index b76ceb6d9..c5d48a1e2 100644 --- a/app/src/main/java/com/eatssu/android/presentation/common/AndroidMessageDialogActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/common/AndroidMessageDialogActivity.kt @@ -3,12 +3,21 @@ package com.eatssu.android.presentation.common import android.app.AlertDialog import android.os.Bundle -import android.util.Log +import android.os.Parcelable import androidx.appcompat.app.AppCompatActivity +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs +import kotlinx.parcelize.Parcelize +import timber.log.Timber class AndroidMessageDialogActivity : AppCompatActivity() { + @Parcelize + data class Args(val message: String) : Parcelable + + companion object : + ActivityCompanionWithArgs(AndroidMessageDialogActivity::class, Args::class) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) showDialog() @@ -18,9 +27,9 @@ class AndroidMessageDialogActivity : AppCompatActivity() { val builder = AlertDialog.Builder(this) builder.setTitle("공지") - val message = intent.getStringExtra("message") - Log.d("message",message.toString()) - builder.setMessage(intent.getStringExtra("message")) + val message = intentOptions?.message + Timber.tag("message").d(message.toString()) + builder.setMessage(message) builder.setPositiveButton("확인") { dialog, which -> // Google Play Store의 앱 페이지로 이동하여 업데이트를 다운로드합니다. diff --git a/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt b/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt index 4e241bbaa..52f2ab10e 100644 --- a/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/common/ForceUpdateActivity.kt @@ -6,10 +6,13 @@ import android.content.Intent import android.net.Uri import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import com.eatssu.android.presentation.base.ActivityCompanion class ForceUpdateDialogActivity : AppCompatActivity() { + companion object : ActivityCompanion(ForceUpdateDialogActivity::class) + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) showForceUpdateDialog() diff --git a/app/src/main/java/com/eatssu/android/presentation/common/MyReviewBottomSheetFragment.kt b/app/src/main/java/com/eatssu/android/presentation/common/MyReviewBottomSheetFragment.kt index 2d4220c59..196d9cf2c 100644 --- a/app/src/main/java/com/eatssu/android/presentation/common/MyReviewBottomSheetFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/common/MyReviewBottomSheetFragment.kt @@ -1,7 +1,7 @@ package com.eatssu.android.presentation.common -import android.content.Intent import android.os.Bundle +import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,13 +11,15 @@ import androidx.lifecycle.lifecycleScope import com.eatssu.android.App import com.eatssu.android.R import com.eatssu.android.databinding.FragmentBottomsheetMyReviewBinding -import com.eatssu.android.presentation.mypage.myreview.MyReviewViewModel +import com.eatssu.android.presentation.base.FragmentCompanionWithArgs import com.eatssu.android.presentation.cafeteria.review.modify.ModifyReviewActivity +import com.eatssu.android.presentation.mypage.myreview.MyReviewViewModel import com.eatssu.android.presentation.util.showToast import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize import timber.log.Timber @AndroidEntryPoint @@ -25,6 +27,21 @@ class MyReviewBottomSheetFragment : BottomSheetDialogFragment() { private var _binding: FragmentBottomsheetMyReviewBinding? = null private val binding get() = _binding!! + @Parcelize + data class Args( + val reviewId: Long, + val menu: String, + val content: String, + val mainGrade: Int, + val amountGrade: Int, + val tasteGrade: Int + ) : Parcelable + + companion object : FragmentCompanionWithArgs( + ::MyReviewBottomSheetFragment, + Args::class + ) + interface OnReviewDeletedListener { fun onReviewDeleted() } @@ -33,13 +50,6 @@ class MyReviewBottomSheetFragment : BottomSheetDialogFragment() { private val viewModel: MyReviewViewModel by activityViewModels() - var reviewId = -1L - var menu = "" - var content = "" - var mainGrade = -1 - var amountGrade = -1 - var tasteGrade = -1 - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -52,30 +62,22 @@ class MyReviewBottomSheetFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - arguments?.let { - reviewId = it.getLong("reviewId") - menu = it.getString("menu").toString() - content = it.getString("content").toString() - mainGrade = it.getInt("mainGrade") - amountGrade = it.getInt("amountGrade") - tasteGrade = it.getInt("tasteGrade") - } + val args = fragmentOptions ?: return - Timber.d("넘겨받은 리뷰 정보: $reviewId $menu $content $reviewId") + Timber.d("넘겨받은 리뷰 정보: ${args.reviewId} ${args.menu} ${args.content}") binding.llModify.setOnClickListener { - val intent = Intent(requireContext(), ModifyReviewActivity::class.java) - - intent.let { - it.putExtra("reviewId", reviewId) - it.putExtra("menu", menu) - it.putExtra("content", content) - it.putExtra("mainGrade", mainGrade) - it.putExtra("amountGrade", amountGrade) - it.putExtra("tasteGrade", tasteGrade) - } - - startActivity(intent) + ModifyReviewActivity.start( + requireContext(), + ModifyReviewActivity.Args( + reviewId = args.reviewId, + menu = args.menu, + content = args.content, + mainGrade = args.mainGrade, + amountGrade = args.amountGrade, + tasteGrade = args.tasteGrade + ) + ) dismiss() } @@ -87,7 +89,7 @@ class MyReviewBottomSheetFragment : BottomSheetDialogFragment() { activity?.showToast(App.appContext.getString(R.string.delete_undo)) } setPositiveButton("삭제") { _, _ -> - viewModel.deleteReview(reviewId) + viewModel.deleteReview(args.reviewId) lifecycleScope.launch { viewModel.uiState.collectLatest { if (it.isDeleted) { diff --git a/app/src/main/java/com/eatssu/android/presentation/common/OthersBottomSheetFragment.kt b/app/src/main/java/com/eatssu/android/presentation/common/OthersBottomSheetFragment.kt index 501482877..8ebddef51 100644 --- a/app/src/main/java/com/eatssu/android/presentation/common/OthersBottomSheetFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/common/OthersBottomSheetFragment.kt @@ -1,14 +1,16 @@ package com.eatssu.android.presentation.common -import android.content.Intent import android.os.Bundle +import android.os.Parcelable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.eatssu.android.databinding.FragmentBottomsheetOthersBinding +import com.eatssu.android.presentation.base.FragmentCompanionWithArgs import com.eatssu.android.presentation.cafeteria.review.report.ReportActivity import com.google.android.material.bottomsheet.BottomSheetDialogFragment import dagger.hilt.android.AndroidEntryPoint +import kotlinx.parcelize.Parcelize import timber.log.Timber @AndroidEntryPoint @@ -16,8 +18,16 @@ class OthersBottomSheetFragment : BottomSheetDialogFragment() { private var _binding: FragmentBottomsheetOthersBinding? = null private val binding get() = _binding!! - var reviewId = -1L - var menu = "" + @Parcelize + data class Args( + val reviewId: Long, + val menu: String + ) : Parcelable + + companion object : FragmentCompanionWithArgs( + ::OthersBottomSheetFragment, + Args::class + ) override fun onCreateView( inflater: LayoutInflater, @@ -31,19 +41,16 @@ class OthersBottomSheetFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - arguments?.let { - reviewId = it.getLong("reviewId") - menu = it.getString("menu").toString() - } + val args = fragmentOptions ?: return - Timber.d("넘겨받은 리뷰 정보: $reviewId $menu") + Timber.d("넘겨받은 리뷰 정보: ${args.reviewId} ${args.menu}") binding.llReport.setOnClickListener { - - val intent = Intent(requireContext(), ReportActivity::class.java) - intent.putExtra("reviewId", reviewId) - Timber.d("reviewId $reviewId") - startActivity(intent) + Timber.d("reviewId ${args.reviewId}") + ReportActivity.start( + requireContext(), + ReportActivity.Args(args.reviewId) + ) dismiss() } } diff --git a/app/src/main/java/com/eatssu/android/presentation/intro/IntroActivity.kt b/app/src/main/java/com/eatssu/android/presentation/intro/IntroActivity.kt index e4d40fc4f..70c3e8e7c 100644 --- a/app/src/main/java/com/eatssu/android/presentation/intro/IntroActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/intro/IntroActivity.kt @@ -1,6 +1,7 @@ package com.eatssu.android.presentation.intro import android.os.Bundle +import android.os.Parcelable import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -9,19 +10,24 @@ import com.eatssu.android.databinding.ActivityIntroBinding import com.eatssu.android.presentation.MainActivity import com.eatssu.android.presentation.UiEvent import com.eatssu.android.presentation.UiState +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.login.LoginActivity import com.eatssu.android.presentation.util.showToast -import com.eatssu.android.presentation.util.startActivity import com.eatssu.common.EventLogger import com.eatssu.common.enums.LaunchPath import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize @AndroidEntryPoint class IntroActivity : AppCompatActivity() { + @Parcelize + data class Args(val launchPath: LaunchPath) : Parcelable + companion object : ActivityCompanionWithArgs(IntroActivity::class, Args::class) + private val introViewModel: IntroViewModel by viewModels() private lateinit var binding: ActivityIntroBinding @@ -36,13 +42,13 @@ class IntroActivity : AppCompatActivity() { introViewModel.uiState.collectLatest { state -> when (state) { is UiState.Success -> { - startActivity() + MainActivity.start(this@IntroActivity) finish() } is UiState.Error -> { // 로그인 액티비티로 이동 - startActivity() + LoginActivity.start(this@IntroActivity) finish() } @@ -62,14 +68,8 @@ class IntroActivity : AppCompatActivity() { } private fun log() { - val launchPath = intent.getStringExtra("launch_path") - when (launchPath) { - "widget" -> EventLogger.appLaunch(LaunchPath.WIDGET) - "local_notification" -> EventLogger.appLaunch(LaunchPath.LOCAL_NOTIFICATION) - "remote_notification" -> EventLogger.appLaunch(LaunchPath.REMOTE_NOTIFICATION) - // launch_path가 없으면 일반적인 앱 아이콘 클릭으로 간주 - else -> EventLogger.appLaunch(LaunchPath.ICON) - } + val launchPath = intentOptions?.launchPath ?: LaunchPath.ICON + EventLogger.appLaunch(launchPath) } override fun onResume() { diff --git a/app/src/main/java/com/eatssu/android/presentation/login/LoginActivity.kt b/app/src/main/java/com/eatssu/android/presentation/login/LoginActivity.kt index 3abc4e64b..4c324a80d 100644 --- a/app/src/main/java/com/eatssu/android/presentation/login/LoginActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/login/LoginActivity.kt @@ -11,9 +11,9 @@ import com.eatssu.android.databinding.ActivityLoginBinding import com.eatssu.android.presentation.MainActivity import com.eatssu.android.presentation.UiEvent import com.eatssu.android.presentation.UiState +import com.eatssu.android.presentation.base.ActivityCompanion import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.util.showToast -import com.eatssu.android.presentation.util.startActivity import com.eatssu.common.enums.ScreenId import com.kakao.sdk.common.model.ClientError import com.kakao.sdk.common.model.ClientErrorCause @@ -30,6 +30,8 @@ class LoginActivity : ScreenId.LOGIN_LOGIN ) { + companion object : ActivityCompanion(LoginActivity::class) + private val loginViewModel: LoginViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { @@ -97,7 +99,7 @@ class LoginActivity : when (state) { is UiState.Loading -> showLoading(true) is UiState.Success -> { - startActivity() + MainActivity.start(this@LoginActivity) finishAffinity() } else -> { diff --git a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt index 1cab2000d..7d0be3af2 100644 --- a/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt +++ b/app/src/main/java/com/eatssu/android/presentation/map/MapFragmentView.kt @@ -4,7 +4,6 @@ import android.Manifest import android.app.Activity import android.content.Context import android.content.ContextWrapper -import android.content.Intent import android.content.pm.PackageManager import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult @@ -240,8 +239,7 @@ fun MapFragmentComposeView( onDismiss = { viewModel.toggleDepartmentBottomSheet() }, onInputClick = { viewModel.toggleDepartmentBottomSheet() - val intent = Intent(context, UserInfoActivity::class.java) - context.startActivity(intent) + UserInfoActivity.start(context) }, sheetState = sheetState ) diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/DeveloperActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/DeveloperActivity.kt index 89c44c0e8..eb78d81f4 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/DeveloperActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/DeveloperActivity.kt @@ -1,11 +1,11 @@ package com.eatssu.android.presentation.mypage -import android.content.Intent import android.os.Bundle import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.toColorInt import com.eatssu.android.R import com.eatssu.android.databinding.ActivityDeveloperBinding +import com.eatssu.android.presentation.base.ActivityCompanion import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.mypage.terms.WebViewActivity import com.eatssu.common.enums.ScreenId @@ -17,6 +17,8 @@ class DeveloperActivity : ActivityDeveloperBinding::inflate, ScreenId.MYPAGE_DEVELOPER ) { + + companion object : ActivityCompanion(DeveloperActivity::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -31,12 +33,14 @@ class DeveloperActivity : private fun clickRecruiting() { binding.imgRecruitingBanner.setOnClickListener { - val intent = Intent(this, WebViewActivity::class.java).apply { - putExtra("TITLE", "Who’s next?") - putExtra("URL", getString(R.string.recruiting_url)) - putExtra("SCREEN_ID", ScreenId.EXTERNAL_RECRUIT.name) - } - startActivity(intent) + WebViewActivity.start( + this, + WebViewActivity.Args( + url = getString(R.string.recruiting_url), + title = "Who's next?", + screenId = ScreenId.EXTERNAL_RECRUIT + ) + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt index e9dec35e6..29e49fdda 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/MyPageFragment.kt @@ -133,7 +133,7 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) private fun setOnClickListener() { binding.llMyInfo.setOnClickListener { - startActivity(Intent(requireContext(), UserInfoActivity::class.java)) + UserInfoActivity.start(requireContext(), UserInfoActivity.Args()) } binding.llInquire.setOnClickListener { @@ -145,7 +145,7 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) } binding.llMyReview.setOnClickListener { - startActivity(Intent(requireContext(), MyReviewListActivity::class.java)) + MyReviewListActivity.start(requireContext()) } binding.tvLogout.setOnClickListener { @@ -154,15 +154,15 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) binding.llSignout.setOnClickListener { // 현재 Success 상태에서 안전하게 닉네임 추출 - val nickname = (myPageViewModel.uiState.value as? UiState.Success)?.data?.nickname - Intent(requireContext(), SignOutActivity::class.java).apply { - putExtra("nickname", nickname) - startActivity(this) - } + val nickname = (myPageViewModel.uiState.value as? UiState.Success)?.data?.nickname ?: "" + SignOutActivity.start( + requireContext(), + SignOutActivity.Args(nickname) + ) } binding.llDeveloper.setOnClickListener { - startActivity(Intent(requireContext(), DeveloperActivity::class.java)) + DeveloperActivity.start(requireContext()) } binding.llOss.setOnClickListener { moveToOss() } @@ -212,7 +212,7 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) .setMessage("로그아웃 하시겠습니까?") .setPositiveButton("로그아웃") { _, _ -> mainViewModel.logOut() // 로그아웃은 메인 액티비티에서 처리하도록 수정 - startActivity(Intent(requireContext(), LoginActivity::class.java)) + LoginActivity.start(requireContext()) } .setNegativeButton("취소", null) .show() @@ -246,11 +246,9 @@ class MyPageFragment : BaseFragment(ScreenId.MYPAGE_MAIN) } private fun startWebView(url: String, title: String, screenId: ScreenId) { - val intent = Intent(requireContext(), WebViewActivity::class.java).apply { - putExtra("URL", url) - putExtra("TITLE", title) - putExtra("SCREEN_ID", screenId.name) - } - startActivity(intent) + WebViewActivity.start( + requireContext(), + WebViewActivity.Args(url, title, screenId) + ) } } diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt index e2b56f8f6..e2066f53f 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/SignOutActivity.kt @@ -2,12 +2,14 @@ package com.eatssu.android.presentation.mypage import android.content.Intent import android.os.Bundle +import android.os.Parcelable import androidx.activity.viewModels import androidx.core.widget.doAfterTextChanged import androidx.lifecycle.lifecycleScope import com.eatssu.android.databinding.ActivitySignOutBinding import com.eatssu.android.presentation.UiEvent import com.eatssu.android.presentation.UiState +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.login.LoginActivity import com.eatssu.android.presentation.util.showToast @@ -15,6 +17,7 @@ import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize @AndroidEntryPoint class SignOutActivity : @@ -24,6 +27,11 @@ class SignOutActivity : ) { //TODO 현재 dev서버 탈퇴하기 500 + @Parcelize + data class Args(val nickname: String) : Parcelable + + companion object : ActivityCompanionWithArgs(SignOutActivity::class, Args::class) + private val signOutViewModel: SignOutViewModel by viewModels() private var inputNickname: String = "" @@ -32,7 +40,7 @@ class SignOutActivity : super.onCreate(savedInstanceState) toolbarTitle.text = "탈퇴하기" // 툴바 제목 설정 - val nickname = intent.getStringExtra("nickname")?.trim() ?: "" + val nickname = intentOptions?.nickname?.trim() ?: "" binding.btnSignOut.isEnabled = false diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListActivity.kt index fa0e96307..95a2b3ac6 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/myreview/MyReviewListActivity.kt @@ -9,6 +9,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.eatssu.android.R import com.eatssu.android.databinding.ActivityMyReviewListBinding import com.eatssu.android.domain.model.Review +import com.eatssu.android.presentation.base.ActivityCompanion import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.common.MyReviewBottomSheetFragment import com.eatssu.common.enums.ScreenId @@ -22,6 +23,8 @@ class MyReviewListActivity : BaseActivity( ScreenId.MYPAGE_REVIEWS, ), MyReviewBottomSheetFragment.OnReviewDeletedListener { + companion object : ActivityCompanion(MyReviewListActivity::class) + private val myReviewViewModel: MyReviewViewModel by viewModels() lateinit var menu: String @@ -81,18 +84,18 @@ class MyReviewListActivity : BaseActivity( } fun onMyReviewClicked(review: Review) { - - val modalBottomSheet = MyReviewBottomSheetFragment().apply { - arguments = Bundle().apply { - putLong("reviewId", review.reviewId) - putString("menu", review.menu) - putString("content", review.content) - putInt("mainGrade", review.mainGrade) - putInt("amountGrade", review.amountGrade) - putInt("tasteGrade", review.tasteGrade) - } - onReviewDeletedListener = this@MyReviewListActivity - } + val modalBottomSheet = MyReviewBottomSheetFragment.newInstance( + MyReviewBottomSheetFragment.Args( + reviewId = review.reviewId, + menu = review.menu, + content = review.content, + mainGrade = review.mainGrade, + amountGrade = review.amountGrade, + tasteGrade = review.tasteGrade + ) + ) as MyReviewBottomSheetFragment + + modalBottomSheet.onReviewDeletedListener = this@MyReviewListActivity modalBottomSheet.setStyle( DialogFragment.STYLE_NORMAL, R.style.RoundCornerBottomSheetDialogTheme diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/terms/WebViewActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/terms/WebViewActivity.kt index c049d92f3..2f38eec7a 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/terms/WebViewActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/terms/WebViewActivity.kt @@ -1,11 +1,14 @@ package com.eatssu.android.presentation.mypage.terms import android.os.Bundle +import android.os.Parcelable import android.webkit.WebViewClient import com.eatssu.android.databinding.ActivityWebviewBinding +import com.eatssu.android.presentation.base.ActivityCompanionWithArgs import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.common.EventLogger import com.eatssu.common.enums.ScreenId +import kotlinx.parcelize.Parcelize import timber.log.Timber @@ -15,15 +18,18 @@ class WebViewActivity : ScreenId.EXTERNAL_INQUIRE // shouldLogScreenId가 false라 미사용 ) { + @Parcelize + data class Args( + val url: String, + val title: String, + val screenId: ScreenId + ) : Parcelable - private var URL = "" - private var TITLE = "" + companion object : ActivityCompanionWithArgs(WebViewActivity::class, Args::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - binding.webview.apply { webViewClient = WebViewClient() @@ -38,14 +44,14 @@ class WebViewActivity : useWideViewPort = true // 화면 크기에 맞게 웹 페이지를 조정 } - URL = intent.getStringExtra("URL") ?: "" //Todo 뷰모델 사용하도록 수정? - TITLE = intent.getStringExtra("TITLE") ?: "" + val title = intentOptions?.title ?: "" + val url = intentOptions?.url ?: "" - toolbarTitle.text = TITLE - Timber.d(URL + TITLE) + toolbarTitle.text = title + Timber.d("%s %s", url, title) if (savedInstanceState != null) restoreState(savedInstanceState) - else loadUrl(URL) + else loadUrl(url) } } @@ -62,11 +68,9 @@ class WebViewActivity : override fun onResume() { super.onResume() - val screenIdString = intent.getStringExtra("SCREEN_ID") ?: return - val screenId = ScreenId.entries.find { it.name == screenIdString } ?: return - + val screenId = intentOptions?.screenId ?: return EventLogger.screenView(screenId) - Timber.d("WebViewActivity screen view logging: $screenId") + Timber.d("WebViewActivity screen view logging: ${screenId}") } override fun shouldLogScreenId() = false diff --git a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt index 8841e221a..d4b1ffc38 100644 --- a/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt +++ b/app/src/main/java/com/eatssu/android/presentation/mypage/userinfo/UserInfoActivity.kt @@ -1,6 +1,7 @@ package com.eatssu.android.presentation.mypage.userinfo import android.os.Bundle +import android.os.Parcelable import android.text.Editable import android.text.TextWatcher import android.view.LayoutInflater @@ -15,12 +16,14 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.eatssu.android.R import com.eatssu.android.databinding.ActivityUserInfoBinding +import com.eatssu.android.presentation.base.ActivityCompanionWithArgsDefault import com.eatssu.android.presentation.base.BaseActivity import com.eatssu.android.presentation.util.showToast import com.eatssu.common.enums.ScreenId import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.parcelize.Parcelize @AndroidEntryPoint class UserInfoActivity : @@ -29,7 +32,11 @@ class UserInfoActivity : ScreenId.MYPAGE_USERINFO ) { - companion object { + @Parcelize + data class Args(val force: Boolean = false) : Parcelable + + companion object : + ActivityCompanionWithArgsDefault(UserInfoActivity::class, Args::class, { Args() }) { private const val MIN_NICKNAME_LENGTH = 2 private const val MAX_NICKNAME_LENGTH = 16 } @@ -50,7 +57,7 @@ class UserInfoActivity : // 현재 설정된 유저 정보 가져오기 userInfoViewModel.loadUserInfo() - force = intent.getBooleanExtra("force", false) + force = intentOptions?.force ?: false binding.btnCheckNicknameDuplication.isEnabled = false binding.btnComplete.isEnabled = false diff --git a/app/src/main/java/com/eatssu/android/presentation/util/ActivityUtil.kt b/app/src/main/java/com/eatssu/android/presentation/util/ActivityUtil.kt index b40d27a41..a715f5d63 100644 --- a/app/src/main/java/com/eatssu/android/presentation/util/ActivityUtil.kt +++ b/app/src/main/java/com/eatssu/android/presentation/util/ActivityUtil.kt @@ -4,6 +4,11 @@ import android.app.Activity import android.content.Intent import androidx.appcompat.app.AppCompatActivity +@Deprecated( + message = "Use Activity companion object pattern instead. Example: MainActivity.start(context)", + replaceWith = ReplaceWith("ActivityName.start(this)"), + level = DeprecationLevel.WARNING +) inline fun AppCompatActivity.startActivity(block: Intent.() -> Unit = {}) { startActivity(Intent(this, T::class.java).apply(block)) } \ No newline at end of file diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt index 0ebba1249..5f96de1fa 100644 --- a/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt +++ b/app/src/main/java/com/eatssu/android/presentation/widget/ui/MealWidget.kt @@ -1,6 +1,7 @@ package com.eatssu.android.presentation.widget.ui import android.content.Context +import android.content.Intent import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -42,12 +43,13 @@ import androidx.glance.text.TextStyle import com.eatssu.android.R import com.eatssu.android.domain.model.WidgetMealInfo import com.eatssu.android.domain.usecase.widget.LoadRestaurantByFileKeyUseCase +import com.eatssu.android.presentation.intro.IntroActivity import com.eatssu.android.presentation.widget.MealInfoStateDefinition import com.eatssu.android.presentation.widget.MealWorker import com.eatssu.android.presentation.widget.theme.EATSSUWidgetColorScheme import com.eatssu.android.presentation.widget.util.MealTime import com.eatssu.android.presentation.widget.util.WidgetDataDisplayManager -import com.eatssu.android.presentation.widget.util.launchApp +import com.eatssu.common.enums.LaunchPath import com.eatssu.common.enums.Restaurant import dagger.hilt.EntryPoint import dagger.hilt.InstallIn @@ -255,7 +257,9 @@ class MealWidget : GlanceAppWidget() { .cornerRadius(20.dp) .clickable { Timber.d("위젯 클릭") - context.launchApp() + IntroActivity.start(context, IntroActivity.Args(LaunchPath.WIDGET)) { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } }, ) { Row( diff --git a/app/src/main/java/com/eatssu/android/presentation/widget/util/launchApp.kt b/app/src/main/java/com/eatssu/android/presentation/widget/util/launchApp.kt deleted file mode 100644 index 1b19b4b38..000000000 --- a/app/src/main/java/com/eatssu/android/presentation/widget/util/launchApp.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.eatssu.android.presentation.widget.util - -import android.content.Context -import android.content.Intent -import android.net.Uri - -fun Context.launchApp() { - val deepLinkUri = Uri.parse("eatssu://root") - val intent = Intent(Intent.ACTION_VIEW, deepLinkUri) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - // 위젯에서 온 것을 표시하는 플래그 추가 - intent.putExtra("launch_path", "widget") - - runCatching { this.startActivity(intent) } -} \ No newline at end of file