-
Notifications
You must be signed in to change notification settings - Fork 0
4.4.0 구조 설계
JI YOON LEE edited this page Mar 1, 2023
·
1 revision
📦 com.ssafy.smile
┣ 📂 data
┃ ┗ 📂 local
┃ ┃ ┗ 📂 datasource
┃ ┃ ┗ 📂 repository
┃ ┗ 📂 remote
┃ ┃ ┗ 📂 model
┃ ┃ ┗ 📂 datasource
┃ ┃ ┗ 📂 repository
┃ ┃ ┗ 📂 datasource
┃ ┃ ┗ 📂 service
┣ 📂 domain
┃ ┗ 📂 model
┃ ┗ 📂 repository
┣ 📂 presentation
📦 com.ssafy.smile
┣ 📂 base
┃ ┗ 📜 BaseRepository.kt
┃ ┗ 📜 BaseViewModel.kt
┃ ┗ 📜 BaseView.kt
┃ ┗ 📜 BaseViewImpl.kt
┃ ┗ 📜 BaseFragment.kt
┃ ┗ 📜 BaseBottomSheetDialogFragment.kt
구현 코드 |
---|
- BaseRepository.kt
open class BaseRepository {
suspend fun <T : Any> safeApiCall(liveData: SingleLiveData<NetworkResponse<T>>, action: suspend () -> Response<T>){
liveData.postValue(NetworkResponse.Loading())
val result : NetworkResponse<T> = safeApiResult(action=action)
liveData.postValue(result)
}
private suspend fun <T : Any> safeApiResult(action: suspend () -> Response<T>): NetworkResponse<T> {
val response = action.invoke()
Log.d(TAG, "safeApiResult: $response")
if (response.isSuccessful && response.body()!=null) return NetworkResponse.Success(response.body()!!)
return NetworkResponse.Failure(response.code())
}
}
- BaseViewModel.kt
abstract class BaseViewModel : ViewModel() {
private val _onBackPressed = SingleLiveData<Any>(null)
val onBackPressed: SingleLiveData<Any> get() = _onBackPressed
private var mBackPressedAt = 0L
private val _error = SingleLiveData<Event<String>>(null)
val error: SingleLiveData<Event<String>> = _error
fun onBackPressed() {
if (mBackPressedAt + TimeUnit.SECONDS.toMillis(2) > System.currentTimeMillis()) {
_onBackPressed.postValue(true)
} else {
_onBackPressed.postValue("\'뒤로\' 버튼을 한번 더 누르시면 종료됩니다.")
mBackPressedAt = System.currentTimeMillis()
}
}
fun handleError(exception: Throwable) {
val message = exception.message ?: ""
_error.value = Event(message)
}
fun makeMultiPartBody(jsonKey: String, file: File) : MultipartBody.Part {
val requestBody = file.convertToRequestBody()
return MultipartBody.Part.createFormData(jsonKey, file.name, requestBody)
}
fun makeMultiPartBodyList(jsonKey: String, images: List<File>): List<MultipartBody.Part> {
val imageList = arrayListOf<MultipartBody.Part>()
for (i in images.indices) {
val multipartBody = makeMultiPartBody(jsonKey, images[i])
imageList.add(multipartBody)
}
return imageList
}
}
- BaseView.kt
interface BaseView {
fun Toolbar.initToolbar(toolbarTitle:String, isBackAvailable:Boolean?=false, backAction : (() -> Unit)?=null )
fun showLoadingDialog(context: Context){}
fun dismissLoadingDialog(){}
fun showDialog(dialog: Dialog, lifecycleOwner: LifecycleOwner?, cancelable: Boolean = true, dismissHandler: (() -> Unit)? = null){}
fun showToast(context: Context, message: String, type : Types.ToastType?=null, iconEnable : Boolean=true)
}
- BaseViewImpl.kt
interface BaseViewImpl : BaseView {
var mLoadingDialog: LoadingDialog
override fun Toolbar.initToolbar(toolbarTitle:String, isBackAvailable:Boolean?, backAction : (() -> Unit)?){
this.title = toolbarTitle
if (isBackAvailable==true) this.setNavigationIcon(R.drawable.ic_back)
this.setNavigationOnClickListener {
if (backAction != null) { backAction() }
}
}
override fun showLoadingDialog(context: Context) {
mLoadingDialog = LoadingDialog(context)
mLoadingDialog.show()
}
override fun dismissLoadingDialog() {
if (mLoadingDialog.isShowing) {
mLoadingDialog.dismiss()
}
}
override fun showDialog(dialog: Dialog, lifecycleOwner: LifecycleOwner?, cancelable: Boolean, dismissHandler: (() -> Unit)?) {
val targetEvent = if (cancelable) Lifecycle.Event.ON_STOP else Lifecycle.Event.ON_DESTROY
val observer = LifecycleEventObserver { _: LifecycleOwner, event: Lifecycle.Event ->
if (event == targetEvent && dialog.isShowing) {
dialog.dismiss()
dismissHandler?.invoke()
}
}
dialog.show()
lifecycleOwner?.lifecycle?.addObserver(observer)
dialog.setOnDismissListener { lifecycleOwner?.lifecycle?.removeObserver(observer) }
}
override fun showToast(context: Context, message: String, type : Types.ToastType?, iconEnable : Boolean) {
when (type){
Types.ToastType.CUSTOM -> Toasty.warning(context, message, Toast.LENGTH_SHORT).show()
Types.ToastType.INFO -> Toasty.info(context, message, Toast.LENGTH_SHORT, iconEnable).show()
Types.ToastType.ERROR -> Toasty.error(context, message, Toast.LENGTH_SHORT, iconEnable).show()
Types.ToastType.SUCCESS -> Toasty.success(context, message, Toast.LENGTH_SHORT, iconEnable).show()
Types.ToastType.WARNING -> Toasty.warning(context, message, Toast.LENGTH_SHORT, iconEnable).show()
else -> Toasty.normal(context, message, Toast.LENGTH_SHORT).show()
}
}
}
- BaseFragment.kt
abstract class BaseFragment<B : ViewBinding>(private val bind: (View) -> B, @LayoutRes layoutResId: Int) : Fragment(layoutResId), BaseViewImpl {
override lateinit var mLoadingDialog: LoadingDialog
lateinit var mainActivity : MainActivity
private var _binding: B? = null
val binding get() = _binding?: throw IllegalStateException("binding fail")
override fun onAttach(context: Context) {
super.onAttach(context)
mLoadingDialog = LoadingDialog(requireContext())
mainActivity = context as MainActivity
mLoadingDialog = LoadingDialog(context)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = bind(super.onCreateView(inflater, container, savedInstanceState)!!)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
setEvent()
}
abstract fun initView()
abstract fun setEvent()
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
}
- BaseBottomSheetDialogFragment.kt
abstract class BaseBottomSheetDialogFragment<B : ViewBinding>(private val bindingInflater: (layoutInflater:LayoutInflater) -> B)
: BottomSheetDialogFragment(), BaseViewImpl {
override lateinit var mLoadingDialog: LoadingDialog
private var _binding: B? = null
val binding get() = _binding?: throw IllegalStateException("binding fail")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mLoadingDialog = LoadingDialog(requireContext())
setStyle(DialogFragment.STYLE_NORMAL, R.style.CustomBottomSheetDialogTheme)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = bindingInflater.invoke(inflater)
return _binding?.root
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
dialog.setOnShowListener {
val bottomSheet = dialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
setupRatio(bottomSheet!!)
}
return dialog
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
setEvent()
}
abstract fun initView()
abstract fun setEvent()
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
fun setupRatio(view : View, ratio:Int=30){
val layoutParams = view.layoutParams
layoutParams.height = getBottomSheetDialogDefaultHeight(ratio)
view.layoutParams = layoutParams
}
private fun getBottomSheetDialogDefaultHeight(ratio:Int): Int { return getWindowHeight() * ratio / 100 }
private fun getWindowHeight(): Int {
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
return displayMetrics.heightPixels
}
}