diff --git a/android/festago/app/src/main/AndroidManifest.xml b/android/festago/app/src/main/AndroidManifest.xml index d4f74b841..6d80b0e80 100644 --- a/android/festago/app/src/main/AndroidManifest.xml +++ b/android/festago/app/src/main/AndroidManifest.xml @@ -17,6 +17,9 @@ android:theme="@style/Theme.Festago" android:usesCleartextTraffic="true" tools:targetApi="31"> + @@ -61,4 +64,4 @@ android:exported="false" /> - \ No newline at end of file + diff --git a/android/festago/app/src/main/java/com/festago/festago/data/di/ViewModelScopeModule.kt b/android/festago/app/src/main/java/com/festago/festago/data/di/ViewModelScopeModule.kt index 403a4f89f..abe7d2efe 100644 --- a/android/festago/app/src/main/java/com/festago/festago/data/di/ViewModelScopeModule.kt +++ b/android/festago/app/src/main/java/com/festago/festago/data/di/ViewModelScopeModule.kt @@ -1,9 +1,11 @@ package com.festago.festago.data.di import com.festago.festago.data.repository.ReservationTicketDefaultRepository +import com.festago.festago.data.repository.SchoolDefaultRepository import com.festago.festago.data.repository.StudentVerificationDefaultRepository import com.festago.festago.data.repository.UserDefaultRepository import com.festago.festago.repository.ReservationTicketRepository +import com.festago.festago.repository.SchoolRepository import com.festago.festago.repository.StudentVerificationRepository import com.festago.festago.repository.UserRepository import dagger.Binds @@ -26,5 +28,9 @@ interface ViewModelScopeModule { @Binds @ViewModelScoped - fun binsUserDefaultRepository(userRepository: UserDefaultRepository): UserRepository + fun bindsUserDefaultRepository(userRepository: UserDefaultRepository): UserRepository + + @Binds + @ViewModelScoped + fun bindsSelectSchoolRepository(schoolRepository: SchoolDefaultRepository): SchoolRepository } diff --git a/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt b/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt index fe922bbc4..1229d7c15 100644 --- a/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt +++ b/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/RepositoryModule.kt @@ -2,12 +2,10 @@ package com.festago.festago.data.di.singletonscope import com.festago.festago.data.repository.AuthDefaultRepository import com.festago.festago.data.repository.FestivalDefaultRepository -import com.festago.festago.data.repository.SchoolDefaultRepository import com.festago.festago.data.repository.TicketDefaultRepository import com.festago.festago.data.repository.TokenDefaultRepository import com.festago.festago.repository.AuthRepository import com.festago.festago.repository.FestivalRepository -import com.festago.festago.repository.SchoolRepository import com.festago.festago.repository.TicketRepository import com.festago.festago.repository.TokenRepository import dagger.Binds @@ -30,10 +28,6 @@ interface RepositoryModule { @Singleton fun bindsFestivalDefaultRepository(festivalRepository: FestivalDefaultRepository): FestivalRepository - @Binds - @Singleton - fun bindsSchoolDefaultRepository(schoolRepository: SchoolDefaultRepository): SchoolRepository - @Binds @Singleton fun bindsTicketDefaultRepository(ticketRepository: TicketDefaultRepository): TicketRepository diff --git a/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt b/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt index a1154dc05..3cbb12e5e 100644 --- a/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt +++ b/android/festago/app/src/main/java/com/festago/festago/data/di/singletonscope/ServiceModule.kt @@ -2,6 +2,7 @@ package com.festago.festago.data.di.singletonscope import com.festago.festago.data.service.FestivalRetrofitService import com.festago.festago.data.service.ReservationTicketRetrofitService +import com.festago.festago.data.service.SchoolRetrofitService import com.festago.festago.data.service.StudentVerificationRetrofitService import com.festago.festago.data.service.TicketRetrofitService import com.festago.festago.data.service.TokenRetrofitService @@ -64,4 +65,12 @@ object ServiceModule { ): StudentVerificationRetrofitService { return retrofit.create(StudentVerificationRetrofitService::class.java) } + + @Provides + @Singleton + fun providesSchoolRetrofitService( + @NormalRetrofitQualifier retrofit: Retrofit + ): SchoolRetrofitService { + return retrofit.create(SchoolRetrofitService::class.java) + } } diff --git a/android/festago/app/src/main/java/com/festago/festago/data/dto/SchoolResponse.kt b/android/festago/app/src/main/java/com/festago/festago/data/dto/SchoolResponse.kt new file mode 100644 index 000000000..52646b6d5 --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/data/dto/SchoolResponse.kt @@ -0,0 +1,17 @@ +package com.festago.festago.data.dto + +import com.festago.festago.model.School +import kotlinx.serialization.Serializable + +@Serializable +data class SchoolResponse( + val id: Int, + val domain: String, + val name: String +) { + fun toDomain(): School = School( + id = id.toLong(), + domain = domain, + name = name + ) +} diff --git a/android/festago/app/src/main/java/com/festago/festago/data/dto/SchoolsResponse.kt b/android/festago/app/src/main/java/com/festago/festago/data/dto/SchoolsResponse.kt new file mode 100644 index 000000000..72e2d61ec --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/data/dto/SchoolsResponse.kt @@ -0,0 +1,11 @@ +package com.festago.festago.data.dto + +import com.festago.festago.model.School +import kotlinx.serialization.Serializable + +@Serializable +data class SchoolsResponse( + val schools: List +) { + fun toDomain(): List = schools.map { it.toDomain() } +} diff --git a/android/festago/app/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt b/android/festago/app/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt index 86d2ac26b..c0b5f93f3 100644 --- a/android/festago/app/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt +++ b/android/festago/app/src/main/java/com/festago/festago/data/repository/SchoolDefaultRepository.kt @@ -1,9 +1,21 @@ package com.festago.festago.data.repository +import com.festago.festago.data.service.SchoolRetrofitService +import com.festago.festago.data.util.runCatchingWithErrorHandler +import com.festago.festago.model.School import com.festago.festago.repository.SchoolRepository import javax.inject.Inject -class SchoolDefaultRepository @Inject constructor() : SchoolRepository { +class SchoolDefaultRepository @Inject constructor( + private val schoolRetrofitService: SchoolRetrofitService +) : SchoolRepository { + + override suspend fun loadSchools(): Result> { + schoolRetrofitService.getSchools() + .runCatchingWithErrorHandler() + .getOrElse { error -> return Result.failure(error) } + .let { return Result.success(it.toDomain()) } + } override suspend fun loadSchoolEmail(schoolId: Long): Result { // TODO: API 연동 작업 필요 diff --git a/android/festago/app/src/main/java/com/festago/festago/data/service/SchoolRetrofitService.kt b/android/festago/app/src/main/java/com/festago/festago/data/service/SchoolRetrofitService.kt new file mode 100644 index 000000000..b21234535 --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/data/service/SchoolRetrofitService.kt @@ -0,0 +1,11 @@ +package com.festago.festago.data.service + +import com.festago.festago.data.dto.SchoolsResponse +import retrofit2.Response +import retrofit2.http.GET + +interface SchoolRetrofitService { + + @GET("/schools") + suspend fun getSchools(): Response +} diff --git a/android/festago/app/src/main/java/com/festago/festago/presentation/ui/home/mypage/MyPageFragment.kt b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/home/mypage/MyPageFragment.kt index 478d0fdd1..13d8f1afe 100644 --- a/android/festago/app/src/main/java/com/festago/festago/presentation/ui/home/mypage/MyPageFragment.kt +++ b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/home/mypage/MyPageFragment.kt @@ -10,8 +10,8 @@ import androidx.fragment.app.viewModels import com.festago.festago.R import com.festago.festago.databinding.FragmentMyPageBinding import com.festago.festago.presentation.ui.home.HomeActivity +import com.festago.festago.presentation.ui.selectschool.SelectSchoolActivity import com.festago.festago.presentation.ui.signin.SignInActivity -import com.festago.festago.presentation.ui.studentverification.StudentVerificationActivity import com.festago.festago.presentation.ui.tickethistory.TicketHistoryActivity import dagger.hilt.android.AndroidEntryPoint @@ -104,9 +104,9 @@ class MyPageFragment : Fragment(R.layout.fragment_my_page) { binding.srlMyPage.setOnRefreshListener { vm.loadUserInfo() } - // TODO: 학교 선택 화면 변경 필요 + binding.tvSchoolAuthorization.setOnClickListener { - startActivity(StudentVerificationActivity.getIntent(requireContext(), 1L)) + startActivity(SelectSchoolActivity.getIntent(requireContext())) } } diff --git a/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolActivity.kt b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolActivity.kt new file mode 100644 index 000000000..46c0c76a0 --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolActivity.kt @@ -0,0 +1,87 @@ +package com.festago.festago.presentation.ui.selectschool + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.widget.ArrayAdapter +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatActivity +import com.festago.festago.R +import com.festago.festago.databinding.ActivitySelectSchoolBinding +import com.festago.festago.presentation.ui.studentverification.StudentVerificationActivity +import com.festago.festago.presentation.util.repeatOnStarted +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class SelectSchoolActivity : AppCompatActivity() { + + private val binding: ActivitySelectSchoolBinding by lazy { + ActivitySelectSchoolBinding.inflate(layoutInflater) + } + + private val vm: SelectSchoolViewModel by viewModels() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initBinding() + initObserve() + initView() + } + + private fun initBinding() { + setContentView(binding.root) + binding.lifecycleOwner = this + binding.vm = vm + } + + private fun initObserve() { + repeatOnStarted { + vm.uiState.collect { uiState -> + handleUiState(uiState) + } + } + repeatOnStarted { + vm.event.collect { event -> + handleEvent(event) + } + } + } + + private fun initView() { + vm.loadSchools() + } + + private fun handleUiState(uiState: SelectSchoolUiState) { + binding.uiState = uiState + when (uiState) { + is SelectSchoolUiState.Loading, is SelectSchoolUiState.Error -> Unit + is SelectSchoolUiState.Success -> handleSuccess(uiState) + } + } + + private fun handleSuccess(uiState: SelectSchoolUiState.Success) { + val adapter = + ArrayAdapter(this, R.layout.item_select_school, uiState.schools.map { it.name }) + binding.actvSelectSchool.setAdapter(adapter) + binding.actvSelectSchool.setOnItemClickListener { _, _, position, _ -> + val selectedSchool = uiState.schools.firstOrNull { + it.name == adapter.getItem(position) + } + selectedSchool?.let { vm.selectSchool(it.id) } + } + } + + private fun handleEvent(event: SelectSchoolEvent) { + when (event) { + is SelectSchoolEvent.ShowStudentVerification -> { + startActivity(StudentVerificationActivity.getIntent(this, event.schoolId)) + } + } + } + + companion object { + fun getIntent(context: Context): Intent { + return Intent(context, SelectSchoolActivity::class.java) + } + } +} diff --git a/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolEvent.kt b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolEvent.kt new file mode 100644 index 000000000..3464fe3e4 --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolEvent.kt @@ -0,0 +1,5 @@ +package com.festago.festago.presentation.ui.selectschool + +interface SelectSchoolEvent { + class ShowStudentVerification(val schoolId: Long) : SelectSchoolEvent +} diff --git a/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolUiState.kt b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolUiState.kt new file mode 100644 index 000000000..e71f6c70b --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolUiState.kt @@ -0,0 +1,21 @@ +package com.festago.festago.presentation.ui.selectschool + +import com.festago.festago.model.School + +interface SelectSchoolUiState { + object Loading : SelectSchoolUiState + + data class Success( + val schools: List, + val selectedSchoolId: Long? = null + ) : SelectSchoolUiState { + val schoolSelected = selectedSchoolId != null + } + + object Error : SelectSchoolUiState + + val enableNext get() = (this is Success) && schoolSelected + val shouldShowSuccess get() = this is Success + val shouldShowLoading get() = this is Loading + val shouldShowError get() = this is Error +} diff --git a/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolViewModel.kt b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolViewModel.kt new file mode 100644 index 000000000..e49b07764 --- /dev/null +++ b/android/festago/app/src/main/java/com/festago/festago/presentation/ui/selectschool/SelectSchoolViewModel.kt @@ -0,0 +1,71 @@ +package com.festago.festago.presentation.ui.selectschool + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.festago.festago.analytics.AnalyticsHelper +import com.festago.festago.analytics.logNetworkFailure +import com.festago.festago.repository.SchoolRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SelectSchoolViewModel @Inject constructor( + private val schoolRepository: SchoolRepository, + private val analyticsHelper: AnalyticsHelper +) : ViewModel() { + + private val _uiState = MutableStateFlow(SelectSchoolUiState.Loading) + val uiState: StateFlow = _uiState.asStateFlow() + + private val _event = MutableSharedFlow() + val event: SharedFlow = _event.asSharedFlow() + + fun loadSchools() { + viewModelScope.launch { + schoolRepository.loadSchools() + .onSuccess { schools -> + if (uiState.value is SelectSchoolUiState.Success) { + val successState = (uiState.value as SelectSchoolUiState.Success) + _uiState.value = successState.copy(schools = schools) + } else { + _uiState.value = SelectSchoolUiState.Success(schools) + } + } + .onFailure { + _uiState.value = SelectSchoolUiState.Error + analyticsHelper.logNetworkFailure(KEY_LOAD_SCHOOLS_LOG, it.message.toString()) + } + } + } + + fun selectSchool(schoolId: Long) { + if (uiState.value is SelectSchoolUiState.Success) { + _uiState.value = + (uiState.value as SelectSchoolUiState.Success).copy(selectedSchoolId = schoolId) + } + } + + fun showStudentVerification() { + viewModelScope.launch { + if (uiState.value is SelectSchoolUiState.Success) { + val success = uiState.value as SelectSchoolUiState.Success + success.selectedSchoolId?.let { schoolId -> + _event.emit( + SelectSchoolEvent.ShowStudentVerification(schoolId) + ) + } + } + } + } + + companion object { + private const val KEY_LOAD_SCHOOLS_LOG = "load_schools" + } +} diff --git a/android/festago/app/src/main/res/layout/activity_select_school.xml b/android/festago/app/src/main/res/layout/activity_select_school.xml new file mode 100644 index 000000000..41739185b --- /dev/null +++ b/android/festago/app/src/main/res/layout/activity_select_school.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + +