From 4331792b75951443a5e097a3b4a75f2abfea38d0 Mon Sep 17 00:00:00 2001 From: klaus Date: Tue, 31 Dec 2024 07:31:37 +0900 Subject: [PATCH] =?UTF-8?q?=EC=98=A4=EB=94=94=EC=85=98=20=ED=83=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/audition/AuditionApi.kt | 16 ++ .../sodalive/audition/AuditionFragment.kt | 92 +++++++++++ .../sodalive/audition/AuditionListAdapter.kt | 154 ++++++++++++++++++ .../sodalive/audition/AuditionRepository.kt | 20 +++ .../sodalive/audition/AuditionViewModel.kt | 68 ++++++++ .../audition/GetAuditionListResponse.kt | 19 +++ .../java/kr/co/vividnext/sodalive/di/AppDI.kt | 6 + .../vividnext/sodalive/main/MainActivity.kt | 22 +-- .../vividnext/sodalive/main/MainViewModel.kt | 4 +- .../ic_tabbar_audition_normal.png | Bin 0 -> 1693 bytes .../ic_tabbar_audition_selected.png | Bin 0 -> 1844 bytes ...ar_explorer.xml => ic_tabbar_audition.xml} | 4 +- app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/fragment_audition.xml | 31 ++++ .../main/res/layout/item_audition_list.xml | 45 +++++ .../item_audition_list_completed_header.xml | 41 +++++ .../item_audition_list_in_progress_header.xml | 41 +++++ 17 files changed, 549 insertions(+), 16 deletions(-) create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionApi.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionFragment.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionListAdapter.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionRepository.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionViewModel.kt create mode 100644 app/src/main/java/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt create mode 100644 app/src/main/res/drawable-xxhdpi/ic_tabbar_audition_normal.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_tabbar_audition_selected.png rename app/src/main/res/drawable/{ic_tabbar_explorer.xml => ic_tabbar_audition.xml} (60%) create mode 100644 app/src/main/res/layout/fragment_audition.xml create mode 100644 app/src/main/res/layout/item_audition_list.xml create mode 100644 app/src/main/res/layout/item_audition_list_completed_header.xml create mode 100644 app/src/main/res/layout/item_audition_list_in_progress_header.xml diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionApi.kt new file mode 100644 index 0000000..8ec06a8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionApi.kt @@ -0,0 +1,16 @@ +package kr.co.vividnext.sodalive.audition + +import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.common.ApiResponse +import retrofit2.http.GET +import retrofit2.http.Header +import retrofit2.http.Query + +interface AuditionApi { + @GET("/audition") + fun getAuditionList( + @Query("page") page: Int, + @Query("size") size: Int, + @Header("Authorization") authHeader: String + ): Single> +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionFragment.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionFragment.kt new file mode 100644 index 0000000..57bdbea --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionFragment.kt @@ -0,0 +1,92 @@ +package kr.co.vividnext.sodalive.audition + +import android.graphics.Rect +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import kr.co.vividnext.sodalive.base.BaseFragment +import kr.co.vividnext.sodalive.common.LoadingDialog +import kr.co.vividnext.sodalive.databinding.FragmentAuditionBinding +import kr.co.vividnext.sodalive.extensions.dpToPx +import org.koin.android.ext.android.inject + +class AuditionFragment : BaseFragment( + FragmentAuditionBinding::inflate +) { + private val viewModel: AuditionViewModel by inject() + + private lateinit var adapter: AuditionListAdapter + private lateinit var loadingDialog: LoadingDialog + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupView() + bindData() + + viewModel.getAuditionList() + } + + private fun setupView() { + loadingDialog = LoadingDialog(requireActivity(), layoutInflater) + + val recyclerView = binding.rvAudition + adapter = AuditionListAdapter { } + + recyclerView.layoutManager = LinearLayoutManager( + requireContext(), + LinearLayoutManager.VERTICAL, + false + ) + + recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() { + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + super.getItemOffsets(outRect, view, parent, state) + + when (parent.getChildAdapterPosition(view)) { + 0 -> { + outRect.top = 0 + outRect.bottom = 8.3f.dpToPx().toInt() + } + + adapter.itemCount - 1 -> { + outRect.top = 8.3f.dpToPx().toInt() + outRect.bottom = 0 + } + + else -> { + outRect.top = 8.3f.dpToPx().toInt() + outRect.bottom = 8.3f.dpToPx().toInt() + } + } + } + }) + + recyclerView.adapter = adapter + } + + private fun bindData() { + viewModel.toastLiveData.observe(viewLifecycleOwner) { + it?.let { Toast.makeText(requireActivity(), it, Toast.LENGTH_LONG).show() } + } + + viewModel.isLoading.observe(viewLifecycleOwner) { + if (it) { + loadingDialog.show(screenWidth, "") + } else { + loadingDialog.dismiss() + } + } + + viewModel.auditionListLiveData.observe(viewLifecycleOwner) { + adapter.addGroupedList(it) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionListAdapter.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionListAdapter.kt new file mode 100644 index 0000000..317cfed --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionListAdapter.kt @@ -0,0 +1,154 @@ +package kr.co.vividnext.sodalive.audition + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import coil.load +import coil.transform.RoundedCornersTransformation +import kr.co.vividnext.sodalive.R +import kr.co.vividnext.sodalive.databinding.ItemAuditionListBinding +import kr.co.vividnext.sodalive.databinding.ItemAuditionListCompletedHeaderBinding +import kr.co.vividnext.sodalive.databinding.ItemAuditionListInProgressHeaderBinding +import kr.co.vividnext.sodalive.extensions.dpToPx + +class AuditionListAdapter( + private val onItemClick: (GetAuditionListItem) -> Unit +) : ListAdapter(DiffCallback()) { + companion object { + private const val TYPE_IN_PROGRESS_HEADER = 0 + private const val TYPE_COMPLETED_HEADER = 1 + private const val TYPE_ITEM = 2 + } + + sealed class DisplayItem { + data class InProgressHeader(val count: Int) : DisplayItem() + data class CompletedHeader(val count: Int) : DisplayItem() + data class Data(val item: GetAuditionListItem) : DisplayItem() + } + + class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: DisplayItem, newItem: DisplayItem): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: DisplayItem, newItem: DisplayItem): Boolean { + return oldItem == newItem + } + } + + inner class InProgressHeaderViewHolder( + private val binding: ItemAuditionListInProgressHeaderBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") + fun bind(totalCount: Int) { + binding.tvTotalCount.text = "총 ${totalCount}개" + } + } + + inner class CompletedHeaderViewHolder( + private val binding: ItemAuditionListCompletedHeaderBinding + ) : RecyclerView.ViewHolder(binding.root) { + @SuppressLint("SetTextI18n") + fun bind(totalCount: Int) { + binding.tvTotalCount.text = "총 ${totalCount}개" + } + } + + inner class ViewHolder( + private val binding: ItemAuditionListBinding + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(data: DisplayItem.Data) { + binding.tvTitle.text = data.item.title + binding.ivCover.load(data.item.imageUrl) { + crossfade(true) + placeholder(R.drawable.ic_place_holder) + transformations(RoundedCornersTransformation(5f.dpToPx())) + } + + if (data.item.isOff) { + binding.blackCover.visibility = View.VISIBLE + } else { + binding.blackCover.visibility = View.GONE + binding.root.setOnClickListener { onItemClick(data.item) } + } + } + } + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is DisplayItem.InProgressHeader -> TYPE_IN_PROGRESS_HEADER + is DisplayItem.CompletedHeader -> TYPE_COMPLETED_HEADER + is DisplayItem.Data -> TYPE_ITEM + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return when (viewType) { + TYPE_IN_PROGRESS_HEADER -> { + InProgressHeaderViewHolder( + ItemAuditionListInProgressHeaderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + TYPE_COMPLETED_HEADER -> { + CompletedHeaderViewHolder( + ItemAuditionListCompletedHeaderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + TYPE_ITEM -> { + ViewHolder( + ItemAuditionListBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val item = getItem(position)) { + is DisplayItem.InProgressHeader -> { + (holder as InProgressHeaderViewHolder).bind(item.count) + } + + is DisplayItem.CompletedHeader -> (holder as CompletedHeaderViewHolder).bind(item.count) + is DisplayItem.Data -> (holder as ViewHolder).bind(item) + } + } + + private val currentItems = mutableListOf() + + fun addGroupedList(newItem: GetAuditionListResponse) { + val isOnItems = newItem.items.filter { !it.isOff } + val isOffItems = newItem.items.filter { it.isOff } + + if (isOnItems.isNotEmpty() && currentItems.none { it is DisplayItem.InProgressHeader }) { + currentItems.add(DisplayItem.InProgressHeader(newItem.inProgressCount)) + } + currentItems.addAll(isOnItems.map { DisplayItem.Data(it) }) + + if (isOffItems.isNotEmpty() && currentItems.none { it is DisplayItem.CompletedHeader }) { + currentItems.add(DisplayItem.CompletedHeader(newItem.completedCount)) + } + currentItems.addAll(isOffItems.map { DisplayItem.Data(it) }) + + submitList(currentItems.toList()) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionRepository.kt new file mode 100644 index 0000000..43cab77 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionRepository.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.audition + +import io.reactivex.rxjava3.core.Single +import kr.co.vividnext.sodalive.common.ApiResponse + +class AuditionRepository( + private val api: AuditionApi +) { + fun getAuditionList( + page: Int, + size: Int, + token: String + ): Single> { + return api.getAuditionList( + page = page - 1, + size = size, + authHeader = token + ) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionViewModel.kt new file mode 100644 index 0000000..90e1387 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/AuditionViewModel.kt @@ -0,0 +1,68 @@ +package kr.co.vividnext.sodalive.audition + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.orhanobut.logger.Logger +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.schedulers.Schedulers +import kr.co.vividnext.sodalive.base.BaseViewModel +import kr.co.vividnext.sodalive.common.SharedPreferenceManager + +class AuditionViewModel(private val repository: AuditionRepository) : BaseViewModel() { + private val _toastLiveData = MutableLiveData() + val toastLiveData: LiveData + get() = _toastLiveData + + private var _isLoading = MutableLiveData(false) + val isLoading: LiveData + get() = _isLoading + + private val _auditionListLiveData = MutableLiveData() + val auditionListLiveData: LiveData + get() = _auditionListLiveData + + var page = 1 + var isLast = false + private val pageSize = 10 + + fun getAuditionList() { + if (!isLast && !_isLoading.value!!) { + _isLoading.value = true + compositeDisposable.add( + repository.getAuditionList( + page = page, + size = pageSize, + token = "Bearer ${SharedPreferenceManager.token}" + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _isLoading.value = false + if (it.success && it.data != null) { + _auditionListLiveData.value = it.data!! + if (it.data.items.isNotEmpty()) { + page += 1 + } else { + isLast = true + } + } else { + if (it.message != null) { + _toastLiveData.postValue(it.message) + } else { + _toastLiveData.postValue( + "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." + ) + } + } + }, + { + _isLoading.postValue(false) + it.message?.let { message -> Logger.e(message) } + _toastLiveData.postValue("알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.") + } + ) + ) + } + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt b/app/src/main/java/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt new file mode 100644 index 0000000..9d65ab6 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/audition/GetAuditionListResponse.kt @@ -0,0 +1,19 @@ +package kr.co.vividnext.sodalive.audition + +import androidx.annotation.Keep +import com.google.gson.annotations.SerializedName + +@Keep +data class GetAuditionListResponse( + @SerializedName("inProgressCount") val inProgressCount: Int, + @SerializedName("completedCount") val completedCount: Int, + @SerializedName("items") val items: List +) + +@Keep +data class GetAuditionListItem( + @SerializedName("id") val id: Long, + @SerializedName("title") val title: String, + @SerializedName("imageUrl") val imageUrl: String, + @SerializedName("isOff") val isOff: Boolean +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt index 83bc333..8a33a70 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/di/AppDI.kt @@ -39,6 +39,9 @@ import kr.co.vividnext.sodalive.audio_content.series.content.SeriesContentAllVie import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailViewModel import kr.co.vividnext.sodalive.audio_content.upload.AudioContentUploadViewModel import kr.co.vividnext.sodalive.audio_content.upload.theme.AudioContentThemeViewModel +import kr.co.vividnext.sodalive.audition.AuditionApi +import kr.co.vividnext.sodalive.audition.AuditionRepository +import kr.co.vividnext.sodalive.audition.AuditionViewModel import kr.co.vividnext.sodalive.common.ApiBuilder import kr.co.vividnext.sodalive.common.ObjectBox import kr.co.vividnext.sodalive.explorer.ExplorerApi @@ -200,6 +203,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { single { ApiBuilder().build(get(), CreatorCommunityApi::class.java) } single { ApiBuilder().build(get(), CategoryApi::class.java) } single { ApiBuilder().build(get(), PlaylistApi::class.java) } + single { ApiBuilder().build(get(), AuditionApi::class.java) } } private val viewModelModule = module { @@ -279,6 +283,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { viewModel { AudioContentPlaylistCreateViewModel(get()) } viewModel { AudioContentPlaylistModifyViewModel(get()) } viewModel { AudioContentPlayerViewModel() } + viewModel { AuditionViewModel(get()) } } private val repositoryModule = module { @@ -309,6 +314,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) { factory { MenuConfigRepository(get()) } factory { AudioContentPlaylistRepository(get()) } factory { AudioContentGenerateUrlRepository(get()) } + factory { AuditionRepository(get()) } } private val moduleList = listOf( diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt index cb5e4d6..c5bb859 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt @@ -33,13 +33,13 @@ import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity import kr.co.vividnext.sodalive.audio_content.main.AudioContentMainFragment import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerFragment import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService +import kr.co.vividnext.sodalive.audition.AuditionFragment import kr.co.vividnext.sodalive.base.BaseActivity import kr.co.vividnext.sodalive.common.Constants import kr.co.vividnext.sodalive.common.LoadingDialog import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.databinding.ActivityMainBinding import kr.co.vividnext.sodalive.databinding.ItemMainTabBinding -import kr.co.vividnext.sodalive.explorer.ExplorerFragment import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity import kr.co.vividnext.sodalive.extensions.dpToPx import kr.co.vividnext.sodalive.live.LiveFragment @@ -254,7 +254,7 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl if (roomId > 0) { changeFragment(MainViewModel.CurrentTab.LIVE) setTabSelected(binding.tabLive, isSelected = true) - setTabSelected(binding.tabExplorer, isSelected = false) + setTabSelected(binding.tabAudition, isSelected = false) setTabSelected(binding.tabMessage, isSelected = false) setTabSelected(binding.tabMy, isSelected = false) setTabSelected(binding.tabContent, isSelected = false) @@ -281,7 +281,7 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl handler.postDelayed({ changeFragment(MainViewModel.CurrentTab.MESSAGE) setTabSelected(binding.tabLive, isSelected = false) - setTabSelected(binding.tabExplorer, isSelected = false) + setTabSelected(binding.tabAudition, isSelected = false) setTabSelected(binding.tabMessage, isSelected = true) setTabSelected(binding.tabMy, isSelected = false) setTabSelected(binding.tabContent, isSelected = false) @@ -316,14 +316,14 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl ) setupTab( - binding = binding.tabExplorer, - title = "탐색", - imageSrc = R.drawable.ic_tabbar_explorer, + binding = binding.tabAudition, + title = "오디션", + imageSrc = R.drawable.ic_tabbar_audition, colorStateList = ContextCompat.getColorStateList( applicationContext, R.color.color_tabbar_title ), - tab = MainViewModel.CurrentTab.EXPLORER + tab = MainViewModel.CurrentTab.AUDITION ) setupTab( @@ -351,7 +351,7 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl viewModel.currentTab.observe(this) { setTabSelected(binding.tabContent, isSelected = false) setTabSelected(binding.tabLive, isSelected = false) - setTabSelected(binding.tabExplorer, isSelected = false) + setTabSelected(binding.tabAudition, isSelected = false) setTabSelected(binding.tabMessage, isSelected = false) setTabSelected(binding.tabMy, isSelected = false) @@ -365,8 +365,8 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl setTabSelected(binding.tabLive, isSelected = true) } - MainViewModel.CurrentTab.EXPLORER -> { - setTabSelected(binding.tabExplorer, isSelected = true) + MainViewModel.CurrentTab.AUDITION -> { + setTabSelected(binding.tabAudition, isSelected = true) } MainViewModel.CurrentTab.MESSAGE -> { @@ -424,7 +424,7 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl fragment = when (currentTab) { MainViewModel.CurrentTab.LIVE -> liveFragment MainViewModel.CurrentTab.CONTENT -> AudioContentMainFragment() - MainViewModel.CurrentTab.EXPLORER -> ExplorerFragment() + MainViewModel.CurrentTab.AUDITION -> AuditionFragment() MainViewModel.CurrentTab.MESSAGE -> MessageFragment() MainViewModel.CurrentTab.MY -> MyPageFragment() } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt index 9fbafb1..32c9a13 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt @@ -32,8 +32,8 @@ class MainViewModel( @SerializedName("LIVE") LIVE, - @SerializedName("EXPLORER") - EXPLORER, + @SerializedName("AUDITION") + AUDITION, @SerializedName("MESSAGE") MESSAGE, diff --git a/app/src/main/res/drawable-xxhdpi/ic_tabbar_audition_normal.png b/app/src/main/res/drawable-xxhdpi/ic_tabbar_audition_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..be44b2a0da33a8ffde575ce3abf03e0b9c08b706 GIT binary patch literal 1693 zcmV;O24eY%P)+2FCM~)mha^%R7HLk9%YVCHrrfQz6If{rvH+VC8k6Mpu>27efhx{M`Y8*8Tnck7~8r_j%vt z_m~J*@MstPAgIG1exB`)01%;WKEXv zHfhtm#rZc==L}Q$&pDT6=vf|zH@3lB!_Za@hf;VAVGd!2Bb~#9k098d;T0X+zr&F! zWI*uY;h~cSS^5SaUqE;fmgYGiK~px(IVq@4wj*MMskmfeO(!oeFTZhG7as5;3`bSL z+#Os)Lv=&PHBC@rKTeoRLY9dNEe1+y_-MHxJOHw_@SNJ!lX|%ht*iIYZg-qy=|E*x zMiilxZQ%hYVM(SucmPW{y$u%)>dJ`o8-kt+Pb6_r)6>%}@h8~{u_Qd;BusNwuOY~Y zcJ)wYi>W3g^~TbC2W=pOmxg`ja78)q>(0k#eyd<2B3DNwR@R(c+|ct=k*~;#imQU+ zBn;seluMH!TgrLgwrH0XkR}BLDN_@tgTmk*YJ`T`&d$zqAbTPLPLtNt$f*B?c|Za% zB^CCp%gY<1{4?}CYhv2<-@*YWVJ)-zZD^wYe`k7!KFudRRf-3|>|!Sc5;bylZlLz9tbQ(rJs62V;Gpo0WrZD zhb5t~(|q$E9kpM=G!VUQUT z4%(@x*G%!WbhC1Pe!eGCfJq7t>L40Kw_y=pDBc~>~j-Nc)*J=<|!SE>a9=oHgrDvz);i7lzjGWsWFy+g|=A1uWHh+W&thV5|4Kg z42b+0Y%$mwDX=i-l?TD8Feldp_!)1Tw&gIj=Ck$h(gc@O1H-HFN+0wY)0F< zh=JH!Ep#eEnP;xg43r(grHDm>bEnZz8L`&IqmXEC310J772fCD3ZmI;7U$>Z2iXx9 z71(R)yS7tJWMy5vE@C3l?je(Q5NvB2>oZ++eWnlxYL!I2pOh?-cz@VJI#|qORkI)G z!xSBoXpIA?d+#)4iBw+zkTha6H88TMAaTZJ2a z&19}S*jFNOkIBgbj*GazWm`}4c^r1rZlneBh>Rdj{K&UH>QY9 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_tabbar_audition_selected.png b/app/src/main/res/drawable-xxhdpi/ic_tabbar_audition_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..0c15f1cc408ae99ffc1aa0e5e52ae6b48c697ca7 GIT binary patch literal 1844 zcmV-42g~@0P)7;5h;2 z1a?AACY4zI=CvfpExF}x*)mmB`P$^_H zRuAC^qCX)YApGqCwEtc$w_!p&I{5w)LDuofHD|D4W21cYEeK(}R{eV%a@W8IF?nmZ3ROudauHmW)Sl%8JR_Ff~=+R#g)rWDw?`LBG_ zTrYQB`upu&r$zwJN#NbNbJ{Sqn}lpseN2S9D#T$*Dj`u`fbCDKz)rn^3OX23Kt_=AqR0!~O-dT()~rWWzBbC$4UZ z-89)z&R*%I^!+82a_tJB>VdMp{k{MeKb&@n;MK{>-MXoLkz^He5q|Wp^Bj_HCzEXG zrabR85!IM%cBcgGxpQe$gQCesT{yy|x4exXm=+AI?~9f^R1)kKMbD52l#Fp01Ny1hu z&D*)A6}xGDbkT1eDi8@gabFNB>G00cEP77YBO!Pz;Cg-~wfMH=f!!?;-ea-G6QbdX z2=D12c7kr!pHtN{L!DCYT|+KebI~Q#5*Ba<7pQi9&{qGl77z-)zNj` z5zarLV1rDO=I}Gq4GBmPzT%i!FQ-)nJ?|@6U?nW292g>^>QDs)eX=FxXHESmDf1zJ zfkV)BUZib_u*GNTg;C)rqL&D0KBpEYz|jk0g53{Cg!%)Ag!JI!=1thUx`ds6nLO_$ z|1`6@`?ykW@J|CQ`2C=>CP8y!hLDyQWJ!+!K05>~b{(eV=IbKMP21r|TgO7KB~q5jem~SZ&3G;ey1;DVhvGUANYJ)v}YdwOtVAx^?{| z*h~A|=(MCASkT)C2e`QE>1jBbUQz-i#C~2$E6H-8ahOW%w@uOoAzo^w?v3-{ zEjYl5FdxNsbqO+=*XcUPPeS99SQsT~6)Cyg+bW5v^lu{kR3Rs)e@)U&?SMw1DV}(c zI%=yb*`54lu&|2$+b_<76MDY@nHA~Ug{UQj-tzxwpJ?5sM@p=D@|lX_Z068om@%5JN!R-J4i zWg0A1&S7tJ>eiV4RmlQKB^&(z*kI-wA}1E#pB(}wj(OIbhv-I7EMe9m!r`XTlo8F! zi;qJ7yCvS*mzTqLOSW$-!nW~e5dE?`RLoyM*Jf}`WL4fQPbz89ZVY5xmGY!<%l4T< z9`v&$>BPafWTUoYBI|xweWoB~@M4m$bHW?SU;Ir}HyA1FJqg*U{RD__Bvgbmbf{3o zy&!n=@k!YlH|zZg-+yKz8g;4?`;*;2Peiznk!~mqSg=Jwa1=pgxkrDNiWi(=@f+Mb0yqC#5E`i%1s6AXIIDH!oyt_jJ_M - - + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b2f5dda..b2ae649 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -177,7 +177,7 @@ android:layout_weight="1" /> + + + + + + diff --git a/app/src/main/res/layout/item_audition_list.xml b/app/src/main/res/layout/item_audition_list.xml new file mode 100644 index 0000000..9b50e4a --- /dev/null +++ b/app/src/main/res/layout/item_audition_list.xml @@ -0,0 +1,45 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/item_audition_list_completed_header.xml b/app/src/main/res/layout/item_audition_list_completed_header.xml new file mode 100644 index 0000000..99b7494 --- /dev/null +++ b/app/src/main/res/layout/item_audition_list_completed_header.xml @@ -0,0 +1,41 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/item_audition_list_in_progress_header.xml b/app/src/main/res/layout/item_audition_list_in_progress_header.xml new file mode 100644 index 0000000..84331ce --- /dev/null +++ b/app/src/main/res/layout/item_audition_list_in_progress_header.xml @@ -0,0 +1,41 @@ + + + + + + + + +