From c75c7cc1a9d384d6409fd4c3bd3a4b28ab6c6be1 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Sat, 29 Apr 2023 20:29:36 +0900 Subject: [PATCH 01/90] =?UTF-8?q?refactor(AlarmController,=20AlarmReceiver?= =?UTF-8?q?):=20=EC=A4=91=EB=B3=B5=20=EC=BD=94=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0,=20scope=20function=20=EC=82=AC=EC=9A=A9=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/movie/view/AlarmController.kt | 32 ++++++------------- .../movie/view/seatselection/AlarmReceiver.kt | 6 ++++ 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/woowacourse/movie/view/AlarmController.kt b/app/src/main/java/woowacourse/movie/view/AlarmController.kt index 9bc6bd018..c547d587b 100644 --- a/app/src/main/java/woowacourse/movie/view/AlarmController.kt +++ b/app/src/main/java/woowacourse/movie/view/AlarmController.kt @@ -8,9 +8,7 @@ import woowacourse.movie.view.model.ReservationUiModel import woowacourse.movie.view.seatselection.AlarmReceiver import java.time.ZoneId -class AlarmController( - private val context: Context -) { +class AlarmController(private val context: Context) { fun registerAlarms(reservations: List, minuteInterval: Long) { reservations.forEach { @@ -20,7 +18,7 @@ class AlarmController( fun registerAlarm(reservation: ReservationUiModel, minuteInterval: Long) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - val pendingIntent = getPendingIntent(reservation) + val pendingIntent = getPendingIntent(AlarmReceiver.newIntent(context, reservation)) alarmManager.set( AlarmManager.RTC_WAKEUP, @@ -31,29 +29,19 @@ class AlarmController( ) } - private fun getPendingIntent(reservation: ReservationUiModel): PendingIntent { - return Intent(context, AlarmReceiver::class.java).let { - it.putExtra(AlarmReceiver.RESERVATION, reservation) - PendingIntent.getBroadcast( - context, - ALARM_REQUEST_CODE, - it, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, - ) - } - } - fun cancelAlarms() { - val pendingIntent = PendingIntent.getBroadcast( - context, - ALARM_REQUEST_CODE, - Intent(context, AlarmReceiver::class.java), - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - ) + val pendingIntent = getPendingIntent(Intent(context, AlarmReceiver::class.java)) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.cancel(pendingIntent) } + private fun getPendingIntent(intent: Intent) = PendingIntent.getBroadcast( + context, + ALARM_REQUEST_CODE, + intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT, + ) + companion object { private const val ALARM_REQUEST_CODE = 100 } diff --git a/app/src/main/java/woowacourse/movie/view/seatselection/AlarmReceiver.kt b/app/src/main/java/woowacourse/movie/view/seatselection/AlarmReceiver.kt index 5fa265adb..c5f68354a 100644 --- a/app/src/main/java/woowacourse/movie/view/seatselection/AlarmReceiver.kt +++ b/app/src/main/java/woowacourse/movie/view/seatselection/AlarmReceiver.kt @@ -54,5 +54,11 @@ class AlarmReceiver : BroadcastReceiver() { const val NOTIFICATION_ID = 200 const val CHANNEL_ID = "RESERVATION_CHANNEL" const val RESERVATION = "RESERVATION" + + fun newIntent(context: Context, reservation: ReservationUiModel): Intent { + return Intent(context, AlarmReceiver::class.java).apply { + putExtra(RESERVATION, reservation) + } + } } } From af1eb2cef80d18430c47a794af1404455ded5742 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Sat, 29 Apr 2023 20:34:18 +0900 Subject: [PATCH 02/90] =?UTF-8?q?refactor(MovieListFragment):=20Mock?= =?UTF-8?q?=EC=9D=B4=20=EC=95=84=EB=8B=8C=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=EB=A1=9C=20=ED=83=80=EC=9E=85=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/view/moviemain/movielist/MovieListFragment.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt index b69a00fbf..d0e1708ad 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt @@ -9,6 +9,7 @@ import androidx.recyclerview.widget.RecyclerView import woowacourse.movie.R import woowacourse.movie.data.MovieMockRepository import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.repository.MovieRepository import woowacourse.movie.view.ReservationActivity import woowacourse.movie.view.mapper.toUiModel import woowacourse.movie.view.model.MovieListModel @@ -16,7 +17,8 @@ import woowacourse.movie.view.model.MovieListModel class MovieListFragment : Fragment(R.layout.fragment_movie_list) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val movies = MovieMockRepository.findAll() + val movieRepository: MovieRepository = MovieMockRepository + val movies = movieRepository.findAll() val dataList = generateMovieListData(movies) val movieAdapter = MovieListAdapter(dataList, ::onClick) From 03aed6f4e853c76e77a281989b25802b1c7d90ed Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Sat, 29 Apr 2023 20:54:21 +0900 Subject: [PATCH 03/90] =?UTF-8?q?feat(SettingDataManager):=20Setting?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4,=20=ED=94=84?= =?UTF-8?q?=EB=A6=AC=ED=8D=BC=EB=9F=B0=EC=8A=A4=EB=A5=BC=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/data/setting/SettingDataManager.kt | 6 ++++++ .../data/setting/SettingPreferencesManager.kt | 17 +++++++++++++++++ .../movie/view/ReservationCompletedActivity.kt | 15 +++++++-------- 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/woowacourse/movie/data/setting/SettingDataManager.kt create mode 100644 app/src/main/java/woowacourse/movie/data/setting/SettingPreferencesManager.kt diff --git a/app/src/main/java/woowacourse/movie/data/setting/SettingDataManager.kt b/app/src/main/java/woowacourse/movie/data/setting/SettingDataManager.kt new file mode 100644 index 000000000..4ff64f862 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/data/setting/SettingDataManager.kt @@ -0,0 +1,6 @@ +package woowacourse.movie.data.setting + +interface SettingDataManager { + fun getIsAlarmSetting(): Boolean + fun setIsAlarmSetting(isOn: Boolean) +} diff --git a/app/src/main/java/woowacourse/movie/data/setting/SettingPreferencesManager.kt b/app/src/main/java/woowacourse/movie/data/setting/SettingPreferencesManager.kt new file mode 100644 index 000000000..c9b596cc5 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/data/setting/SettingPreferencesManager.kt @@ -0,0 +1,17 @@ +package woowacourse.movie.data.setting + +import android.content.Context +import androidx.preference.PreferenceManager + +class SettingPreferencesManager(context: Context) : SettingDataManager { + private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) + override fun getIsAlarmSetting(): Boolean = sharedPreferences.getBoolean(IS_ALARM_ON, false) + override fun setIsAlarmSetting(isOn: Boolean) { + val editor = sharedPreferences.edit() + editor.putBoolean(IS_ALARM_ON, isOn).apply() + } + + companion object { + const val IS_ALARM_ON = "IS_ALARM_ON" + } +} diff --git a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt index 993ab2bd4..b7f11abf4 100644 --- a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt @@ -6,7 +6,6 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent -import android.content.SharedPreferences import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -15,8 +14,9 @@ import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat -import androidx.preference.PreferenceManager import woowacourse.movie.R +import woowacourse.movie.data.setting.SettingDataManager +import woowacourse.movie.data.setting.SettingPreferencesManager import woowacourse.movie.databinding.ActivityReservationCompletedBinding import woowacourse.movie.util.DATE_FORMATTER import woowacourse.movie.util.TIME_FORMATTER @@ -30,15 +30,15 @@ import java.text.DecimalFormat class ReservationCompletedActivity : AppCompatActivity() { private lateinit var binding: ActivityReservationCompletedBinding - private lateinit var sharedPreferences: SharedPreferences + private lateinit var settingManager: SettingDataManager override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityReservationCompletedBinding.inflate(layoutInflater) setContentView(binding.root) val reservation = intent.getParcelableCompat(RESERVATION) - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) - val isAlarmOn = sharedPreferences.getBoolean(SettingFragment.IS_ALARM_ON, false) + settingManager = SettingPreferencesManager(this) + val isAlarmOn = settingManager.getIsAlarmSetting() requestNotificationPermission() @@ -119,12 +119,11 @@ class ReservationCompletedActivity : AppCompatActivity() { private val requestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { isGranted: Boolean -> - val editor: SharedPreferences.Editor = sharedPreferences.edit() if (isGranted) { - editor.putBoolean(SettingFragment.IS_ALARM_ON, true).apply() + settingManager.setIsAlarmSetting(true) return@registerForActivityResult } - editor.putBoolean(SettingFragment.IS_ALARM_ON, false).apply() + settingManager.setIsAlarmSetting(false) } companion object { From 35e49788f48891de345fb717a7ed4365fb940ab0 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Sat, 29 Apr 2023 21:07:39 +0900 Subject: [PATCH 04/90] =?UTF-8?q?feat(AlarmController):=20=EC=B1=84?= =?UTF-8?q?=EB=84=90=20=EA=B4=80=EB=A6=AC,=20=EC=95=8C=EB=9E=8C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20AlarmController=EB=A1=9C=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/movie/view/AlarmController.kt | 12 +++++++++ .../view/ReservationCompletedActivity.kt | 27 +++---------------- .../view/moviemain/setting/SettingFragment.kt | 21 +++++++-------- 3 files changed, 26 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/woowacourse/movie/view/AlarmController.kt b/app/src/main/java/woowacourse/movie/view/AlarmController.kt index c547d587b..e2212fec9 100644 --- a/app/src/main/java/woowacourse/movie/view/AlarmController.kt +++ b/app/src/main/java/woowacourse/movie/view/AlarmController.kt @@ -1,6 +1,8 @@ package woowacourse.movie.view import android.app.AlarmManager +import android.app.NotificationChannel +import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent @@ -9,6 +11,9 @@ import woowacourse.movie.view.seatselection.AlarmReceiver import java.time.ZoneId class AlarmController(private val context: Context) { + init { + createChannel() + } fun registerAlarms(reservations: List, minuteInterval: Long) { reservations.forEach { @@ -35,6 +40,12 @@ class AlarmController(private val context: Context) { alarmManager.cancel(pendingIntent) } + private fun createChannel() { + val channel = NotificationChannel(AlarmReceiver.CHANNEL_ID, CHANNER_NAME, NotificationManager.IMPORTANCE_DEFAULT) + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + if (!notificationManager.notificationChannels.contains(channel)) notificationManager.createNotificationChannel(channel) + } + private fun getPendingIntent(intent: Intent) = PendingIntent.getBroadcast( context, ALARM_REQUEST_CODE, @@ -44,5 +55,6 @@ class AlarmController(private val context: Context) { companion object { private const val ALARM_REQUEST_CODE = 100 + private const val CHANNER_NAME = "Reservation Notification" } } diff --git a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt index b7f11abf4..a6528f003 100644 --- a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt @@ -1,8 +1,6 @@ package woowacourse.movie.view import android.Manifest -import android.app.NotificationChannel -import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent @@ -24,18 +22,19 @@ import woowacourse.movie.util.getParcelableCompat import woowacourse.movie.view.model.ReservationUiModel import woowacourse.movie.view.moviemain.MovieMainActivity import woowacourse.movie.view.moviemain.setting.SettingFragment -import woowacourse.movie.view.seatselection.AlarmReceiver import java.text.DecimalFormat class ReservationCompletedActivity : AppCompatActivity() { private lateinit var binding: ActivityReservationCompletedBinding private lateinit var settingManager: SettingDataManager + private lateinit var alarmController: AlarmController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityReservationCompletedBinding.inflate(layoutInflater) setContentView(binding.root) + alarmController = AlarmController(this) val reservation = intent.getParcelableCompat(RESERVATION) settingManager = SettingPreferencesManager(this) val isAlarmOn = settingManager.getIsAlarmSetting() @@ -44,7 +43,7 @@ class ReservationCompletedActivity : AppCompatActivity() { reservation?.let { initViewData(it) - if (isAlarmOn) setAlarm(reservation, SettingFragment.ALARM_MINUTE_INTERVAL) + if (isAlarmOn) alarmController.registerAlarm(reservation, SettingFragment.ALARM_MINUTE_INTERVAL) } supportActionBar?.setDisplayHomeAsUpEnabled(true) @@ -60,14 +59,8 @@ class ReservationCompletedActivity : AppCompatActivity() { ) } - private fun setAlarm(reservation: ReservationUiModel, alarmMinuteInterval: Long) { - val alarmController = AlarmController(this) - createChannel() - alarmController.registerAlarm(reservation, alarmMinuteInterval) - } - private fun initViewData(reservation: ReservationUiModel) { - binding.apply { + with(binding) { movieTitle.text = reservation.title movieScreeningDate.text = getString( R.string.datetime_with_space, @@ -96,18 +89,6 @@ class ReservationCompletedActivity : AppCompatActivity() { return super.onOptionsItemSelected(item) } - private fun createChannel() { - val name = "Reservation Notification" - val channel = NotificationChannel( - AlarmReceiver.CHANNEL_ID, - name, - NotificationManager.IMPORTANCE_DEFAULT, - ) - val notificationManager = - getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.createNotificationChannel(channel) - } - private fun requestNotificationPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) { diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt index c77e3c615..9ffb7f97d 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt @@ -1,7 +1,6 @@ package woowacourse.movie.view.moviemain.setting import android.Manifest -import android.content.SharedPreferences import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -10,17 +9,19 @@ import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment -import androidx.preference.PreferenceManager import com.google.android.material.switchmaterial.SwitchMaterial import woowacourse.movie.R import woowacourse.movie.data.ReservationMockRepository +import woowacourse.movie.data.setting.SettingDataManager +import woowacourse.movie.data.setting.SettingPreferencesManager +import woowacourse.movie.domain.repository.ReservationRepository import woowacourse.movie.view.AlarmController import woowacourse.movie.view.mapper.toUiModel class SettingFragment : Fragment(R.layout.fragment_setting) { private lateinit var alarmController: AlarmController - private lateinit var sharedPreferences: SharedPreferences + private lateinit var settingManager: SettingDataManager private lateinit var toggle: SwitchMaterial private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> @@ -32,7 +33,7 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext()) + settingManager = SettingPreferencesManager(requireContext()) alarmController = AlarmController(requireContext()) } @@ -41,8 +42,7 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { toggle = view.findViewById(R.id.setting_toggle) - val savedToggle = sharedPreferences.getBoolean(IS_ALARM_ON, false) - toggle.isChecked = savedToggle + toggle.isChecked = settingManager.getIsAlarmSetting() toggle.setOnCheckedChangeListener { _, isChecked -> setToggleChangeListener(isChecked) @@ -50,15 +50,15 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { } private fun setToggleChangeListener(isChecked: Boolean) { - val editor: SharedPreferences.Editor = sharedPreferences.edit() if (isChecked && requestNotificationPermission()) { - val reservations = ReservationMockRepository.findAll().map { it.toUiModel() } + val reservationRepo: ReservationRepository = ReservationMockRepository + val reservations = reservationRepo.findAll().map { it.toUiModel() } alarmController.registerAlarms(reservations, ALARM_MINUTE_INTERVAL) - editor.putBoolean(IS_ALARM_ON, true).apply() + settingManager.setIsAlarmSetting(true) return } alarmController.cancelAlarms() - editor.putBoolean(IS_ALARM_ON, false).apply() + settingManager.setIsAlarmSetting(false) } private fun requestNotificationPermission(): Boolean { @@ -72,7 +72,6 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { } companion object { - const val IS_ALARM_ON = "IS_ALARM_ON" const val ALARM_MINUTE_INTERVAL = 30L } } From af6ad6c162b4a1dbfc32a46d956be99c0e82431c Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Sun, 30 Apr 2023 23:05:18 +0900 Subject: [PATCH 05/90] =?UTF-8?q?refactor(MovieMainActivity):=20TAG=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=9C=84=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/view/moviemain/MovieMainActivity.kt | 11 ++++------- .../view/moviemain/movielist/MovieListFragment.kt | 1 + .../reservationlist/ReservationListFragment.kt | 4 ++++ .../movie/view/moviemain/setting/SettingFragment.kt | 1 + 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/MovieMainActivity.kt b/app/src/main/java/woowacourse/movie/view/moviemain/MovieMainActivity.kt index 4f49d69e5..668aeebd4 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/MovieMainActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/MovieMainActivity.kt @@ -7,8 +7,11 @@ import androidx.fragment.app.commit import com.google.android.material.bottomnavigation.BottomNavigationView import woowacourse.movie.R import woowacourse.movie.view.moviemain.movielist.MovieListFragment +import woowacourse.movie.view.moviemain.movielist.MovieListFragment.Companion.TAG_MOVIE_LIST import woowacourse.movie.view.moviemain.reservationlist.ReservationListFragment +import woowacourse.movie.view.moviemain.reservationlist.ReservationListFragment.Companion.TAG_RESERVATION_LIST import woowacourse.movie.view.moviemain.setting.SettingFragment +import woowacourse.movie.view.moviemain.setting.SettingFragment.Companion.TAG_SETTING class MovieMainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -30,7 +33,7 @@ class MovieMainActivity : AppCompatActivity() { return@setOnItemSelectedListener true } R.id.action_setting -> { - val fragment = supportFragmentManager.findFragmentByTag(TAG_MOVIE_LIST) as? SettingFragment ?: SettingFragment().also { addFragment(it, TAG_SETTING) } + val fragment = supportFragmentManager.findFragmentByTag(TAG_SETTING) as? SettingFragment ?: SettingFragment().also { addFragment(it, TAG_SETTING) } replaceFragment(fragment) return@setOnItemSelectedListener true } @@ -52,10 +55,4 @@ class MovieMainActivity : AppCompatActivity() { add(fragment, tag) } } - - companion object { - private const val TAG_RESERVATION_LIST = "RESERVATION_LIST" - private const val TAG_MOVIE_LIST = "MOVIE_LIST" - private const val TAG_SETTING = "SETTING" - } } diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt index d0e1708ad..495ba6867 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt @@ -67,5 +67,6 @@ class MovieListFragment : Fragment(R.layout.fragment_movie_list) { companion object { private const val AD_POST_INTERVAL = 3 + const val TAG_MOVIE_LIST = "MOVIE_LIST" } } diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt index f7e95b6b6..84724ecac 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt @@ -19,4 +19,8 @@ class ReservationListFragment : Fragment(R.layout.fragment_reservation_list) { startActivity(intent) } } + + companion object { + const val TAG_RESERVATION_LIST = "RESERVATION_LIST" + } } diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt index 9ffb7f97d..4cf1f5bdc 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt @@ -73,5 +73,6 @@ class SettingFragment : Fragment(R.layout.fragment_setting) { companion object { const val ALARM_MINUTE_INTERVAL = 30L + const val TAG_SETTING = "SETTING" } } From 29edef5546e08e25ab4bf87bc1767f244ac4d5b4 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Sun, 30 Apr 2023 23:12:20 +0900 Subject: [PATCH 06/90] =?UTF-8?q?refactor(MovieMainActivityTest):=20?= =?UTF-8?q?=ED=94=84=EB=9E=98=EA=B7=B8=EB=A8=BC=ED=8A=B8=20=EC=9E=90?= =?UTF-8?q?=EC=B2=B4=EB=A5=BC=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=A7=81=EA=B4=80=EC=A0=81=EC=9D=B8=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/view/MovieMainActivityTest.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/androidTest/java/woowacourse/movie/view/MovieMainActivityTest.kt b/app/src/androidTest/java/woowacourse/movie/view/MovieMainActivityTest.kt index c755a402b..ac03331b4 100644 --- a/app/src/androidTest/java/woowacourse/movie/view/MovieMainActivityTest.kt +++ b/app/src/androidTest/java/woowacourse/movie/view/MovieMainActivityTest.kt @@ -2,16 +2,18 @@ package woowacourse.movie.view import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import woowacourse.movie.R import woowacourse.movie.view.moviemain.MovieMainActivity +import woowacourse.movie.view.moviemain.movielist.MovieListFragment +import woowacourse.movie.view.moviemain.reservationlist.ReservationListFragment +import woowacourse.movie.view.moviemain.setting.SettingFragment @RunWith(AndroidJUnit4::class) class MovieMainActivityTest { @@ -21,18 +23,27 @@ class MovieMainActivityTest { @Test fun 예매내역_버튼을_누르면_예매내역_Fragment로_바뀐다() { onView(withId(R.id.action_reservation_list)).perform(click()) - onView(withId(R.id.recyclerview)).check(matches(isDisplayed())) + mActivityTestRule.scenario.onActivity { + val fragment = it.supportFragmentManager.findFragmentByTag(ReservationListFragment.TAG_RESERVATION_LIST) + assertTrue(fragment != null && fragment.isVisible) + } } @Test fun 홈_버튼을_누르면_홈_Fragment로_바뀐다() { onView(withId(R.id.action_home)).perform(click()) - onView(withId(R.id.movie_recyclerview)).check(matches(isDisplayed())) + mActivityTestRule.scenario.onActivity { + val fragment = it.supportFragmentManager.findFragmentByTag(MovieListFragment.TAG_MOVIE_LIST) + assertTrue(fragment != null && fragment.isVisible) + } } @Test fun 설정_버튼을_누르면_설정_Fragment로_바뀐다() { onView(withId(R.id.action_setting)).perform(click()) - onView(withId(R.id.setting_toggle)).check(matches(isDisplayed())) + mActivityTestRule.scenario.onActivity { + val fragment = it.supportFragmentManager.findFragmentByTag(SettingFragment.TAG_SETTING) + assertTrue(fragment != null && fragment.isVisible) + } } } From bb6cca9c146b349d7c768b202d425f1a89271f1f Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Mon, 1 May 2023 00:42:25 +0900 Subject: [PATCH 07/90] =?UTF-8?q?feat(SettingFragmentTest):=20UI=20?= =?UTF-8?q?=EC=A4=91=EC=8B=AC=EC=9C=BC=EB=A1=9C=20=ED=94=84=EB=9E=98?= =?UTF-8?q?=EA=B7=B8=EB=A8=BC=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=AC=EC=9E=91=EC=84=B1=20(=ED=86=A0=EA=B8=80=EA=B0=92=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 + .../movie/view/SettingFragmentTest.kt | 62 ++++++------------- 2 files changed, 20 insertions(+), 44 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4cd178d07..0e43d76f2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,4 +50,6 @@ dependencies { testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + debugImplementation("androidx.fragment:fragment-testing:1.5.7") + debugImplementation("androidx.fragment:fragment-ktx:1.6.0-beta01") } diff --git a/app/src/androidTest/java/woowacourse/movie/view/SettingFragmentTest.kt b/app/src/androidTest/java/woowacourse/movie/view/SettingFragmentTest.kt index 79ae36f48..21b5c6013 100644 --- a/app/src/androidTest/java/woowacourse/movie/view/SettingFragmentTest.kt +++ b/app/src/androidTest/java/woowacourse/movie/view/SettingFragmentTest.kt @@ -1,61 +1,35 @@ package woowacourse.movie.view -import android.content.Context -import android.content.SharedPreferences -import androidx.fragment.app.commit -import androidx.preference.PreferenceManager -import androidx.test.core.app.ActivityScenario +import androidx.fragment.app.testing.launchFragmentInContainer import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions +import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Before -import org.junit.Rule +import com.google.android.material.switchmaterial.SwitchMaterial import org.junit.Test import org.junit.runner.RunWith import woowacourse.movie.R -import woowacourse.movie.view.moviemain.MovieMainActivity import woowacourse.movie.view.moviemain.setting.SettingFragment @RunWith(AndroidJUnit4::class) class SettingFragmentTest { - @get:Rule - val mActivityTestRule = ActivityScenarioRule(MovieMainActivity::class.java) - - @Before - fun setup() { - ActivityScenario.launch(MovieMainActivity::class.java).onActivity { - it.supportFragmentManager.commit { - replace(R.id.fragment_container_view, SettingFragment()) - } - } - } - - @Test - fun 설정_Fragment에서_저장된_세팅값이_false면_토글도_꺼져있다() { - setSharedPreferences(false) - onView(ViewMatchers.withId(R.id.action_setting)).perform(ViewActions.click()) - onView(ViewMatchers.withId(R.id.setting_toggle)) - .check(ViewAssertions.matches(ViewMatchers.isNotChecked())) - } + private val scenario = launchFragmentInContainer(themeResId = R.style.Theme_Movie) @Test - fun 설정_Fragment에서_저장된_세팅값이_true면_토글도_켜져있다() { - setSharedPreferences(true) - onView(ViewMatchers.withId(R.id.action_setting)).perform(ViewActions.click()) - onView(ViewMatchers.withId(R.id.setting_toggle)) - .check(ViewAssertions.matches(ViewMatchers.isChecked())) - } - - private fun setSharedPreferences(isAlarmOn: Boolean) { - val targetContext: Context = InstrumentationRegistry.getInstrumentation().targetContext - - val preferencesEditor: SharedPreferences.Editor = PreferenceManager.getDefaultSharedPreferences(targetContext).edit() - preferencesEditor.clear() - preferencesEditor.putBoolean(SettingFragment.IS_ALARM_ON, isAlarmOn) - preferencesEditor.commit() + fun 토글의_설정값이_유지된다() { + onView(ViewMatchers.withId(R.id.setting_toggle)).perform(click()) + var isChecked = false + scenario.onFragment { + isChecked = it.requireView().findViewById(R.id.setting_toggle).isChecked + } + scenario.recreate() + if (isChecked) { + onView(ViewMatchers.withId(R.id.setting_toggle)) + .check(ViewAssertions.matches(ViewMatchers.isChecked())).perform(click()) + } else { + onView(ViewMatchers.withId(R.id.setting_toggle)) + .check(ViewAssertions.matches(ViewMatchers.isNotChecked())).perform(click()) + } } } From b95885ca6c6e865f254d889fd1c596444c761c80 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Tue, 2 May 2023 17:49:16 +0900 Subject: [PATCH 08/90] =?UTF-8?q?refactor(SettingFragment):=20MVP=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/moviemain/setting/SettingContract.kt | 17 ++++++ .../view/moviemain/setting/SettingFragment.kt | 58 ++++--------------- .../moviemain/setting/SettingPresenter.kt | 47 +++++++++++++++ 3 files changed, 75 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt new file mode 100644 index 000000000..11b897578 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt @@ -0,0 +1,17 @@ +package woowacourse.movie.view.moviemain.setting + +import androidx.activity.result.ActivityResultLauncher + +interface SettingContract { + interface View { + var presenter: Presenter + val requestPermissionLauncher: ActivityResultLauncher + fun setToggle(isOn: Boolean) + } + + interface Presenter { + fun initToggle() + fun onClick(isOn: Boolean) + fun requestNotificationPermission(): Boolean + } +} diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt index 4cf1f5bdc..799e4a255 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt @@ -1,74 +1,38 @@ package woowacourse.movie.view.moviemain.setting -import android.Manifest -import android.content.pm.PackageManager -import android.os.Build import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.android.material.switchmaterial.SwitchMaterial import woowacourse.movie.R -import woowacourse.movie.data.ReservationMockRepository -import woowacourse.movie.data.setting.SettingDataManager -import woowacourse.movie.data.setting.SettingPreferencesManager -import woowacourse.movie.domain.repository.ReservationRepository -import woowacourse.movie.view.AlarmController -import woowacourse.movie.view.mapper.toUiModel -class SettingFragment : Fragment(R.layout.fragment_setting) { +class SettingFragment : Fragment(R.layout.fragment_setting), SettingContract.View { - private lateinit var alarmController: AlarmController - private lateinit var settingManager: SettingDataManager private lateinit var toggle: SwitchMaterial - - private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> - if (!isGranted) { - Toast.makeText(requireContext(), "권한을 설정해야 알림을 받을 수 있습니다. 설정에서 알림을 켜주세요.", Toast.LENGTH_LONG).show() - toggle.isChecked = false - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - settingManager = SettingPreferencesManager(requireContext()) - alarmController = AlarmController(requireContext()) - } + override lateinit var presenter: SettingContract.Presenter override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + presenter = SettingPresenter(this, requireContext()) toggle = view.findViewById(R.id.setting_toggle) - - toggle.isChecked = settingManager.getIsAlarmSetting() - + presenter.initToggle() toggle.setOnCheckedChangeListener { _, isChecked -> - setToggleChangeListener(isChecked) + presenter.onClick(isChecked) } } - private fun setToggleChangeListener(isChecked: Boolean) { - if (isChecked && requestNotificationPermission()) { - val reservationRepo: ReservationRepository = ReservationMockRepository - val reservations = reservationRepo.findAll().map { it.toUiModel() } - alarmController.registerAlarms(reservations, ALARM_MINUTE_INTERVAL) - settingManager.setIsAlarmSetting(true) - return - } - alarmController.cancelAlarms() - settingManager.setIsAlarmSetting(false) + override fun setToggle(isOn: Boolean) { + toggle.isChecked = isOn } - private fun requestNotificationPermission(): Boolean { - if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) - return ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED - } + override val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> + if (!isGranted) { + Toast.makeText(requireContext(), "권한을 설정해야 알림을 받을 수 있습니다. 설정에서 알림을 켜주세요.", Toast.LENGTH_LONG).show() + setToggle(false) } - return true // android 12 version 이하는 notification 권한 필요 없기 때문 } companion object { diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt new file mode 100644 index 000000000..e93b6fda3 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt @@ -0,0 +1,47 @@ +package woowacourse.movie.view.moviemain.setting + +import android.Manifest +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import androidx.core.content.ContextCompat +import woowacourse.movie.data.reservation.ReservationMockRepository +import woowacourse.movie.data.setting.SettingDataManager +import woowacourse.movie.data.setting.SettingPreferencesManager +import woowacourse.movie.domain.repository.ReservationRepository +import woowacourse.movie.view.AlarmController +import woowacourse.movie.view.mapper.toUiModel +import woowacourse.movie.view.moviemain.setting.SettingFragment.Companion.ALARM_MINUTE_INTERVAL + +class SettingPresenter(private val view: SettingContract.View, private val context: Context) : SettingContract.Presenter { + private val settingManager: SettingDataManager = SettingPreferencesManager(context) + private val alarmController: AlarmController = AlarmController(context) + + override fun initToggle() { + view.setToggle(settingManager.getIsAlarmSetting()) + } + + override fun onClick(isOn: Boolean) { + if (isOn && requestNotificationPermission()) { + val reservationRepo: ReservationRepository = ReservationMockRepository + val reservations = reservationRepo.findAll().map { it.toUiModel() } + alarmController.registerAlarms(reservations, ALARM_MINUTE_INTERVAL) + settingManager.setIsAlarmSetting(true) + view.setToggle(true) + return + } + alarmController.cancelAlarms() + settingManager.setIsAlarmSetting(false) + view.setToggle(false) + } + + override fun requestNotificationPermission(): Boolean { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + view.requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + return ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED + } + } + return true // android 12 version 이하는 notification 권한 필요 없기 때문 + } +} From a3fb3df79d69ad2adf0f9099f026a49b3df6732f Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 00:19:01 +0900 Subject: [PATCH 09/90] =?UTF-8?q?feat(AlarmManageable):=20AlarmController?= =?UTF-8?q?=EC=9D=98=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/view/{ => model}/AlarmController.kt | 13 ++++++------- .../woowacourse/movie/view/model/AlarmManageable.kt | 8 ++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) rename app/src/main/java/woowacourse/movie/view/{ => model}/AlarmController.kt (83%) create mode 100644 app/src/main/java/woowacourse/movie/view/model/AlarmManageable.kt diff --git a/app/src/main/java/woowacourse/movie/view/AlarmController.kt b/app/src/main/java/woowacourse/movie/view/model/AlarmController.kt similarity index 83% rename from app/src/main/java/woowacourse/movie/view/AlarmController.kt rename to app/src/main/java/woowacourse/movie/view/model/AlarmController.kt index e2212fec9..aa5651a32 100644 --- a/app/src/main/java/woowacourse/movie/view/AlarmController.kt +++ b/app/src/main/java/woowacourse/movie/view/model/AlarmController.kt @@ -1,4 +1,4 @@ -package woowacourse.movie.view +package woowacourse.movie.view.model import android.app.AlarmManager import android.app.NotificationChannel @@ -6,22 +6,21 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent -import woowacourse.movie.view.model.ReservationUiModel import woowacourse.movie.view.seatselection.AlarmReceiver import java.time.ZoneId -class AlarmController(private val context: Context) { +class AlarmController(private val context: Context) : AlarmManageable { init { createChannel() } - fun registerAlarms(reservations: List, minuteInterval: Long) { + override fun registerAlarms(reservations: List, minuteInterval: Long) { reservations.forEach { registerAlarm(it, minuteInterval) } } - fun registerAlarm(reservation: ReservationUiModel, minuteInterval: Long) { + override fun registerAlarm(reservation: ReservationUiModel, minuteInterval: Long) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val pendingIntent = getPendingIntent(AlarmReceiver.newIntent(context, reservation)) @@ -34,13 +33,13 @@ class AlarmController(private val context: Context) { ) } - fun cancelAlarms() { + override fun cancelAlarms() { val pendingIntent = getPendingIntent(Intent(context, AlarmReceiver::class.java)) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmManager.cancel(pendingIntent) } - private fun createChannel() { + override fun createChannel() { val channel = NotificationChannel(AlarmReceiver.CHANNEL_ID, CHANNER_NAME, NotificationManager.IMPORTANCE_DEFAULT) val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (!notificationManager.notificationChannels.contains(channel)) notificationManager.createNotificationChannel(channel) diff --git a/app/src/main/java/woowacourse/movie/view/model/AlarmManageable.kt b/app/src/main/java/woowacourse/movie/view/model/AlarmManageable.kt new file mode 100644 index 000000000..10e288c8c --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/model/AlarmManageable.kt @@ -0,0 +1,8 @@ +package woowacourse.movie.view.model + +interface AlarmManageable { + fun registerAlarms(reservations: List, minuteInterval: Long) + fun registerAlarm(reservation: ReservationUiModel, minuteInterval: Long) + fun cancelAlarms() + fun createChannel() +} From 163fb8064f346f4898782729078226644bbcc031 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 00:24:38 +0900 Subject: [PATCH 10/90] =?UTF-8?q?refactor(SettingPresenter,=20SettingContr?= =?UTF-8?q?act):=20MVP=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81,=20=EC=B6=94?= =?UTF-8?q?=EC=83=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/moviemain/setting/SettingContract.kt | 2 +- .../view/moviemain/setting/SettingFragment.kt | 25 ++++++++++++++++- .../moviemain/setting/SettingPresenter.kt | 28 +++++-------------- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt index 11b897578..2010809b6 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingContract.kt @@ -7,11 +7,11 @@ interface SettingContract { var presenter: Presenter val requestPermissionLauncher: ActivityResultLauncher fun setToggle(isOn: Boolean) + fun requestNotificationPermission(): Boolean } interface Presenter { fun initToggle() fun onClick(isOn: Boolean) - fun requestNotificationPermission(): Boolean } } diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt index 799e4a255..dff0ff8f2 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingFragment.kt @@ -1,12 +1,18 @@ package woowacourse.movie.view.moviemain.setting +import android.Manifest +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.view.View import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.android.material.switchmaterial.SwitchMaterial import woowacourse.movie.R +import woowacourse.movie.data.setting.SettingPreferencesManager +import woowacourse.movie.view.model.AlarmController class SettingFragment : Fragment(R.layout.fragment_setting), SettingContract.View { @@ -15,7 +21,7 @@ class SettingFragment : Fragment(R.layout.fragment_setting), SettingContract.Vie override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - presenter = SettingPresenter(this, requireContext()) + presenter = SettingPresenter(this, SettingPreferencesManager(requireContext()), AlarmController(requireContext())) toggle = view.findViewById(R.id.setting_toggle) presenter.initToggle() @@ -35,6 +41,23 @@ class SettingFragment : Fragment(R.layout.fragment_setting), SettingContract.Vie } } + override fun requestNotificationPermission(): Boolean { + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.POST_NOTIFICATIONS, + ) != PackageManager.PERMISSION_GRANTED + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) + return ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED + } + } + return true // android 12 version 이하는 notification 권한 필요 없기 때문 + } + companion object { const val ALARM_MINUTE_INTERVAL = 30L const val TAG_SETTING = "SETTING" diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt index e93b6fda3..95623d8c5 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/setting/SettingPresenter.kt @@ -1,28 +1,24 @@ package woowacourse.movie.view.moviemain.setting -import android.Manifest -import android.content.Context -import android.content.pm.PackageManager -import android.os.Build -import androidx.core.content.ContextCompat import woowacourse.movie.data.reservation.ReservationMockRepository import woowacourse.movie.data.setting.SettingDataManager -import woowacourse.movie.data.setting.SettingPreferencesManager import woowacourse.movie.domain.repository.ReservationRepository -import woowacourse.movie.view.AlarmController import woowacourse.movie.view.mapper.toUiModel +import woowacourse.movie.view.model.AlarmManageable import woowacourse.movie.view.moviemain.setting.SettingFragment.Companion.ALARM_MINUTE_INTERVAL -class SettingPresenter(private val view: SettingContract.View, private val context: Context) : SettingContract.Presenter { - private val settingManager: SettingDataManager = SettingPreferencesManager(context) - private val alarmController: AlarmController = AlarmController(context) +class SettingPresenter( + private val view: SettingContract.View, + private val settingManager: SettingDataManager, + private val alarmController: AlarmManageable, +) : SettingContract.Presenter { override fun initToggle() { view.setToggle(settingManager.getIsAlarmSetting()) } override fun onClick(isOn: Boolean) { - if (isOn && requestNotificationPermission()) { + if (isOn && view.requestNotificationPermission()) { val reservationRepo: ReservationRepository = ReservationMockRepository val reservations = reservationRepo.findAll().map { it.toUiModel() } alarmController.registerAlarms(reservations, ALARM_MINUTE_INTERVAL) @@ -34,14 +30,4 @@ class SettingPresenter(private val view: SettingContract.View, private val conte settingManager.setIsAlarmSetting(false) view.setToggle(false) } - - override fun requestNotificationPermission(): Boolean { - if (ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - view.requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) - return ContextCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED - } - } - return true // android 12 version 이하는 notification 권한 필요 없기 때문 - } } From 77d3cc28803b3faf9ca1e5c3b1000fb0e93e2815 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 00:38:56 +0900 Subject: [PATCH 11/90] =?UTF-8?q?refactor(ReservationListFragment):=20MVP?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/{ => reservation}/ReservationMockRepository.kt | 2 +- .../moviemain/reservationlist/ReservationListContract.kt | 9 +++++++++ .../moviemain/reservationlist/ReservationListFragment.kt | 4 +--- .../reservationlist/ReservationListPresenter.kt | 8 ++++++++ 4 files changed, 19 insertions(+), 4 deletions(-) rename app/src/main/java/woowacourse/movie/data/{ => reservation}/ReservationMockRepository.kt (90%) create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListContract.kt create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListPresenter.kt diff --git a/app/src/main/java/woowacourse/movie/data/ReservationMockRepository.kt b/app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt similarity index 90% rename from app/src/main/java/woowacourse/movie/data/ReservationMockRepository.kt rename to app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt index e044da84b..40d617ab4 100644 --- a/app/src/main/java/woowacourse/movie/data/ReservationMockRepository.kt +++ b/app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt @@ -1,4 +1,4 @@ -package woowacourse.movie.data +package woowacourse.movie.data.reservation import woowacourse.movie.domain.Reservation import woowacourse.movie.domain.repository.ReservationRepository diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListContract.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListContract.kt new file mode 100644 index 000000000..c595054fe --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListContract.kt @@ -0,0 +1,9 @@ +package woowacourse.movie.view.moviemain.reservationlist + +import woowacourse.movie.view.model.ReservationUiModel + +interface ReservationListContract { + interface Presenter { + fun getReservations(): List + } +} diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt index 84724ecac..75e95901a 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListFragment.kt @@ -5,15 +5,13 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView import woowacourse.movie.R -import woowacourse.movie.data.ReservationMockRepository import woowacourse.movie.view.ReservationCompletedActivity -import woowacourse.movie.view.mapper.toUiModel class ReservationListFragment : Fragment(R.layout.fragment_reservation_list) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val recyclerView = view.findViewById(R.id.recyclerview) - val reservations = ReservationMockRepository.findAll().map { it.toUiModel() } + val reservations = ReservationListPresenter().getReservations() recyclerView.adapter = ReservationListAdapter(reservations) { reservation -> val intent = ReservationCompletedActivity.newIntent(requireContext(), reservation) startActivity(intent) diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListPresenter.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListPresenter.kt new file mode 100644 index 000000000..fa9462f45 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservationlist/ReservationListPresenter.kt @@ -0,0 +1,8 @@ +package woowacourse.movie.view.moviemain.reservationlist + +import woowacourse.movie.data.reservation.ReservationMockRepository +import woowacourse.movie.view.mapper.toUiModel + +class ReservationListPresenter : ReservationListContract.Presenter { + override fun getReservations() = ReservationMockRepository.findAll().map { it.toUiModel() } +} From 42ef79c138de4a3cbc5bc2d4baeaf330d706bb02 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 13:23:44 +0900 Subject: [PATCH 12/90] =?UTF-8?q?refactor(MovieListFragment):=20MVP=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moviemain/movielist/MovieListContract.kt | 9 ++++ .../moviemain/movielist/MovieListFragment.kt | 36 +-------------- .../moviemain/movielist/MovieListPresenter.kt | 46 +++++++++++++++++++ 3 files changed, 57 insertions(+), 34 deletions(-) create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListContract.kt create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListContract.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListContract.kt new file mode 100644 index 000000000..4106177ff --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListContract.kt @@ -0,0 +1,9 @@ +package woowacourse.movie.view.moviemain.movielist + +import woowacourse.movie.view.model.MovieListModel + +interface MovieListContract { + interface Presenter { + fun getMovieListData(): List + } +} diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt index 495ba6867..5cd9c1300 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt @@ -7,19 +7,14 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView import woowacourse.movie.R -import woowacourse.movie.data.MovieMockRepository -import woowacourse.movie.domain.Movie -import woowacourse.movie.domain.repository.MovieRepository import woowacourse.movie.view.ReservationActivity -import woowacourse.movie.view.mapper.toUiModel import woowacourse.movie.view.model.MovieListModel class MovieListFragment : Fragment(R.layout.fragment_movie_list) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val movieRepository: MovieRepository = MovieMockRepository - val movies = movieRepository.findAll() - val dataList = generateMovieListData(movies) + val presenter = MovieListPresenter() + val dataList = presenter.getMovieListData() val movieAdapter = MovieListAdapter(dataList, ::onClick) val movieListView = view.findViewById(R.id.movie_recyclerview) @@ -39,34 +34,7 @@ class MovieListFragment : Fragment(R.layout.fragment_movie_list) { } } - private fun generateMovieListData(movies: List): List { - val ad = MovieListModel.MovieAdModel( - R.drawable.woowacourse_banner, - "https://woowacourse.github.io/", - ) - - return mixMovieAdData(movies, ad, AD_POST_INTERVAL) - } - - private fun mixMovieAdData( - movies: List, - ad: MovieListModel.MovieAdModel, - adPostInterval: Int, - ): List { - val dataList = mutableListOf() - movies.forEachIndexed { index, movie -> - if (index % adPostInterval == adPostInterval - 1) { - dataList.add(movie.toUiModel()) - dataList.add(ad) - return@forEachIndexed - } - dataList.add(movie.toUiModel()) - } - return dataList - } - companion object { - private const val AD_POST_INTERVAL = 3 const val TAG_MOVIE_LIST = "MOVIE_LIST" } } diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt new file mode 100644 index 000000000..353a54200 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt @@ -0,0 +1,46 @@ +package woowacourse.movie.view.moviemain.movielist + +import woowacourse.movie.R +import woowacourse.movie.data.MovieMockRepository +import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.repository.MovieRepository +import woowacourse.movie.view.mapper.toUiModel +import woowacourse.movie.view.model.MovieListModel + +class MovieListPresenter : MovieListContract.Presenter { + override fun getMovieListData(): List { + val movieRepository: MovieRepository = MovieMockRepository + val movies = movieRepository.findAll() + return generateMovieListData(movies) + } + + private fun generateMovieListData(movies: List): List { + val ad = MovieListModel.MovieAdModel( + R.drawable.woowacourse_banner, + "https://woowacourse.github.io/", + ) + + return mixMovieAdData(movies, ad, AD_POST_INTERVAL) + } + + private fun mixMovieAdData( + movies: List, + ad: MovieListModel.MovieAdModel, + adPostInterval: Int, + ): List { + val dataList = mutableListOf() + movies.forEachIndexed { index, movie -> + if (index % adPostInterval == adPostInterval - 1) { + dataList.add(movie.toUiModel()) + dataList.add(ad) + return@forEachIndexed + } + dataList.add(movie.toUiModel()) + } + return dataList + } + + companion object { + private const val AD_POST_INTERVAL = 3 + } +} From de04d9b46262aa896ac68aed2c8983b81317ae37 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 17:00:11 +0900 Subject: [PATCH 13/90] =?UTF-8?q?refactor(ReservationActivity):=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../movie/view/seatselection/SeatSelectionActivity.kt | 2 +- app/src/main/res/layout/activity_reservation.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4600f42a3..1268fbe25 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,7 +26,7 @@ android:name=".view.ReservationCompletedActivity" android:exported="false" /> + tools:context=".view.moviemain.reservation.ReservationActivity"> Date: Wed, 3 May 2023 17:01:49 +0900 Subject: [PATCH 14/90] =?UTF-8?q?refactor(Movie):=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=A1=9C=EC=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/movie/view/mapper/MovieMapper.kt | 7 +++---- .../movie/view/model/MovieListModel.kt | 16 ++-------------- .../moviemain/movielist/MovieItemViewHolder.kt | 4 ++-- .../main/java/woowacourse/movie/domain/Movie.kt | 13 ++++++++++++- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt b/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt index 4ff73c13b..9414f4915 100644 --- a/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt +++ b/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt @@ -6,8 +6,7 @@ import woowacourse.movie.view.model.MovieListModel.MovieUiModel fun Movie.toUiModel(): MovieUiModel = MovieUiModel( title, - screeningStartDate, - screeningEndDate, + getAllScreeningDates(), runningTime.value, posterResourceId, summary @@ -15,8 +14,8 @@ fun Movie.toUiModel(): MovieUiModel = MovieUiModel( fun MovieUiModel.toDomainModel(): Movie = Movie( title, - screeningStartDate, - screeningEndDate, + screeningDates.min(), + screeningDates.max(), Minute(runningTime), posterResourceId, summary diff --git a/app/src/main/java/woowacourse/movie/view/model/MovieListModel.kt b/app/src/main/java/woowacourse/movie/view/model/MovieListModel.kt index 23798be9f..060dc70cc 100644 --- a/app/src/main/java/woowacourse/movie/view/model/MovieListModel.kt +++ b/app/src/main/java/woowacourse/movie/view/model/MovieListModel.kt @@ -4,7 +4,6 @@ import android.os.Parcelable import androidx.annotation.DrawableRes import kotlinx.parcelize.Parcelize import java.time.LocalDate -import java.time.temporal.ChronoUnit sealed class MovieListModel { @@ -16,20 +15,9 @@ sealed class MovieListModel { @Parcelize data class MovieUiModel( val title: String, - val screeningStartDate: LocalDate, - val screeningEndDate: LocalDate, + val screeningDates: List, val runningTime: Int, val posterResourceId: Int, val summary: String - ) : Parcelable, MovieListModel() { - fun getAllScreeningDates(): List { - val screeningDates = mutableListOf() - var screeningDate = screeningStartDate - repeat(ChronoUnit.DAYS.between(screeningStartDate, screeningEndDate).toInt() + 1) { - screeningDates.add(screeningDate) - screeningDate = screeningDate.plusDays(1) - } - return screeningDates - } - } + ) : Parcelable, MovieListModel() } diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieItemViewHolder.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieItemViewHolder.kt index 440f92f2a..640b7196f 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieItemViewHolder.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieItemViewHolder.kt @@ -17,8 +17,8 @@ class MovieItemViewHolder( movieTitle.text = movie.title movieScreeningDate.text = context.resources.getString(R.string.screening_date_format).format( - movie.screeningStartDate.format(DATE_FORMATTER), - movie.screeningEndDate.format(DATE_FORMATTER) + movie.screeningDates.min().format(DATE_FORMATTER), + movie.screeningDates.max().format(DATE_FORMATTER) ) movieRunningTime.text = context.resources.getString(R.string.running_time_format) .format(movie.runningTime) diff --git a/domain/src/main/java/woowacourse/movie/domain/Movie.kt b/domain/src/main/java/woowacourse/movie/domain/Movie.kt index 2edb46562..2b5658a8b 100644 --- a/domain/src/main/java/woowacourse/movie/domain/Movie.kt +++ b/domain/src/main/java/woowacourse/movie/domain/Movie.kt @@ -1,6 +1,7 @@ package woowacourse.movie.domain import java.time.LocalDate +import java.time.temporal.ChronoUnit class Movie( val title: String, @@ -9,4 +10,14 @@ class Movie( val runningTime: Minute, val posterResourceId: Int, val summary: String -) +) { + fun getAllScreeningDates(): List { + val screeningDates = mutableListOf() + var screeningDate = screeningStartDate + repeat(ChronoUnit.DAYS.between(screeningStartDate, screeningEndDate).toInt() + 1) { + screeningDates.add(screeningDate) + screeningDate = screeningDate.plusDays(1) + } + return screeningDates + } +} From f36a1183a89eb607e31c76044e13f45bec1a4939 Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 17:02:30 +0900 Subject: [PATCH 15/90] refactor(Ktlint): KtlintFormat --- .../java/woowacourse/movie/view/ReservationCompletedActivity.kt | 1 + .../movie/view/moviemain/movielist/MovieListFragment.kt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt index a6528f003..b10838001 100644 --- a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt @@ -19,6 +19,7 @@ import woowacourse.movie.databinding.ActivityReservationCompletedBinding import woowacourse.movie.util.DATE_FORMATTER import woowacourse.movie.util.TIME_FORMATTER import woowacourse.movie.util.getParcelableCompat +import woowacourse.movie.view.model.AlarmController import woowacourse.movie.view.model.ReservationUiModel import woowacourse.movie.view.moviemain.MovieMainActivity import woowacourse.movie.view.moviemain.setting.SettingFragment diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt index 5cd9c1300..fcf5afc54 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListFragment.kt @@ -7,8 +7,8 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView import woowacourse.movie.R -import woowacourse.movie.view.ReservationActivity import woowacourse.movie.view.model.MovieListModel +import woowacourse.movie.view.moviemain.reservation.ReservationActivity class MovieListFragment : Fragment(R.layout.fragment_movie_list) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { From 11e4cc566fea6b7e416a690878a8062640d5193f Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 17:03:19 +0900 Subject: [PATCH 16/90] =?UTF-8?q?refactor(ReservationActivity):=20MVP=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/view/ReservationActivityTest.kt | 15 +- .../data/reservation/ReservationConstract.kt | 11 ++ .../reservation}/ReservationActivity.kt | 129 ++++++------------ .../reservation/ReservationContract.kt | 18 +++ .../reservation/ReservationPresenter.kt | 27 ++++ 5 files changed, 105 insertions(+), 95 deletions(-) create mode 100644 app/src/main/java/woowacourse/movie/data/reservation/ReservationConstract.kt rename app/src/main/java/woowacourse/movie/view/{ => moviemain/reservation}/ReservationActivity.kt (51%) create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationContract.kt create mode 100644 app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationPresenter.kt diff --git a/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt b/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt index c8d160553..c6c8e07f9 100644 --- a/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt +++ b/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt @@ -12,24 +12,27 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import woowacourse.movie.R -import woowacourse.movie.view.model.MovieListModel +import woowacourse.movie.domain.Minute +import woowacourse.movie.domain.Movie +import woowacourse.movie.view.mapper.toUiModel +import woowacourse.movie.view.moviemain.reservation.ReservationActivity import java.time.LocalDate @RunWith(AndroidJUnit4::class) class ReservationActivityTest { - private val movie = MovieListModel.MovieUiModel( + private val movie = Movie( "해리 포터와 마법사의 돌", LocalDate.of(2024, 3, 1), LocalDate.of(2024, 3, 31), - 152, + Minute(152), R.drawable.harry_potter1_poster, - "《해리 포터와 마법사의 돌》은 2001년 J. K. 롤링의 동명 소설을 원작으로 하여 만든, 영국과 미국 합작, 판타지 영화이다. 해리포터 시리즈 영화 8부작 중 첫 번째에 해당하는 작품이다. 크리스 콜럼버스가 감독을 맡았다." - ) + "《해리 포터와 마법사의 돌》은 2001년 J. K. 롤링의 동명 소설을 원작으로 하여 만든, 영국과 미국 합작, 판타지 영화이다. 해리포터 시리즈 영화 8부작 중 첫 번째에 해당하는 작품이다. 크리스 콜럼버스가 감독을 맡았다.", + ).toUiModel() private val intent = ReservationActivity.newIntent( ApplicationProvider.getApplicationContext(), - movie + movie, ) @get:Rule diff --git a/app/src/main/java/woowacourse/movie/data/reservation/ReservationConstract.kt b/app/src/main/java/woowacourse/movie/data/reservation/ReservationConstract.kt new file mode 100644 index 000000000..27563c1d3 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/data/reservation/ReservationConstract.kt @@ -0,0 +1,11 @@ +package woowacourse.movie.data.reservation + +import android.provider.BaseColumns + +object ReservationConstract : BaseColumns { + const val TABLE_NAME = "reservation" + const val TABLE_COLUMN_TITLE = "title" + const val TABLE_COLUMN_SCREENING_TIME = "screening_time" + const val TABLE_COLUMN_SEATS = "seats" + const val TABLE_COLUMN_FEE = "fee" +} diff --git a/app/src/main/java/woowacourse/movie/view/ReservationActivity.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationActivity.kt similarity index 51% rename from app/src/main/java/woowacourse/movie/view/ReservationActivity.kt rename to app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationActivity.kt index 25b215c02..991e07632 100644 --- a/app/src/main/java/woowacourse/movie/view/ReservationActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationActivity.kt @@ -1,4 +1,4 @@ -package woowacourse.movie.view +package woowacourse.movie.view.moviemain.reservation import android.content.Context import android.content.Intent @@ -7,14 +7,12 @@ import android.view.MenuItem import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import woowacourse.movie.R import woowacourse.movie.databinding.ActivityReservationBinding -import woowacourse.movie.domain.Reservation -import woowacourse.movie.domain.ScreeningTime import woowacourse.movie.util.DATE_FORMATTER import woowacourse.movie.util.getParcelableCompat -import woowacourse.movie.util.getSerializableCompat import woowacourse.movie.view.model.MovieListModel.MovieUiModel import woowacourse.movie.view.model.ReservationOptions import woowacourse.movie.view.seatselection.SeatSelectionActivity @@ -22,46 +20,41 @@ import java.time.LocalDate import java.time.LocalDateTime import java.time.LocalTime -class ReservationActivity : AppCompatActivity() { +class ReservationActivity : AppCompatActivity(), ReservationContract.View { private lateinit var binding: ActivityReservationBinding - - private var peopleCountSaved = 1 - private lateinit var selectedScreeningDate: LocalDate - private lateinit var selectedScreeningTime: LocalTime - private val movie: MovieUiModel by lazy { initMovieFromIntent() } - private var timeSpinnerPosition = 0 + override lateinit var presenter: ReservationContract.Presenter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityReservationBinding.inflate(layoutInflater) setContentView(binding.root) + val movie = intent.getParcelableCompat(MOVIE) + if (movie == null) { + Toast.makeText(this, "데이터가 없습니다. 다시 시도해주세요.", Toast.LENGTH_SHORT).show() + finish() + return + } + presenter = ReservationPresenter(this) - initViewData() - initSpinner() - initPeopleCountAdjustButtonClickListener() - initReserveButtonClickListener() + setViewData(movie) + setDateSpinner(movie.screeningDates) + setPeopleCountAdjustButtonClickListener() + setReserveButtonClickListener(movie) supportActionBar?.setDisplayHomeAsUpEnabled(true) } - override fun onResume() { - super.onResume() - binding.peopleCount.text = peopleCountSaved.toString() + override fun setCount(count: Int) { + binding.peopleCount.text = count.toString() } - private fun initMovieFromIntent(): MovieUiModel { - val movie = intent.getParcelableCompat(MOVIE) - requireNotNull(movie) { "인텐트로 받아온 데이터가 널일 수 없습니다." } - return movie - } - - private fun initViewData() { + private fun setViewData(movie: MovieUiModel) { binding.apply { moviePoster.setImageResource(movie.posterResourceId) movieTitle.text = movie.title movieScreeningDate.text = getString(R.string.screening_date_format).format( - movie.screeningStartDate.format(DATE_FORMATTER), - movie.screeningEndDate.format(DATE_FORMATTER), + movie.screeningDates.min().format(DATE_FORMATTER), + movie.screeningDates.max().format(DATE_FORMATTER), ) movieRunningTime.text = getString(R.string.running_time_format).format(movie.runningTime) @@ -69,12 +62,7 @@ class ReservationActivity : AppCompatActivity() { } } - private fun initSpinner() { - selectedScreeningDate = movie.screeningStartDate - selectedScreeningTime = ScreeningTime(selectedScreeningDate).getFirstScreeningTime() - - val screeningDates = movie.getAllScreeningDates() - + private fun setDateSpinner(screeningDates: List) { val dateSpinnerAdapter = ArrayAdapter( this, android.R.layout.simple_spinner_item, @@ -90,8 +78,7 @@ class ReservationActivity : AppCompatActivity() { position: Int, id: Long, ) { - selectedScreeningDate = screeningDates[position] - initTimeSpinner(timeSpinnerPosition) + presenter.onDateSpinnerChanged(screeningDates[position]) } override fun onNothingSelected(parent: AdapterView<*>?) = Unit @@ -99,9 +86,7 @@ class ReservationActivity : AppCompatActivity() { } } - private fun initTimeSpinner(selectedPosition: Int?) { - val screeningTimes = ScreeningTime(selectedScreeningDate).getAllScreeningTimes() - + override fun setTimeSpinner(screeningTimes: List) { val timeSpinnerAdapter = ArrayAdapter( this, android.R.layout.simple_spinner_item, @@ -109,56 +94,29 @@ class ReservationActivity : AppCompatActivity() { ) binding.timeSpinner.apply { adapter = timeSpinnerAdapter - - if (selectedPosition != null) { - this.setSelection(selectedPosition, false) - } - onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>?, - view: View?, - position: Int, - id: Long, - ) { - selectedScreeningTime = screeningTimes[position] - } - - override fun onNothingSelected(parent: AdapterView<*>?) = Unit - } } } - private fun initPeopleCountAdjustButtonClickListener() { + private fun setPeopleCountAdjustButtonClickListener() { binding.apply { minusButton.setOnClickListener { - decreasePeopleCount() + presenter.onMinusClick() } plusButton.setOnClickListener { - increasePeopleCount() + presenter.onPlusClick() } } } - private fun ActivityReservationBinding.decreasePeopleCount() { - if (peopleCountSaved > Reservation.MIN_PEOPLE_COUNT) { - peopleCountSaved-- - peopleCount.text = peopleCountSaved.toString() - } - } - - private fun ActivityReservationBinding.increasePeopleCount() { - if (peopleCountSaved < Reservation.MAX_PEOPLE_COUNT) { - peopleCountSaved++ - peopleCount.text = peopleCountSaved.toString() - } - } - - private fun initReserveButtonClickListener() { + private fun setReserveButtonClickListener(movie: MovieUiModel) { binding.reservationButton.setOnClickListener { val reservationOptions = ReservationOptions( movie.title, - LocalDateTime.of(selectedScreeningDate, selectedScreeningTime), - peopleCountSaved, + LocalDateTime.of( + binding.dateSpinner.selectedItem as LocalDate, + binding.timeSpinner.selectedItem as LocalTime, + ), + binding.peopleCount.text.toString().toInt(), ) startActivity(SeatSelectionActivity.newIntent(this, reservationOptions, movie)) } @@ -168,25 +126,17 @@ class ReservationActivity : AppCompatActivity() { super.onSaveInstanceState(outState) outState.apply { - putInt(PEOPLE_COUNT, peopleCountSaved) - putSerializable(SELECTED_DATE, selectedScreeningDate) - putSerializable(SELECTED_TIME, selectedScreeningTime) + putInt(PEOPLE_COUNT, binding.peopleCount.text.toString().toInt()) + putInt(SELECTED_DATE_POSITION, binding.dateSpinner.selectedItemPosition) putInt(SELECTED_TIME_POSITION, binding.timeSpinner.selectedItemPosition) } } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) - - peopleCountSaved = savedInstanceState.getInt(PEOPLE_COUNT) - timeSpinnerPosition = savedInstanceState.getInt(SELECTED_TIME_POSITION) - - savedInstanceState.getSerializableCompat(SELECTED_DATE)?.run { - selectedScreeningDate = this - } - savedInstanceState.getSerializableCompat(SELECTED_TIME)?.run { - selectedScreeningTime = this - } + setCount(savedInstanceState.getInt(PEOPLE_COUNT)) + binding.dateSpinner.setSelection(savedInstanceState.getInt(SELECTED_DATE_POSITION)) + binding.timeSpinner.setSelection(savedInstanceState.getInt(SELECTED_TIME_POSITION)) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -198,10 +148,11 @@ class ReservationActivity : AppCompatActivity() { companion object { private const val PEOPLE_COUNT = "PEOPLE_COUNT" - private const val SELECTED_DATE = "SELECTED_DATE" - private const val SELECTED_TIME = "SELECTED_TIME" + + private const val SELECTED_DATE_POSITION = "SELECTED_DATE_POSITION" private const val SELECTED_TIME_POSITION = "SELECTED_TIME_POSITION" private const val MOVIE = "MOVIE" + fun newIntent(context: Context, movie: MovieUiModel): Intent { val intent = Intent(context, ReservationActivity::class.java) intent.putExtra(MOVIE, movie) diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationContract.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationContract.kt new file mode 100644 index 000000000..893e296a6 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationContract.kt @@ -0,0 +1,18 @@ +package woowacourse.movie.view.moviemain.reservation + +import java.time.LocalDate +import java.time.LocalTime + +interface ReservationContract { + interface View { + var presenter: Presenter + fun setCount(count: Int) + fun setTimeSpinner(times: List) + } + + interface Presenter { + fun onMinusClick() + fun onPlusClick() + fun onDateSpinnerChanged(date: LocalDate) + } +} diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationPresenter.kt b/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationPresenter.kt new file mode 100644 index 000000000..915f6857e --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/moviemain/reservation/ReservationPresenter.kt @@ -0,0 +1,27 @@ +package woowacourse.movie.view.moviemain.reservation + +import woowacourse.movie.domain.Reservation +import woowacourse.movie.domain.ScreeningTime +import java.time.LocalDate + +class ReservationPresenter(private val view: ReservationContract.View) : ReservationContract.Presenter { + private var peopleCountSaved = 0 + + override fun onMinusClick() { + if (peopleCountSaved > Reservation.MIN_PEOPLE_COUNT) { + peopleCountSaved-- + view.setCount(peopleCountSaved) + } + } + + override fun onPlusClick() { + if (peopleCountSaved < Reservation.MAX_PEOPLE_COUNT) { + peopleCountSaved++ + view.setCount(peopleCountSaved) + } + } + + override fun onDateSpinnerChanged(date: LocalDate) { + view.setTimeSpinner(ScreeningTime(date).getAllScreeningTimes()) + } +} From ceb7db9c696ae03f30a0bf6832caa16e8f2f9a8b Mon Sep 17 00:00:00 2001 From: hyemdooly Date: Wed, 3 May 2023 17:18:57 +0900 Subject: [PATCH 17/90] =?UTF-8?q?refactor(package):=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/woowacourse/movie/view/ReservationActivityTest.kt | 2 +- app/src/main/AndroidManifest.xml | 2 +- .../movie/view/moviemain/movielist/MovieListFragment.kt | 2 +- .../view/{moviemain => }/reservation/ReservationActivity.kt | 2 +- .../view/{moviemain => }/reservation/ReservationContract.kt | 2 +- .../view/{moviemain => }/reservation/ReservationPresenter.kt | 5 +++-- app/src/main/res/layout/activity_reservation.xml | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) rename app/src/main/java/woowacourse/movie/view/{moviemain => }/reservation/ReservationActivity.kt (99%) rename app/src/main/java/woowacourse/movie/view/{moviemain => }/reservation/ReservationContract.kt (87%) rename app/src/main/java/woowacourse/movie/view/{moviemain => }/reservation/ReservationPresenter.kt (88%) diff --git a/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt b/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt index c6c8e07f9..bb2724dcf 100644 --- a/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt +++ b/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt @@ -15,7 +15,7 @@ import woowacourse.movie.R import woowacourse.movie.domain.Minute import woowacourse.movie.domain.Movie import woowacourse.movie.view.mapper.toUiModel -import woowacourse.movie.view.moviemain.reservation.ReservationActivity +import woowacourse.movie.view.reservation.ReservationActivity import java.time.LocalDate @RunWith(AndroidJUnit4::class) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1268fbe25..354cfb3a7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,7 +26,7 @@ android:name=".view.ReservationCompletedActivity" android:exported="false" /> + tools:context=".view.reservation.ReservationActivity"> Date: Thu, 4 May 2023 01:06:51 +0900 Subject: [PATCH 18/90] =?UTF-8?q?refactor(domain,=20SeatSelectionAcitivty)?= =?UTF-8?q?:=20=EB=8F=84=EB=A9=94=EC=9D=B8,=20=EC=A2=8C=EC=84=9D=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95=20=EC=BD=94=EB=93=9C=20=EC=A0=84=EC=B2=B4=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../movie/view/ReservationActivityTest.kt | 4 +- .../movie/view/SeatSelectionActivityTest.kt | 4 +- .../movie/data/MovieMockRepository.kt | 4 +- .../java/woowacourse/movie/data/Theater.kt | 26 ++ .../reservation/ReservationMockRepository.kt | 2 +- .../view/ReservationCompletedActivity.kt | 4 +- .../movie/view/mapper/MovieMapper.kt | 4 +- .../movie/view/mapper/PriceMapper.kt | 8 + .../movie/view/mapper/ReservationMapper.kt | 8 +- .../movie/view/mapper/SeatMapper.kt | 13 +- .../movie/view/model/ReservationUiModel.kt | 4 +- .../movie/view/model/SeatUiModel.kt | 7 +- .../moviemain/movielist/MovieListPresenter.kt | 2 +- .../view/reservation/ReservationPresenter.kt | 7 +- .../seatselection/SeatSelectionActivity.kt | 239 +++++++++--------- .../res/layout/activity_seat_selection.xml | 81 +++--- app/src/main/res/values/colors.xml | 7 + app/src/main/res/values/strings.xml | 9 +- .../movie/domain/DayDiscountCondition.kt | 8 - .../movie/domain/DiscountCondition.kt | 7 - .../movie/domain/DiscountPolicy.kt | 49 ---- .../java/woowacourse/movie/domain/Money.kt | 18 -- .../woowacourse/movie/domain/Reservation.kt | 26 -- .../movie/domain/ReservationAgency.kt | 29 --- .../domain/ScreeningTimeDiscountCondition.kt | 8 - .../java/woowacourse/movie/domain/Seat.kt | 32 --- .../java/woowacourse/movie/domain/SeatType.kt | 7 - .../woowacourse/movie/domain/TimeRange.kt | 8 - .../movie/domain/{ => movie}/Minute.kt | 2 +- .../movie/domain/{ => movie}/Movie.kt | 2 +- .../movie/domain/{ => movie}/ScreeningTime.kt | 2 +- .../movie/domain/policy/DiscountPolicies.kt | 5 + .../movie/domain/policy/DiscountPolicy.kt | 14 + .../movie/domain/policy/MorningPolicy.kt | 15 ++ .../movie/domain/policy/MovieDayPolicy.kt | 15 ++ .../movie/domain/policy/NightPolicy.kt | 15 ++ .../woowacourse/movie/domain/price/Price.kt | 21 ++ .../movie/domain/price/PriceCalculator.kt | 17 ++ .../domain/repository/MovieRepository.kt | 2 +- .../repository/ReservationRepository.kt | 2 +- .../movie/domain/reservation/Reservation.kt | 12 + .../movie/domain/system/PriceSystem.kt | 25 ++ .../woowacourse/movie/domain/system/Seat.kt | 11 + .../movie/domain/system/SeatSelectSystem.kt | 33 +++ .../movie/domain/system/SelectResult.kt | 13 + .../woowacourse/movie/domain/theater/Grade.kt | 7 + .../movie/domain/theater/TheaterInfo.kt | 13 + .../src/test/java/DayDiscountConditionTest.kt | 15 -- domain/src/test/java/DiscountPolicyTest.kt | 88 ------- domain/src/test/java/MinuteTest.kt | 1 + domain/src/test/java/MoneyTest.kt | 27 -- domain/src/test/java/MorningPolicyTest.kt | 22 ++ domain/src/test/java/MovieDayPolicyTest.kt | 47 ++++ domain/src/test/java/NightPolicyTest.kt | 23 ++ domain/src/test/java/PriceCalculatorTest.kt | 36 +++ domain/src/test/java/PriceSystemTest.kt | 63 +++++ domain/src/test/java/ReservationAgencyTest.kt | 138 ---------- domain/src/test/java/ReservationTest.kt | 65 ----- .../ScreeningTimeDiscountConditionTest.kt | 18 -- domain/src/test/java/ScreeningTimeTest.kt | 50 ---- domain/src/test/java/SeatSelectSystemTest.kt | 79 ++++++ domain/src/test/java/SeatTest.kt | 20 -- 62 files changed, 721 insertions(+), 822 deletions(-) create mode 100644 app/src/main/java/woowacourse/movie/data/Theater.kt create mode 100644 app/src/main/java/woowacourse/movie/view/mapper/PriceMapper.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/DayDiscountCondition.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/DiscountCondition.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/DiscountPolicy.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/Money.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/Reservation.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/ReservationAgency.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/ScreeningTimeDiscountCondition.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/Seat.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/SeatType.kt delete mode 100644 domain/src/main/java/woowacourse/movie/domain/TimeRange.kt rename domain/src/main/java/woowacourse/movie/domain/{ => movie}/Minute.kt (88%) rename domain/src/main/java/woowacourse/movie/domain/{ => movie}/Movie.kt (94%) rename domain/src/main/java/woowacourse/movie/domain/{ => movie}/ScreeningTime.kt (96%) create mode 100644 domain/src/main/java/woowacourse/movie/domain/policy/DiscountPolicies.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/policy/DiscountPolicy.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/policy/MorningPolicy.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/policy/MovieDayPolicy.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/policy/NightPolicy.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/price/Price.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/price/PriceCalculator.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/reservation/Reservation.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/system/PriceSystem.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/system/Seat.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/system/SeatSelectSystem.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/system/SelectResult.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/theater/Grade.kt create mode 100644 domain/src/main/java/woowacourse/movie/domain/theater/TheaterInfo.kt delete mode 100644 domain/src/test/java/DayDiscountConditionTest.kt delete mode 100644 domain/src/test/java/DiscountPolicyTest.kt delete mode 100644 domain/src/test/java/MoneyTest.kt create mode 100644 domain/src/test/java/MorningPolicyTest.kt create mode 100644 domain/src/test/java/MovieDayPolicyTest.kt create mode 100644 domain/src/test/java/NightPolicyTest.kt create mode 100644 domain/src/test/java/PriceCalculatorTest.kt create mode 100644 domain/src/test/java/PriceSystemTest.kt delete mode 100644 domain/src/test/java/ReservationAgencyTest.kt delete mode 100644 domain/src/test/java/ReservationTest.kt delete mode 100644 domain/src/test/java/ScreeningTimeDiscountConditionTest.kt delete mode 100644 domain/src/test/java/ScreeningTimeTest.kt create mode 100644 domain/src/test/java/SeatSelectSystemTest.kt delete mode 100644 domain/src/test/java/SeatTest.kt diff --git a/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt b/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt index bb2724dcf..29549f670 100644 --- a/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt +++ b/app/src/androidTest/java/woowacourse/movie/view/ReservationActivityTest.kt @@ -12,8 +12,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import woowacourse.movie.R -import woowacourse.movie.domain.Minute -import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.movie.Minute +import woowacourse.movie.domain.movie.Movie import woowacourse.movie.view.mapper.toUiModel import woowacourse.movie.view.reservation.ReservationActivity import java.time.LocalDate diff --git a/app/src/androidTest/java/woowacourse/movie/view/SeatSelectionActivityTest.kt b/app/src/androidTest/java/woowacourse/movie/view/SeatSelectionActivityTest.kt index e328fbf1f..d5b7f0ac9 100644 --- a/app/src/androidTest/java/woowacourse/movie/view/SeatSelectionActivityTest.kt +++ b/app/src/androidTest/java/woowacourse/movie/view/SeatSelectionActivityTest.kt @@ -17,8 +17,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import woowacourse.movie.R -import woowacourse.movie.domain.Minute -import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.movie.Minute +import woowacourse.movie.domain.movie.Movie import woowacourse.movie.view.mapper.toUiModel import woowacourse.movie.view.model.ReservationOptions import woowacourse.movie.view.seatselection.SeatSelectionActivity diff --git a/app/src/main/java/woowacourse/movie/data/MovieMockRepository.kt b/app/src/main/java/woowacourse/movie/data/MovieMockRepository.kt index 725eb834a..e95826f00 100644 --- a/app/src/main/java/woowacourse/movie/data/MovieMockRepository.kt +++ b/app/src/main/java/woowacourse/movie/data/MovieMockRepository.kt @@ -1,8 +1,8 @@ package woowacourse.movie.data import woowacourse.movie.R -import woowacourse.movie.domain.Minute -import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.movie.Minute +import woowacourse.movie.domain.movie.Movie import woowacourse.movie.domain.repository.MovieRepository import java.time.LocalDate diff --git a/app/src/main/java/woowacourse/movie/data/Theater.kt b/app/src/main/java/woowacourse/movie/data/Theater.kt new file mode 100644 index 000000000..c7e78b130 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/data/Theater.kt @@ -0,0 +1,26 @@ +package woowacourse.movie.data + +import woowacourse.movie.domain.policy.MorningPolicy +import woowacourse.movie.domain.policy.MovieDayPolicy +import woowacourse.movie.domain.policy.NightPolicy +import woowacourse.movie.domain.theater.Grade +import woowacourse.movie.domain.theater.TheaterInfo + +object Theater { + private val rowGrade = mapOf( + 0 to Grade.B, + 1 to Grade.B, + 2 to Grade.S, + 3 to Grade.S, + 4 to Grade.A, + ) + val row = 5 + val col = 4 + + val info = TheaterInfo(rowGrade, row, col) + val policies = listOf( + MovieDayPolicy(), + MorningPolicy(), + NightPolicy(), + ) +} diff --git a/app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt b/app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt index 40d617ab4..627cb3d9a 100644 --- a/app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt +++ b/app/src/main/java/woowacourse/movie/data/reservation/ReservationMockRepository.kt @@ -1,7 +1,7 @@ package woowacourse.movie.data.reservation -import woowacourse.movie.domain.Reservation import woowacourse.movie.domain.repository.ReservationRepository +import woowacourse.movie.domain.reservation.Reservation object ReservationMockRepository : ReservationRepository { diff --git a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt index b10838001..c841be7db 100644 --- a/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/ReservationCompletedActivity.kt @@ -71,11 +71,11 @@ class ReservationCompletedActivity : AppCompatActivity() { peopleCount.text = getString(R.string.reservation_people_count_format) .format( getString(R.string.general_person), - reservation.peopleCount, + reservation.count, reservation.seats.joinToString(), ) totalPrice.text = - getString(R.string.total_price_format).format(DECIMAL_FORMAT.format(reservation.finalReservationFee)) + getString(R.string.total_price_format).format(DECIMAL_FORMAT.format(reservation.count)) } } diff --git a/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt b/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt index 9414f4915..e353045d1 100644 --- a/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt +++ b/app/src/main/java/woowacourse/movie/view/mapper/MovieMapper.kt @@ -1,7 +1,7 @@ package woowacourse.movie.view.mapper -import woowacourse.movie.domain.Minute -import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.movie.Minute +import woowacourse.movie.domain.movie.Movie import woowacourse.movie.view.model.MovieListModel.MovieUiModel fun Movie.toUiModel(): MovieUiModel = MovieUiModel( diff --git a/app/src/main/java/woowacourse/movie/view/mapper/PriceMapper.kt b/app/src/main/java/woowacourse/movie/view/mapper/PriceMapper.kt new file mode 100644 index 000000000..8ddd89814 --- /dev/null +++ b/app/src/main/java/woowacourse/movie/view/mapper/PriceMapper.kt @@ -0,0 +1,8 @@ +package woowacourse.movie.view.mapper + +import woowacourse.movie.domain.price.Price +import java.text.DecimalFormat + +fun Price.toUiModel(): String { + return DecimalFormat("#,###").format(price) +} diff --git a/app/src/main/java/woowacourse/movie/view/mapper/ReservationMapper.kt b/app/src/main/java/woowacourse/movie/view/mapper/ReservationMapper.kt index af2a2b391..a5bde3216 100644 --- a/app/src/main/java/woowacourse/movie/view/mapper/ReservationMapper.kt +++ b/app/src/main/java/woowacourse/movie/view/mapper/ReservationMapper.kt @@ -1,12 +1,12 @@ package woowacourse.movie.view.mapper -import woowacourse.movie.domain.Reservation +import woowacourse.movie.domain.reservation.Reservation import woowacourse.movie.view.model.ReservationUiModel fun Reservation.toUiModel(): ReservationUiModel = ReservationUiModel( - movieTitle, + title, screeningDateTime, seats.size, - seats.map { it.toUiModel().name }, - finalReservationFee.amount + seats.map { it.toUiModel().seatId }, + price.price ) diff --git a/app/src/main/java/woowacourse/movie/view/mapper/SeatMapper.kt b/app/src/main/java/woowacourse/movie/view/mapper/SeatMapper.kt index 0139dd451..baa9ddea3 100644 --- a/app/src/main/java/woowacourse/movie/view/mapper/SeatMapper.kt +++ b/app/src/main/java/woowacourse/movie/view/mapper/SeatMapper.kt @@ -1,15 +1,6 @@ package woowacourse.movie.view.mapper -import woowacourse.movie.R -import woowacourse.movie.domain.Seat -import woowacourse.movie.domain.SeatType +import woowacourse.movie.domain.system.Seat import woowacourse.movie.view.model.SeatUiModel -fun Seat.toUiModel(): SeatUiModel = SeatUiModel( - ('A' + row - 1).toString() + "$column", - when (this.type) { - SeatType.BType -> R.color.purple_700 - SeatType.SType -> R.color.green - SeatType.AType -> R.color.blue - } -) +fun Seat.toUiModel(): SeatUiModel = SeatUiModel(row, col) diff --git a/app/src/main/java/woowacourse/movie/view/model/ReservationUiModel.kt b/app/src/main/java/woowacourse/movie/view/model/ReservationUiModel.kt index 736494f9a..1b0159415 100644 --- a/app/src/main/java/woowacourse/movie/view/model/ReservationUiModel.kt +++ b/app/src/main/java/woowacourse/movie/view/model/ReservationUiModel.kt @@ -8,7 +8,7 @@ import java.time.LocalDateTime data class ReservationUiModel( val title: String, val screeningDateTime: LocalDateTime, - val peopleCount: Int, + val count: Int, val seats: List, - val finalReservationFee: Int + val price: Int ) : Parcelable diff --git a/app/src/main/java/woowacourse/movie/view/model/SeatUiModel.kt b/app/src/main/java/woowacourse/movie/view/model/SeatUiModel.kt index 5f6a22728..2001b4bd6 100644 --- a/app/src/main/java/woowacourse/movie/view/model/SeatUiModel.kt +++ b/app/src/main/java/woowacourse/movie/view/model/SeatUiModel.kt @@ -1,6 +1,5 @@ package woowacourse.movie.view.model -class SeatUiModel( - val name: String, - val color: Int -) +class SeatUiModel(row: Int, col: Int) : java.io.Serializable { + val seatId: String = ('A'.code + row).toChar() + (col + 1).toString() +} diff --git a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt index 353a54200..2306631b5 100644 --- a/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt +++ b/app/src/main/java/woowacourse/movie/view/moviemain/movielist/MovieListPresenter.kt @@ -2,7 +2,7 @@ package woowacourse.movie.view.moviemain.movielist import woowacourse.movie.R import woowacourse.movie.data.MovieMockRepository -import woowacourse.movie.domain.Movie +import woowacourse.movie.domain.movie.Movie import woowacourse.movie.domain.repository.MovieRepository import woowacourse.movie.view.mapper.toUiModel import woowacourse.movie.view.model.MovieListModel diff --git a/app/src/main/java/woowacourse/movie/view/reservation/ReservationPresenter.kt b/app/src/main/java/woowacourse/movie/view/reservation/ReservationPresenter.kt index 129ca8c67..44feeccfe 100644 --- a/app/src/main/java/woowacourse/movie/view/reservation/ReservationPresenter.kt +++ b/app/src/main/java/woowacourse/movie/view/reservation/ReservationPresenter.kt @@ -1,7 +1,6 @@ package woowacourse.movie.view.reservation -import woowacourse.movie.domain.Reservation -import woowacourse.movie.domain.ScreeningTime +import woowacourse.movie.domain.movie.ScreeningTime import java.time.LocalDate class ReservationPresenter(private val view: ReservationContract.View) : @@ -9,14 +8,14 @@ class ReservationPresenter(private val view: ReservationContract.View) : private var peopleCountSaved = 0 override fun onMinusClick() { - if (peopleCountSaved > Reservation.MIN_PEOPLE_COUNT) { + if (peopleCountSaved > 0) { peopleCountSaved-- view.setCount(peopleCountSaved) } } override fun onPlusClick() { - if (peopleCountSaved < Reservation.MAX_PEOPLE_COUNT) { + if (peopleCountSaved < 20) { peopleCountSaved++ view.setCount(peopleCountSaved) } diff --git a/app/src/main/java/woowacourse/movie/view/seatselection/SeatSelectionActivity.kt b/app/src/main/java/woowacourse/movie/view/seatselection/SeatSelectionActivity.kt index 29744f1f2..51766300e 100644 --- a/app/src/main/java/woowacourse/movie/view/seatselection/SeatSelectionActivity.kt +++ b/app/src/main/java/woowacourse/movie/view/seatselection/SeatSelectionActivity.kt @@ -3,189 +3,176 @@ package woowacourse.movie.view.seatselection import android.content.Context import android.content.Intent import android.os.Bundle +import android.view.Gravity import android.view.MenuItem -import android.widget.Button import android.widget.TableLayout import android.widget.TableRow +import android.widget.TextView +import android.widget.Toast import android.widget.Toolbar.LayoutParams import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources -import androidx.appcompat.widget.AppCompatButton import androidx.core.view.children import woowacourse.movie.R -import woowacourse.movie.data.reservation.ReservationMockRepository +import woowacourse.movie.data.Theater import woowacourse.movie.databinding.ActivitySeatSelectionBinding -import woowacourse.movie.domain.ReservationAgency -import woowacourse.movie.domain.Seat -import woowacourse.movie.domain.repository.ReservationRepository +import woowacourse.movie.domain.price.Price +import woowacourse.movie.domain.price.PriceCalculator +import woowacourse.movie.domain.reservation.Reservation +import woowacourse.movie.domain.system.PriceSystem +import woowacourse.movie.domain.system.Seat +import woowacourse.movie.domain.system.SeatSelectSystem +import woowacourse.movie.domain.system.SelectResult import woowacourse.movie.util.getParcelableCompat import woowacourse.movie.view.ReservationCompletedActivity -import woowacourse.movie.view.mapper.toDomainModel import woowacourse.movie.view.mapper.toUiModel import woowacourse.movie.view.model.MovieListModel.MovieUiModel import woowacourse.movie.view.model.ReservationOptions +import woowacourse.movie.view.model.ReservationUiModel import woowacourse.movie.view.model.SeatUiModel -import java.text.DecimalFormat +import java.time.LocalDateTime class SeatSelectionActivity : AppCompatActivity() { private lateinit var binding: ActivitySeatSelectionBinding - private val reservationRepository: ReservationRepository = ReservationMockRepository - private val reservationOptions by lazy { - intent.getParcelableCompat(RESERVATION_OPTIONS) - } - private lateinit var reservationAgency: ReservationAgency - private var selectedSeatCount = 0 - private var selectedSeats: List = emptyList() + private lateinit var seatSystem: SeatSelectSystem + private lateinit var priceSystem: PriceSystem + private val theater = Theater + private var price: Price = Price(0) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivitySeatSelectionBinding.inflate(layoutInflater) setContentView(binding.root) - initSeatButtons() - initReserveLayout() - initReservationAgency() - initConfirmReservationButton() + val reserveOptions = intent.getParcelableCompat(RESERVATION_OPTIONS) + if (reserveOptions == null) { + Toast.makeText(this, DATA_LOADING_ERROR_MESSAGE, Toast.LENGTH_LONG).show() + finish() + return + } + + seatSystem = SeatSelectSystem(Theater.info, reserveOptions.peopleCount) + priceSystem = PriceSystem(PriceCalculator(Theater.policies), reserveOptions.screeningDateTime) + + createRows() + setTitle(reserveOptions.title) + setNextButton(reserveOptions.title, reserveOptions.screeningDateTime) + setSeatViews( + binding.layoutSeats.children.filterIsInstance() + .flatMap { it.children } + .filterIsInstance().toList(), + ) supportActionBar?.setDisplayHomeAsUpEnabled(true) } - private fun initSeatButtons() { - for (row in Seat.MIN_ROW..Seat.MAX_ROW) { + private fun createRows() { + for (row in 0 until theater.row) { val tableRow = TableRow(this).apply { layoutParams = TableLayout.LayoutParams(0, 0, 1f) } - for (col in Seat.MIN_COLUMN..Seat.MAX_COLUMN) { + for (col in 0 until theater.col) { val seat = Seat(col, row) - tableRow.addView(createSeat(this, seat.toUiModel())) + tableRow.addView(createSeat(seat.toUiModel())) } - binding.seatTablelayout.addView(tableRow) + binding.layoutSeats.addView(tableRow) } } - private fun createSeat(context: Context, seatUi: SeatUiModel): AppCompatButton = - AppCompatButton(context).apply { - text = seatUi.name - setTextColor(getColor(seatUi.color)) - setOnClickListener { onSeatClick(this) } + private fun createSeat(seatUi: SeatUiModel): TextView = + TextView(this).apply { + text = seatUi.seatId + // setTextColor(getColor(seatUi.color)) + textAlignment = TextView.TEXT_ALIGNMENT_CENTER + gravity = Gravity.CENTER + // setOnClickListener { onSeatClick(this) } background = AppCompatResources.getDrawable(this@SeatSelectionActivity, R.drawable.selector_seat) layoutParams = TableRow.LayoutParams(0, LayoutParams.MATCH_PARENT, 1f) } - private fun onSeatClick(seat: Button) { - if (seat.isSelected) { - deselectSeat(seat) - return - } - selectSeat(seat) - } - - private fun deselectSeat(seat: Button) { - selectedSeatCount-- - seat.isSelected = false - binding.confirmReservationButton.isEnabled = false - binding.reservationFeeTextview.text = getString(R.string.reservation_fee_format).format( - DECIMAL_FORMAT.format(0), - ) + private fun setTitle(title: String) { + binding.textTitle.text = title } - private fun selectSeat(seat: Button) { - reservationOptions?.let { - if (selectedSeatCount < it.peopleCount) { - seat.isSelected = true - selectedSeatCount++ - if (selectedSeatCount == it.peopleCount) { - onSelectionComplete() - return - } + private fun setNextButton(title: String, dateTime: LocalDateTime) { + binding.btnNext.setOnClickListener { + with(getDialog(Reservation(title, dateTime, seatSystem.seats, price).toUiModel())) { + setCanceledOnTouchOutside(false) + show() } } } - private fun onSelectionComplete() { - val seats = binding.seatTablelayout.children - .filterIsInstance() - .flatMap { it.children } - .filterIsInstance