diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9c741d0e..fa0bb8bb 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -10,6 +10,6 @@ |스크린샷| |:--:| |파일첨부바람| - + ## 📮 관련 이슈 - Resolved: #이슈번호 diff --git a/README.md b/README.md index f5aec8a3..efe3ec5c 100644 --- a/README.md +++ b/README.md @@ -1 +1,68 @@ -# Android +# TOASTER-AOS + + +더 이상 링크를 태우지 마세요. 토스트 먹듯이 간단하게! +유저가 링크를 산재된 플랫폼에 저장함으로 인해 발생하는 모든 불편함을 토스터에서 해소해줄 수 있어요. + +- 33기 DO SOPT APP-JAM (2023.12.17 ~ ) +- Development Environment : `Hedgehog | 2023.1.1` + +
+ +## AOS Developer + +| [상욱](operawook@catholic.ac.kr) | [이삭](lsls4868@gmail.com) | [채은](parkchangel@naver.com) | [민영](codingmy@naver.com) | +| :--: | :--: | :--: | :--: | +| 상욱 | 이삭 | 채은 | 민영 | +|

`타이머페이지` |

`메인페이지` `링크저장` |

`로그인, 검색` `마이페이지` |

`카테고리페이지` | + +
+ +## 💻 Code Convention +[NOTION Code Convetion Link](https://hill-agenda-2b0.notion.site/Code-Convention-f492a5bdf5b444a6aae561e53d9d4e10) +
+ +## 🔖 Branch Strategy +[NOTION Branch Strategy](https://hill-agenda-2b0.notion.site/Branch-Strategy-e3a9c5e70f6241ae9ccad544666b095c?pvs=4) +
+ +## 🎁 Git Convention +[NOTION Git Convention](https://hill-agenda-2b0.notion.site/Git-Convention-064dee5df78e4b0c9dd59d18c775a460?pvs=4) +
+ +## 💻 Kanban Board +[GIT Kanban Board](https://github.com/orgs/Link-MIND/projects/1/views/1) +
+ +## 📜 Project Plan +[Project Plan](https://hill-agenda-2b0.notion.site/7a635a2c014c470899899073be2ff49f?v=4de94ec87af045d8ba9a69afa39511af) + +### 📂 Module +```bash +├── LinkMind-Android +├── 📁 app +├── 📁 build-logic +├── 📁 Convention +├── 📁 core +│ ├── 🗂️ auth +│ ├── 🗂️ authimpl +│ ├── 🗂️ common +│ ├── 🗂️ datastore +│ ├── 🗂️ model +│ ├── 🗂️ network +│ ├── 🗂️ ui +├── 📁 data +│ ├── 🗂️ linkminddata +│ ├── 🗂️ oauthdata +├── 📁 data-local +│ ├── 🗂️ linkminddata-local +├── 📁 data- remote +│ ├── 🗂️ linkminddata-remote +├── 📁 domain +│ ├── 🗂️ linkminddomain +│ ├── 🗂️ oauthdomain +├── 📁 feature +│ ├── 🗂️ mainfeature +``` + + diff --git a/core/network/src/main/java/org/sopt/network/authenticator/LinkMindAuthenticator.kt b/core/network/src/main/java/org/sopt/network/authenticator/LinkMindAuthenticator.kt index ac87d61e..f90ded7e 100644 --- a/core/network/src/main/java/org/sopt/network/authenticator/LinkMindAuthenticator.kt +++ b/core/network/src/main/java/org/sopt/network/authenticator/LinkMindAuthenticator.kt @@ -3,6 +3,7 @@ package org.sopt.network.authenticator import android.content.Context import com.jakewharton.processphoenix.ProcessPhoenix import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import okhttp3.Authenticator import okhttp3.Request @@ -23,7 +24,7 @@ class LinkMindAuthenticator @Inject constructor( if (response.code == CODE_TOKEN_EXPIRED) { val newTokens = runCatching { runBlocking { - tokenRefreshService.postAuthRefresh() + tokenRefreshService.postAuthRefresh(dataStore.flowRefreshToken().first()) } }.onSuccess { runBlocking { diff --git a/core/network/src/main/java/org/sopt/network/interceptor/AuthenticationIntercept.kt b/core/network/src/main/java/org/sopt/network/interceptor/AuthenticationIntercept.kt index 228d4534..8bcdac37 100644 --- a/core/network/src/main/java/org/sopt/network/interceptor/AuthenticationIntercept.kt +++ b/core/network/src/main/java/org/sopt/network/interceptor/AuthenticationIntercept.kt @@ -25,8 +25,7 @@ class AuthenticationIntercept @Inject constructor( runBlocking { dataStore.setAutoLogin(false) } ProcessPhoenix.triggerRebirth(context, intentProvider.getAuthIntent()) } - response.close() - return chain.proceed(authRequest) + return response } private fun handleRequest(originalRequest: Request) = diff --git a/core/network/src/main/java/org/sopt/network/service/TokenRefreshService.kt b/core/network/src/main/java/org/sopt/network/service/TokenRefreshService.kt index 60a05c10..caba2bad 100644 --- a/core/network/src/main/java/org/sopt/network/service/TokenRefreshService.kt +++ b/core/network/src/main/java/org/sopt/network/service/TokenRefreshService.kt @@ -2,9 +2,12 @@ package org.sopt.network.service import org.sopt.network.model.response.ResponsePostAuthRefreshDto import org.sopt.network.model.response.base.BaseResponse +import retrofit2.http.Header import retrofit2.http.POST interface TokenRefreshService { @POST("auth/token") - suspend fun postAuthRefresh(): BaseResponse + suspend fun postAuthRefresh( + @Header("refreshToken") refreshToken: String, + ): BaseResponse } diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 02fad677..72367584 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -9,5 +9,6 @@ android { buildFeatures { dataBinding = true + viewBinding = true } } diff --git a/core/ui/src/main/java/org/sopt/ui/base/BindingActivity.kt b/core/ui/src/main/java/org/sopt/ui/base/BindingActivity.kt index 1f1dbb74..2be4df52 100644 --- a/core/ui/src/main/java/org/sopt/ui/base/BindingActivity.kt +++ b/core/ui/src/main/java/org/sopt/ui/base/BindingActivity.kt @@ -1,23 +1,22 @@ package org.sopt.ui.base import android.os.Bundle +import android.view.LayoutInflater import android.view.MotionEvent import android.view.View -import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding +import androidx.viewbinding.ViewBinding import org.sopt.ui.context.hideKeyboard -abstract class BindingActivity( - @LayoutRes private val layoutRes: Int, +abstract class BindingActivity( + private val inflater: (LayoutInflater) -> T, ) : AppCompatActivity() { protected lateinit var binding: T override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = DataBindingUtil.setContentView(this, layoutRes) - binding.lifecycleOwner = this + binding = inflater(layoutInflater) + setContentView(binding.root) } override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { diff --git a/core/ui/src/main/java/org/sopt/ui/base/BindingBottomSheetDialogFragment.kt b/core/ui/src/main/java/org/sopt/ui/base/BindingBottomSheetDialogFragment.kt new file mode 100644 index 00000000..27aecf9d --- /dev/null +++ b/core/ui/src/main/java/org/sopt/ui/base/BindingBottomSheetDialogFragment.kt @@ -0,0 +1,44 @@ +package org.sopt.ui.base + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.viewbinding.ViewBinding +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +abstract class BindingBottomSheetDialogFragment( + private val inflater: (LayoutInflater) -> T, +) : BottomSheetDialogFragment() { + private var _binding: T? = null + protected val binding get() = requireNotNull(_binding) { { "binding object is not initialized" } } + + override fun onStart() { + super.onStart() + dialog?.window?.apply { + setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT, + ) + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + _binding = inflater(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + override fun onDestroyView() { + _binding = null + super.onDestroyView() + } +} diff --git a/core/ui/src/main/java/org/sopt/ui/base/BindingDialogFragment.kt b/core/ui/src/main/java/org/sopt/ui/base/BindingDialogFragment.kt index 07630999..b7180671 100644 --- a/core/ui/src/main/java/org/sopt/ui/base/BindingDialogFragment.kt +++ b/core/ui/src/main/java/org/sopt/ui/base/BindingDialogFragment.kt @@ -5,13 +5,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.WindowManager -import androidx.annotation.LayoutRes -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding import androidx.fragment.app.DialogFragment +import androidx.viewbinding.ViewBinding -abstract class BindingDialogFragment( - @LayoutRes val layoutRes: Int, +abstract class BindingDialogFragment( + private val inflater: (LayoutInflater) -> T, ) : DialogFragment() { private var _binding: T? = null protected val binding get() = requireNotNull(_binding) { { "binding object is not initialized" } } @@ -31,13 +29,12 @@ abstract class BindingDialogFragment( container: ViewGroup?, savedInstanceState: Bundle?, ): View { - _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) + _binding = inflater(layoutInflater) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.lifecycleOwner = viewLifecycleOwner } override fun onDestroyView() { diff --git a/core/ui/src/main/java/org/sopt/ui/base/BindingFragment.kt b/core/ui/src/main/java/org/sopt/ui/base/BindingFragment.kt index 812252c3..0d4a5ac9 100644 --- a/core/ui/src/main/java/org/sopt/ui/base/BindingFragment.kt +++ b/core/ui/src/main/java/org/sopt/ui/base/BindingFragment.kt @@ -4,25 +4,23 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.LayoutRes -import androidx.databinding.DataBindingUtil -import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment +import androidx.viewbinding.ViewBinding -abstract class BindingFragment( - @LayoutRes private val layoutRes: Int, +abstract class BindingFragment( + private val inflater: (LayoutInflater) -> T, ) : Fragment() { private var _binding: T? = null - protected val binding get() = requireNotNull(_binding) { - } + protected val binding + get() = requireNotNull(_binding) { + } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { - _binding = DataBindingUtil.inflate(inflater, layoutRes, container, false) - binding.lifecycleOwner = viewLifecycleOwner + _binding = inflater(layoutInflater) return binding.root } diff --git a/feature/mainfeature/src/main/AndroidManifest.xml b/feature/mainfeature/src/main/AndroidManifest.xml index 241ed066..55c534aa 100644 --- a/feature/mainfeature/src/main/AndroidManifest.xml +++ b/feature/mainfeature/src/main/AndroidManifest.xml @@ -1,38 +1,40 @@ + xmlns:tools="http://schemas.android.com/tools"> - + - - - - + + + + + + + - - - - - - - - + + + + diff --git a/feature/mainfeature/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt b/feature/mainfeature/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt new file mode 100644 index 00000000..0023a237 --- /dev/null +++ b/feature/mainfeature/src/main/java/designsystem/components/bottomsheet/LinkMindBottomSheet.kt @@ -0,0 +1,72 @@ +package designsystem.components.bottomsheet + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import androidx.annotation.StringRes +import com.google.android.material.bottomsheet.BottomSheetDialog +import designsystem.components.button.state.LinkMIndFullWidthButtonState +import org.sopt.mainfeature.databinding.BottomSheetDialogLinkmindBinding +import org.sopt.ui.view.onThrottleClick + +class LinkMindBottomSheet(context: Context) { + private val binding: BottomSheetDialogLinkmindBinding = BottomSheetDialogLinkmindBinding.inflate(LayoutInflater.from(context)) + private val bottomSheetDialog: BottomSheetDialog = BottomSheetDialog(context).apply { + setContentView(binding.root) + } + + init { + binding.ivBottomSheetClose.onThrottleClick { dismiss() } + + binding.btnBottomSheet.state = LinkMIndFullWidthButtonState.DISABLE + + bottomSheetDialog.window?.let { window -> + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) + binding.etvBottomSheet.editText.requestFocus() + } + + binding.etvBottomSheet.apply { + throttleAfterTextChanged { + handleTextChange() + } + + onClickTextClear { + binding.btnBottomSheet.state = LinkMIndFullWidthButtonState.DISABLE + } + } + } + + fun bottomSheetConfirmBtnClick(onClick: () -> Unit) { + binding.btnBottomSheet.btnClick { + onClick() + } + } + + private fun handleTextChange() { + binding.tvBottomSheetErrorText.visibility = if (showErrorMsg()) View.VISIBLE else View.GONE + binding.btnBottomSheet.state = + if (!showErrorMsg() && isTextLongEnough()) LinkMIndFullWidthButtonState.ENABLE_PRIMARY else LinkMIndFullWidthButtonState.DISABLE + } + + private fun isTextLongEnough() = binding.etvBottomSheet.editText.text.length > 1 + + fun setTitle(@StringRes textId: Int) { + binding.tvBottomSheetTitle.setText(textId) + } + + fun showErrorMsg(): Boolean = binding.etvBottomSheet.editText.text.length > 10 + + fun setErroMsg(@StringRes textId: Int) { + binding.tvBottomSheetErrorText.setText(textId) + } + + fun show() { + bottomSheetDialog.show() + } + + fun dismiss() { + bottomSheetDialog.dismiss() + } +} diff --git a/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindBlockButton.kt b/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindBlockButton.kt index c353cb5c..a3321f6a 100644 --- a/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindBlockButton.kt +++ b/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindBlockButton.kt @@ -6,7 +6,7 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import androidx.core.content.ContextCompat -import designsystem.components.button.state.LinkMindButtonFullWidthState +import designsystem.components.button.state.LinkMindButtonState import org.sopt.mainfeature.R import org.sopt.mainfeature.databinding.ButtonBlockLinkmindBinding import org.sopt.ui.view.onThrottleClick @@ -20,33 +20,35 @@ class LinkMindBlockButton @JvmOverloads constructor( private val binding: ButtonBlockLinkmindBinding - var state: LinkMindButtonFullWidthState = LinkMindButtonFullWidthState.ENABLE + var state: LinkMindButtonState = LinkMindButtonState.ENABLE set(value) { field = value when (field) { - LinkMindButtonFullWidthState.ENABLE -> { - setBtnEnable(R.color.black) + LinkMindButtonState.ENABLE -> { + setBtnEnable(R.drawable.shape_neutrals850_fill_12_rect) } - LinkMindButtonFullWidthState.DISABLE -> { - setBtnDisable(R.color.black) + LinkMindButtonState.DISABLE -> { + setBtnDisable(R.drawable.shape_neutrals200_fill_12_rect) } } } - private fun setBtnEnable(textColorResId: Int) { + private fun setBtnEnable(drawableResId: Int) { binding.apply { clBtnMediumWidthLinkmind.isClickable = true clBtnMediumWidthLinkmind.isFocusable = true - tvBtn.setTextColor(ContextCompat.getColor(context, textColorResId)) + tvBtn.setTextColor(ContextCompat.getColor(context, R.color.white)) + clBtnMediumWidthLinkmind.setBackgroundResource(drawableResId) } } - private fun setBtnDisable(textColorResId: Int) { + private fun setBtnDisable(drawableResId: Int) { binding.apply { clBtnMediumWidthLinkmind.isClickable = false clBtnMediumWidthLinkmind.isFocusable = false - clBtnMediumWidthLinkmind.setBackgroundColor(ContextCompat.getColor(context, textColorResId)) + tvBtn.setTextColor(ContextCompat.getColor(context, R.color.white)) + clBtnMediumWidthLinkmind.setBackgroundResource(drawableResId) } } diff --git a/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindFullWidthButton.kt b/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindFullWidthButton.kt index b27a2d3a..8e2d0637 100644 --- a/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindFullWidthButton.kt +++ b/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindFullWidthButton.kt @@ -6,7 +6,7 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import androidx.core.content.ContextCompat -import designsystem.components.button.state.LinkMindButtonFullWidthState +import designsystem.components.button.state.LinkMIndFullWidthButtonState import org.sopt.mainfeature.R import org.sopt.mainfeature.databinding.ButtonFullWidthLinkmindBinding import org.sopt.ui.view.onThrottleClick @@ -20,31 +20,37 @@ class LinkMindFullWidthButton @JvmOverloads constructor( private val binding: ButtonFullWidthLinkmindBinding - var state: LinkMindButtonFullWidthState = LinkMindButtonFullWidthState.ENABLE + var state: LinkMIndFullWidthButtonState = LinkMIndFullWidthButtonState.ENABLE_PRIMARY set(value) { field = value when (field) { - LinkMindButtonFullWidthState.ENABLE -> { - setBtnEnable(R.color.black) + LinkMIndFullWidthButtonState.ENABLE_PRIMARY -> { + setBtnEnable(R.color.primary) } - LinkMindButtonFullWidthState.DISABLE -> { - setBtnDisable(R.color.black) + LinkMIndFullWidthButtonState.ENABLE_BLACK -> { + setBtnEnable(R.color.neutrals_black) + } + + LinkMIndFullWidthButtonState.DISABLE -> { + setBtnDisable(R.color.neutrals100) } } } private fun setBtnEnable(textColorResId: Int) { binding.apply { - clBtnFullWidthLinkmind.isClickable = false - clBtnFullWidthLinkmind.isFocusable = false - tvBtn.setTextColor(ContextCompat.getColor(context, textColorResId)) + clBtnFullWidthLinkmind.isClickable = true + clBtnFullWidthLinkmind.isFocusable = true + tvBtn.setTextColor(ContextCompat.getColor(context, R.color.neutrals_white)) + clBtnFullWidthLinkmind.setBackgroundColor(ContextCompat.getColor(context, textColorResId)) } } private fun setBtnDisable(textColorResId: Int) { binding.apply { - clBtnFullWidthLinkmind.isClickable = true - clBtnFullWidthLinkmind.isFocusable = true + clBtnFullWidthLinkmind.isClickable = false + clBtnFullWidthLinkmind.isFocusable = false + tvBtn.setTextColor(ContextCompat.getColor(context, R.color.neutrals_white)) clBtnFullWidthLinkmind.setBackgroundColor(ContextCompat.getColor(context, textColorResId)) } } diff --git a/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindPopUpButton.kt b/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindPopUpButton.kt index efd7db1c..a6226044 100644 --- a/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindPopUpButton.kt +++ b/feature/mainfeature/src/main/java/designsystem/components/button/LinkMindPopUpButton.kt @@ -6,7 +6,7 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import androidx.core.content.ContextCompat -import designsystem.components.button.state.LinkMindButtonFullWidthState +import designsystem.components.button.state.LinkMindButtonState import org.sopt.mainfeature.R import org.sopt.mainfeature.databinding.ButtonPopUpLinkmindBinding import org.sopt.ui.view.onThrottleClick @@ -20,15 +20,15 @@ class LinkMindPopUpButton @JvmOverloads constructor( private val binding: ButtonPopUpLinkmindBinding - var state: LinkMindButtonFullWidthState = LinkMindButtonFullWidthState.ENABLE + var state: LinkMindButtonState = LinkMindButtonState.ENABLE set(value) { field = value when (field) { - LinkMindButtonFullWidthState.ENABLE -> { + LinkMindButtonState.ENABLE -> { setBtnEnable() } - LinkMindButtonFullWidthState.DISABLE -> { + LinkMindButtonState.DISABLE -> { setBtnDisable() } } @@ -38,7 +38,8 @@ class LinkMindPopUpButton @JvmOverloads constructor( binding.apply { clBtnHalfWidthLinkmind.isClickable = true clBtnHalfWidthLinkmind.isFocusable = true - tvBtn.setTextColor(ContextCompat.getColor(context, R.color.black)) + tvBtn.setTextColor(ContextCompat.getColor(context, R.color.white)) + clBtnHalfWidthLinkmind.setBackgroundResource(R.drawable.shape_primary_fill_8_rect) } } @@ -46,8 +47,8 @@ class LinkMindPopUpButton @JvmOverloads constructor( binding.apply { clBtnHalfWidthLinkmind.isClickable = false clBtnHalfWidthLinkmind.isFocusable = false - tvBtn.setTextColor(ContextCompat.getColor(context, R.color.black)) - clBtnHalfWidthLinkmind.setBackgroundColor(ContextCompat.getColor(context, R.color.black)) + tvBtn.setTextColor(ContextCompat.getColor(context, R.color.neutrals400)) + clBtnHalfWidthLinkmind.setBackgroundResource(R.drawable.shape_neutrals_fill_8_rect) } } diff --git a/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMIndFullWidthButtonState.kt b/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMIndFullWidthButtonState.kt new file mode 100644 index 00000000..6f128712 --- /dev/null +++ b/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMIndFullWidthButtonState.kt @@ -0,0 +1,4 @@ +package designsystem.components.button.state +enum class LinkMIndFullWidthButtonState { + ENABLE_PRIMARY, ENABLE_BLACK, DISABLE +} diff --git a/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMindButtonFullWidthState.kt b/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMindButtonState.kt similarity index 61% rename from feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMindButtonFullWidthState.kt rename to feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMindButtonState.kt index b092a11d..77aca73e 100644 --- a/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMindButtonFullWidthState.kt +++ b/feature/mainfeature/src/main/java/designsystem/components/button/state/LinkMindButtonState.kt @@ -1,5 +1,5 @@ package designsystem.components.button.state -enum class LinkMindButtonFullWidthState { +enum class LinkMindButtonState { ENABLE, DISABLE } diff --git a/feature/mainfeature/src/main/java/designsystem/components/dialog/LinkMindDialog.kt b/feature/mainfeature/src/main/java/designsystem/components/dialog/LinkMindDialog.kt index bd46ba6a..838a76cf 100644 --- a/feature/mainfeature/src/main/java/designsystem/components/dialog/LinkMindDialog.kt +++ b/feature/mainfeature/src/main/java/designsystem/components/dialog/LinkMindDialog.kt @@ -10,8 +10,10 @@ import android.view.Window import android.view.WindowManager import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog +import designsystem.components.button.state.LinkMindButtonState import org.sopt.mainfeature.databinding.DialogLinkmindBinding import org.sopt.ui.view.caculateMarignDialog +import org.sopt.ui.view.onThrottleClick class LinkMindDialog constructor(private val context: Context) { @@ -63,11 +65,17 @@ class LinkMindDialog constructor(private val context: Context) { return this } + fun setCloseBtn() = + binding.ivDialogCancel.onThrottleClick { + dismiss() + } + fun setPositiveButton( @StringRes text: Int, onClickListener: (view: View) -> (Unit), ): LinkMindDialog { binding.btnPositive.apply { + state = LinkMindButtonState.ENABLE setText(context.getText(text).toString()) setOnClickListener(onClickListener) dismiss() @@ -80,6 +88,7 @@ class LinkMindDialog constructor(private val context: Context) { onClickListener: (view: View) -> (Unit) = {}, ): LinkMindDialog { binding.btnNegative.apply { + state = LinkMindButtonState.DISABLE visibility = View.VISIBLE setText(context.getText(text).toString()) setOnClickListener(onClickListener) diff --git a/feature/mainfeature/src/main/java/designsystem/components/edittext/LinkMindEditTextBox.kt b/feature/mainfeature/src/main/java/designsystem/components/edittext/LinkMindEditTextBox.kt index c3113720..d562398a 100644 --- a/feature/mainfeature/src/main/java/designsystem/components/edittext/LinkMindEditTextBox.kt +++ b/feature/mainfeature/src/main/java/designsystem/components/edittext/LinkMindEditTextBox.kt @@ -33,16 +33,10 @@ class LinkMindEditTextBox @JvmOverloads constructor( preventFocusClearedByAdjustResize() } } - - binding.ivCancel.onThrottleClick { - binding.editText.text.clear() - } - binding.editText.doAfterTextChanged { text -> binding.ivCancel.visibility = - if (text.isNullOrEmpty()) View.GONE else View.VISIBLE + if (text.isNullOrEmpty()) View.INVISIBLE else View.VISIBLE } - val typedArray = context.obtainStyledAttributes(attrs, R.styleable.EditTextSearch, defStyleAttr, 0) @@ -52,6 +46,12 @@ class LinkMindEditTextBox @JvmOverloads constructor( typedArray.recycle() } + fun onClickTextClear(onClick: () -> Unit) { + binding.ivCancel.onThrottleClick { + binding.editText.text.clear() + onClick() + } + } /** * 실제 사용하면서 사이드 효과 측정할게염 @@ -60,7 +60,7 @@ class LinkMindEditTextBox @JvmOverloads constructor( binding.editText.doAfterTextChanged { text -> val isTextEmpty = text.isNullOrEmpty() - binding.ivCancel.visibility = if (isTextEmpty) View.GONE else View.VISIBLE + binding.ivCancel.visibility = if (isTextEmpty) View.INVISIBLE else View.VISIBLE if (!isTextEmpty) { editThrottleValue.setDelay(text.toString(), 300L) { diff --git a/feature/mainfeature/src/main/java/designsystem/components/toast/LinkMindSnackBar.kt b/feature/mainfeature/src/main/java/designsystem/components/toast/LinkMindSnackBar.kt new file mode 100644 index 00000000..0eff3192 --- /dev/null +++ b/feature/mainfeature/src/main/java/designsystem/components/toast/LinkMindSnackBar.kt @@ -0,0 +1,37 @@ +package designsystem.components.toast + +import android.content.Context +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.core.content.ContextCompat +import com.google.android.material.snackbar.Snackbar +import org.sopt.mainfeature.R +import org.sopt.mainfeature.databinding.LayoutToasterSnackbarBinding + +fun Context.linkMindSnackBar(view: View, message: String, warning: Boolean = false) { + Snackbar.make(view, "", Snackbar.LENGTH_LONG).apply { + val snackbarBinding = LayoutToasterSnackbarBinding.inflate(LayoutInflater.from(this@linkMindSnackBar)) + + (this.view as Snackbar.SnackbarLayout).apply { + removeAllViews() + setPadding(0, 0, 0, 0) + setBackgroundColor(ContextCompat.getColor(this@linkMindSnackBar, android.R.color.transparent)) + + if (warning) snackbarBinding.ivToast.setImageResource(R.drawable.ic_alert_18_white) + snackbarBinding.tvToast.text = message + + addView(snackbarBinding.root, 0) + } + + snackbarBinding.root.layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + FrameLayout.LayoutParams.WRAP_CONTENT, + ).apply { + gravity = Gravity.CENTER + } + + show() + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/ExamDesignActivity.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/ExamDesignActivity.kt new file mode 100644 index 00000000..184ab30b --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/ExamDesignActivity.kt @@ -0,0 +1,80 @@ +package org.sopt.mainfeature + +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import dagger.hilt.android.AndroidEntryPoint +import designsystem.components.bottomsheet.LinkMindBottomSheet +import designsystem.components.button.state.LinkMIndFullWidthButtonState +import designsystem.components.button.state.LinkMindButtonState +import designsystem.components.dialog.LinkMindDialog +import org.sopt.mainfeature.databinding.ActivityDesignComponentsBinding +import timber.log.Timber + +@AndroidEntryPoint +class ExamDesignActivity : AppCompatActivity() { + private lateinit var binding: ActivityDesignComponentsBinding + + private val linkMindDialog by lazy { + LinkMindDialog(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityDesignComponentsBinding.inflate(layoutInflater) + + binding.progress.apply { + setProgressBarMain(63) + } + binding.btnBlock.apply { + state = LinkMindButtonState.ENABLE + btnClick { + } + } + binding.etv.apply { + onClickTextClear { + Timber.d("SaK") + } + throttleAfterTextChanged { + Timber.d("SaK") + } + } + + binding.btnBlock2.apply { + state = LinkMindButtonState.DISABLE + } + binding.btnFull.apply { + state = LinkMIndFullWidthButtonState.ENABLE_PRIMARY + } + binding.btnFull1.apply { + state = LinkMIndFullWidthButtonState.ENABLE_BLACK + } + binding.btnFull2.apply { + state = LinkMIndFullWidthButtonState.DISABLE + } + setContentView(binding.root) + showRevokeCommonDialog() + + val linkMindBottomSheet = LinkMindBottomSheet(this) + linkMindBottomSheet.show() + linkMindBottomSheet.apply { + setTitle(R.string.text_clip) + setErroMsg(R.string.text_clip) + bottomSheetConfirmBtnClick { + Log.d("test", "test") + } + } + } + + private fun showRevokeCommonDialog() { + linkMindDialog.setTitle(R.string.text_home) + .setSubtitle(R.string.text_clip) + .setNegativeButton(R.string.text_home) { + linkMindDialog.dismiss() + } + .setPositiveButton(R.string.text_home) { + linkMindDialog.dismiss() + } + .show() + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/SplashActivity.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/SplashActivity.kt new file mode 100644 index 00000000..d3c400cc --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/SplashActivity.kt @@ -0,0 +1,15 @@ +package org.sopt.mainfeature + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import org.sopt.mainfeature.onboarding.LoginActivity + +class SplashActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val intent = Intent(this, LoginActivity::class.java) + startActivity(intent) + finish() + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/onboarding/LoginActivity.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/onboarding/LoginActivity.kt index f23b5174..9079a4a5 100644 --- a/feature/mainfeature/src/main/java/org/sopt/mainfeature/onboarding/LoginActivity.kt +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/onboarding/LoginActivity.kt @@ -8,14 +8,12 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import dagger.hilt.android.AndroidEntryPoint -import designsystem.components.dialog.LinkMindDialog import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import org.sopt.datastore.datastore.SecurityDataStore import org.sopt.mainfeature.MainActivity -import org.sopt.mainfeature.R import org.sopt.mainfeature.databinding.ActivityLoginBinding import org.sopt.oauthdomain.interactor.OAuthInteractor import org.sopt.ui.context.toast @@ -33,9 +31,6 @@ class LoginActivity : AppCompatActivity() { @Inject lateinit var dataStore: SecurityDataStore - private val linkMindDialog by lazy { - LinkMindDialog(this) - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityLoginBinding.inflate(layoutInflater) @@ -43,19 +38,8 @@ class LoginActivity : AppCompatActivity() { initCheckAutoLogin() initKakaoLoginBtnClickListener() initAuthStateObserver() - showRevokeCommonDialog() - } - private fun showRevokeCommonDialog() { - linkMindDialog.setTitle(R.string.text_home) - .setSubtitle(R.string.text_clip) - .setNegativeButton(R.string.text_home) { - linkMindDialog.dismiss() - } - .setPositiveButton(R.string.text_home) { - linkMindDialog.dismiss() - } - .show() } + private fun initCheckAutoLogin() { lifecycleScope.launch { if (dataStore.flowAutoLogin().first()) { @@ -89,6 +73,7 @@ class LoginActivity : AppCompatActivity() { is UiState.Failure -> { this@LoginActivity.toast(state.msg) } + is UiState.Loading -> {} else -> {} } diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CenterSnapHelper.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CenterSnapHelper.kt new file mode 100644 index 00000000..5b1cd1ce --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CenterSnapHelper.kt @@ -0,0 +1,36 @@ +package org.sopt.mainfeature.timer + +import android.view.View +import androidx.recyclerview.widget.LinearSnapHelper +import androidx.recyclerview.widget.OrientationHelper +import androidx.recyclerview.widget.RecyclerView + +class CenterSnapHelper : LinearSnapHelper() { + + override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray? { + val out = IntArray(2) + if (layoutManager.canScrollHorizontally()) { + out[0] = distanceToCenter(layoutManager, targetView, OrientationHelper.createHorizontalHelper(layoutManager)) + } else { + out[0] = 0 + } + + if (layoutManager.canScrollVertically()) { + out[1] = distanceToCenter(layoutManager, targetView, OrientationHelper.createVerticalHelper(layoutManager)) + } else { + out[1] = 0 + } + return out + } + + private fun distanceToCenter(layoutManager: RecyclerView.LayoutManager, targetView: View, helper: OrientationHelper): Int { + val childCenter = helper.getDecoratedStart(targetView) + helper.getDecoratedMeasurement(targetView) / 2 + val containerCenter: Int + containerCenter = if (layoutManager.clipToPadding) { + helper.startAfterPadding + helper.totalSpace / 2 + } else { + helper.end / 2 + } + return childCenter - containerCenter + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CompleteTimerAdapter.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CompleteTimerAdapter.kt new file mode 100644 index 00000000..75ee6af6 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CompleteTimerAdapter.kt @@ -0,0 +1,30 @@ +package org.sopt.mainfeature.timer + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.mainfeature.databinding.ItemTimerCompleteBinding +import org.sopt.mainfeature.timer.dummymodel.Timer +import org.sopt.ui.view.ItemDiffCallback + +class CompleteTimerAdapter( + private val onClicked: (Timer) -> Unit, +) : ListAdapter(DiffUtil) { + override fun onBindViewHolder(holder: CompleteTimerViewHolder, position: Int) { + holder.onBind(getItem(position)) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CompleteTimerViewHolder { + return CompleteTimerViewHolder( + ItemTimerCompleteBinding.inflate(LayoutInflater.from(parent.context), parent, false), + onClicked, + ) + } + + companion object { + private val DiffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.id == new.id }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CompleteTimerViewHolder.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CompleteTimerViewHolder.kt new file mode 100644 index 00000000..e53b71a3 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/CompleteTimerViewHolder.kt @@ -0,0 +1,30 @@ +package org.sopt.mainfeature.timer + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.mainfeature.databinding.ItemTimerCompleteBinding +import org.sopt.mainfeature.timer.dummymodel.Timer + +class CompleteTimerViewHolder( + private val binding: ItemTimerCompleteBinding, + private val onClicked: (Timer) -> Unit, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind(data: Timer?) { + if (data == null) return + with(binding) { + val ampm = if (data.am) AM else PM + val minute = if (data.minute >= 10) data.minute.toString() else MINUTE_FORMAT.format(data.minute) + tvItemTimerCompleteCategory.text = data.category + tvItemTimerCompleteTime.text = TIME_FORMAT.format(data.day, ampm, data.hour, minute) + tvItemTimerCompleteRead.setOnClickListener { + onClicked(data) + } + } + } + + companion object { + private const val TIME_FORMAT = "%s %s %d:%s" + private const val MINUTE_FORMAT = "0%d" + private const val AM = "오전" + private const val PM = "오후" + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/ExampleTimePickerFragment.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/ExampleTimePickerFragment.kt new file mode 100644 index 00000000..b5c4b3f7 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/ExampleTimePickerFragment.kt @@ -0,0 +1,124 @@ +package org.sopt.mainfeature.timer + +import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.RecyclerView +import org.sopt.mainfeature.databinding.FragmentExampleTimePickerBinding +import org.sopt.mainfeature.timer.dummymodel.PickerItem +import org.sopt.ui.base.BindingFragment + +class ExampleTimePickerFragment : BindingFragment({ FragmentExampleTimePickerBinding.inflate(it) }) { + private lateinit var textAdapter: TextAdapter + private lateinit var numberAdapter1: NumberAdapter + private lateinit var numberAdapter2: NumberAdapter + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + setupRecyclerViews() + } + + private fun setupRecyclerViews() { + val list1 = generateList1() + val list2 = generateNumberList(1, 12) + val list3 = generateNumberList(0, 59) + textAdapter = TextAdapter() + numberAdapter1 = NumberAdapter() + numberAdapter2 = NumberAdapter() + setupRecyclerView2(binding.rv1, list1, textAdapter) + setupRecyclerView1(binding.rv2, list2, numberAdapter1) + setupRecyclerView1(binding.rv3, list3, numberAdapter2) + } + + private fun generateList1() = listOf(PickerItem("", false), PickerItem("오전", true), PickerItem("오후", false), PickerItem("", false)) + + private fun generateNumberList(start: Int, end: Int): MutableList { + val list = mutableListOf() + list.add(PickerItem(end.toString(), false)) + list.add(PickerItem(start.toString(), true)) + for (i in start + 1..end - 1) { + list.add(PickerItem(i.toString(), false)) + } + return list + } + + private fun setupRecyclerView1(recyclerView: RecyclerView, list: List, numberAdapter: NumberAdapter) { + val snapHolder = CenterSnapHelper().apply { + attachToRecyclerView(recyclerView) + } + + with(numberAdapter) { + submitList(list) + recyclerView.adapter = this + numberAdapter + } + + recyclerView.itemAnimator = null + recyclerView.scrollToPosition(numberAdapter.getMiddlePosition()) + recyclerView.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + val centerView = snapHolder.findSnapView(recyclerView.layoutManager) + val pos = recyclerView.layoutManager?.getPosition(centerView!!) + val newList: MutableList = numberAdapter.currentList.mapIndexed { index, item -> + item.copy(isSelected = index == pos!! % numberAdapter.currentList.size) + }.toMutableList() + numberAdapter.run { + submitList(newList) + notifyDataSetChanged() + } + } + }, + ) + recyclerView.onFlingListener = object : RecyclerView.OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + val centerView = snapHolder.findSnapView(recyclerView.layoutManager) + val pos = recyclerView.layoutManager?.getPosition(centerView!!) + val newList: MutableList = numberAdapter.currentList.mapIndexed { index, item -> + item.copy(isSelected = index == pos!! % numberAdapter.currentList.size) + }.toMutableList() + numberAdapter.run { + submitList(newList) + notifyDataSetChanged() + } + return false + } + } + } + + private fun setupRecyclerView2(recyclerView: RecyclerView, list: List, textAdapter: TextAdapter) { + val snapHolder = CenterSnapHelper().apply { + attachToRecyclerView(recyclerView) + } + + with(textAdapter) { + submitList(list) + recyclerView.adapter = this + textAdapter + } + + recyclerView.itemAnimator = null + recyclerView.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + val centerView = snapHolder.findSnapView(recyclerView.layoutManager) + val pos = recyclerView.layoutManager?.getPosition(centerView!!) + val newList: MutableList = textAdapter.currentList.mapIndexed { index, item -> + item.copy(isSelected = index == pos) + }.toMutableList() + textAdapter.submitList(newList) + } + }, + ) + recyclerView.onFlingListener = object : RecyclerView.OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + val centerView = snapHolder.findSnapView(recyclerView.layoutManager) + val pos = recyclerView.layoutManager?.getPosition(centerView!!) + val newList: MutableList = textAdapter.currentList.mapIndexed { index, item -> + item.copy(isSelected = index == pos) + }.toMutableList() + textAdapter.submitList(newList) + return false + } + } + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/NumberAdapter.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/NumberAdapter.kt new file mode 100644 index 00000000..a9f1d757 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/NumberAdapter.kt @@ -0,0 +1,31 @@ +package org.sopt.mainfeature.timer + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.mainfeature.databinding.ItemNumberPickerBinding +import org.sopt.mainfeature.timer.dummymodel.PickerItem +import org.sopt.ui.view.ItemDiffCallback +import kotlin.math.round + +class NumberAdapter : ListAdapter(DiffUtil) { + override fun onBindViewHolder(holder: NumberViewHolder, position: Int) { + val realPosition = position % currentList.size + holder.onBind(getItem(realPosition)) + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NumberViewHolder { + return NumberViewHolder( + ItemNumberPickerBinding.inflate(LayoutInflater.from(parent.context), parent, false), + ) + } + override fun getItemCount(): Int = Int.MAX_VALUE + + fun getMiddlePosition() = + round(Int.MAX_VALUE / 2.0).toInt() - round(Int.MAX_VALUE / 2.0).toInt() % currentList.size + companion object { + private val DiffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old == new }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/NumberViewHolder.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/NumberViewHolder.kt new file mode 100644 index 00000000..411948be --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/NumberViewHolder.kt @@ -0,0 +1,22 @@ +package org.sopt.mainfeature.timer + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.mainfeature.R +import org.sopt.mainfeature.databinding.ItemNumberPickerBinding +import org.sopt.mainfeature.timer.dummymodel.PickerItem + +class NumberViewHolder( + val binding: ItemNumberPickerBinding, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind(data: PickerItem?) { + if (data == null) return + with(binding) { + if (data.isSelected) { + tvText.setTextAppearance(R.style.Typography_suit_bold_18) + } else { + tvText.setTextAppearance(R.style.Typography_suit_regular_16) + } + tvText.text = data.text + } + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TextAdapter.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TextAdapter.kt new file mode 100644 index 00000000..b92a9d3b --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TextAdapter.kt @@ -0,0 +1,25 @@ +package org.sopt.mainfeature.timer + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.mainfeature.databinding.ItemNumberPickerBinding +import org.sopt.mainfeature.timer.dummymodel.PickerItem +import org.sopt.ui.view.ItemDiffCallback + +class TextAdapter : ListAdapter(DiffUtil) { + override fun onBindViewHolder(holder: NumberViewHolder, position: Int) { + holder.onBind(getItem(position)) + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NumberViewHolder { + return NumberViewHolder( + ItemNumberPickerBinding.inflate(LayoutInflater.from(parent.context), parent, false), + ) + } + companion object { + private val DiffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old == new }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TextViewHolder.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TextViewHolder.kt new file mode 100644 index 00000000..966550ce --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TextViewHolder.kt @@ -0,0 +1,22 @@ +package org.sopt.mainfeature.timer + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.mainfeature.R +import org.sopt.mainfeature.databinding.ItemNumberPickerBinding +import org.sopt.mainfeature.timer.dummymodel.PickerItem + +class TextViewHolder( + val binding: ItemNumberPickerBinding, +) : RecyclerView.ViewHolder(binding.root) { + fun onBind(data: PickerItem?) { + if (data == null) return + with(binding) { + if (data.isSelected) { + tvText.setTextAppearance(R.style.Typography_suit_bold_18) + } else { + tvText.setTextAppearance(R.style.Typography_suit_regular_16) + } + tvText.text = data.text + } + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TimerFragment.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TimerFragment.kt index d2adf09e..14f3d79e 100644 --- a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TimerFragment.kt +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/TimerFragment.kt @@ -1,61 +1,87 @@ package org.sopt.mainfeature.timer +import android.content.res.ColorStateList import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.isGone +import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.fragment.app.commit import org.sopt.mainfeature.R +import org.sopt.mainfeature.databinding.FragmentTimerBinding +import org.sopt.mainfeature.timer.dummymodel.Timer +import org.sopt.mainfeature.timer.modifytimer.ModifyTimerBottomSheetFragment +import org.sopt.ui.fragment.colorOf +import org.sopt.ui.fragment.snackBar -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [TimerFragment.newInstance] factory method to - * create an instance of this fragment. - */ class TimerFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) + private var _binding: FragmentTimerBinding? = null + private val binding + get() = requireNotNull(_binding) { } - } - + private lateinit var completeTimerAdapter: CompleteTimerAdapter + private lateinit var waitTimerAdapter: WaitTimerAdapter + var timerExist = true override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_timer, container, false) + _binding = FragmentTimerBinding.inflate(layoutInflater) + return binding.root } - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment TimerFragment. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - TimerFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.tvTimerTitle.setOnClickListener { + if (timerExist) { + binding.svTimerExist.isVisible = true + binding.llTimerNotExist.isGone = true + timerExist = false + } else { + binding.svTimerExist.isGone = true + binding.llTimerNotExist.isVisible = true + timerExist = true } + } + + completeTimerAdapter = CompleteTimerAdapter({ snackBar(binding.root, { "안녕" }) }) + waitTimerAdapter = WaitTimerAdapter({}, { ModifyTimerBottomSheetFragment.newInstance(it.id).show(parentFragmentManager, this.tag) }) + + val list = listOf( + Timer(1, "네이버", "일요일", true, 8, 37), + Timer(1, "네이버", "일요일", true, 8, 37), + ) + // val list = emptyList() + completeTimerAdapter.submitList(list) + waitTimerAdapter.submitList(list) + binding.tvTimerCompleteCount.text = list.count().toString() + if (list.count() != 0) { + val color = colorOf(R.color.primary) + val textColor = colorOf(R.color.white) + val colorStateList = ColorStateList.valueOf(color) + binding.flTimerCompleteCount.backgroundTintList = colorStateList + binding.tvTimerCompleteCount.setTextColor(textColor) + binding.tvTimerNotComplete.isGone = true + } else { + binding.tvTimerNotComplete.isVisible = true + } + binding.rvTimerComplete.adapter = completeTimerAdapter + binding.rvTimerWait.adapter = waitTimerAdapter + + binding.ivTimerPlus.setOnClickListener { + parentFragmentManager.commit { + val exampleTimePickerFragment = ExampleTimePickerFragment() + replace(R.id.fcv_main, exampleTimePickerFragment) + } + } + } + + override fun onDestroyView() { + _binding = null + super.onDestroyView() } } diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/WaitTimerAdapter.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/WaitTimerAdapter.kt new file mode 100644 index 00000000..acd797e8 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/WaitTimerAdapter.kt @@ -0,0 +1,32 @@ +package org.sopt.mainfeature.timer + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import org.sopt.mainfeature.databinding.ItemTimerWaitBinding +import org.sopt.mainfeature.timer.dummymodel.Timer +import org.sopt.ui.view.ItemDiffCallback + +class WaitTimerAdapter( + private val onToggleClicked: (Timer) -> Unit, + private val onMoreClicked: (Timer) -> Unit, +) : ListAdapter(DiffUtil) { + override fun onBindViewHolder(holder: WaitTimerViewHolder, position: Int) { + holder.onBind(getItem(position)) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WaitTimerViewHolder { + return WaitTimerViewHolder( + ItemTimerWaitBinding.inflate(LayoutInflater.from(parent.context), parent, false), + onToggleClicked, + onMoreClicked, + ) + } + + companion object { + private val DiffUtil = ItemDiffCallback( + onItemsTheSame = { old, new -> old.id == new.id }, + onContentsTheSame = { old, new -> old == new }, + ) + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/WaitTimerViewHolder.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/WaitTimerViewHolder.kt new file mode 100644 index 00000000..280f059b --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/WaitTimerViewHolder.kt @@ -0,0 +1,36 @@ +package org.sopt.mainfeature.timer + +import androidx.recyclerview.widget.RecyclerView +import org.sopt.mainfeature.databinding.ItemTimerWaitBinding +import org.sopt.mainfeature.timer.dummymodel.Timer + +class WaitTimerViewHolder( + private val binding: ItemTimerWaitBinding, + private val onToggleClicked: (Timer) -> Unit, + private val onMoreClicked: (Timer) -> Unit, +) : RecyclerView.ViewHolder(binding.root) { + + fun onBind(data: Timer?) { + if (data == null) return + with(binding) { + tvItemTimerWaitCategory.text = data.category + val ampm = if (data.am) AM else PM + val minute = if (data.minute != 0) MINUTE_FORMAT.format(data.minute) else "" + tvItemTimerWaitWhen.text = TIME_FORMAT.format(data.day, ampm, data.hour, minute) + + tgItemTimerWait.setOnClickListener { + onToggleClicked(data) + } + ivItemTimerWaitMore.setOnClickListener { + onMoreClicked(data) + } + } + } + + companion object { + private const val TIME_FORMAT = "매주 %s %s %d시%s마다" + private const val MINUTE_FORMAT = " %d분" + private const val AM = "오전" + private const val PM = "오후" + } +} diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/dummymodel/PickerItem.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/dummymodel/PickerItem.kt new file mode 100644 index 00000000..db555807 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/dummymodel/PickerItem.kt @@ -0,0 +1,6 @@ +package org.sopt.mainfeature.timer.dummymodel + +data class PickerItem( + val text: String, + val isSelected: Boolean, +) diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/dummymodel/Timer.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/dummymodel/Timer.kt new file mode 100644 index 00000000..1b222f73 --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/dummymodel/Timer.kt @@ -0,0 +1,10 @@ +package org.sopt.mainfeature.timer.dummymodel + +data class Timer( + val id: Int, + val category: String, + val day: String, + val am: Boolean, + val hour: Int, + val minute: Int, +) diff --git a/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/modifytimer/ModifyTimerBottomSheetFragment.kt b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/modifytimer/ModifyTimerBottomSheetFragment.kt new file mode 100644 index 00000000..ae7ac2bf --- /dev/null +++ b/feature/mainfeature/src/main/java/org/sopt/mainfeature/timer/modifytimer/ModifyTimerBottomSheetFragment.kt @@ -0,0 +1,33 @@ +package org.sopt.mainfeature.timer.modifytimer + +import android.os.Bundle +import android.view.View +import org.sopt.mainfeature.databinding.FragmentModifyTimerBottomSheetBinding +import org.sopt.ui.base.BindingBottomSheetDialogFragment + +class ModifyTimerBottomSheetFragment : + BindingBottomSheetDialogFragment({ FragmentModifyTimerBottomSheetBinding.inflate(it) }) { + var id: Int? = null + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.ivModifyTimerClose.setOnClickListener { + dismiss() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + id = arguments?.getInt("id") + } + companion object { + fun newInstance(id: Int): ModifyTimerBottomSheetFragment { + val args = Bundle().apply { + putInt("id", id) + } + return ModifyTimerBottomSheetFragment().apply { + arguments = args + } + } + } +} diff --git a/feature/mainfeature/src/main/res/drawable/ic_alarm_24.xml b/feature/mainfeature/src/main/res/drawable/ic_alarm_24.xml new file mode 100644 index 00000000..3754e762 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_alarm_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_alarm_disabled_20.xml b/feature/mainfeature/src/main/res/drawable/ic_alarm_disabled_20.xml new file mode 100644 index 00000000..58e15972 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_alarm_disabled_20.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_alert_18_white.xml b/feature/mainfeature/src/main/res/drawable/ic_alert_18_white.xml new file mode 100644 index 00000000..452fd619 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_alert_18_white.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_check_18_white.xml b/feature/mainfeature/src/main/res/drawable/ic_check_18_white.xml new file mode 100644 index 00000000..0ea78935 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_check_18_white.xml @@ -0,0 +1,16 @@ + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_clip_unclicked.xml b/feature/mainfeature/src/main/res/drawable/ic_clip_unclicked.xml new file mode 100644 index 00000000..3f5592a1 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_clip_unclicked.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_close_20.xml b/feature/mainfeature/src/main/res/drawable/ic_close_20.xml new file mode 100644 index 00000000..b4529451 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_close_20.xml @@ -0,0 +1,20 @@ + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_close_24.xml b/feature/mainfeature/src/main/res/drawable/ic_close_24.xml index cde145a5..9933b993 100644 --- a/feature/mainfeature/src/main/res/drawable/ic_close_24.xml +++ b/feature/mainfeature/src/main/res/drawable/ic_close_24.xml @@ -8,13 +8,13 @@ android:strokeLineJoin="round" android:strokeWidth="2" android:fillColor="#00000000" - android:strokeColor="#444444" + android:strokeColor="#4C4C4C" android:strokeLineCap="round"/> diff --git a/feature/mainfeature/src/main/res/drawable/ic_ellipse_18.xml b/feature/mainfeature/src/main/res/drawable/ic_ellipse_18.xml new file mode 100644 index 00000000..b00cd2d5 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_ellipse_18.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_home_unclicked.xml b/feature/mainfeature/src/main/res/drawable/ic_home_unclicked.xml new file mode 100644 index 00000000..f1bc01c5 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_home_unclicked.xml @@ -0,0 +1,16 @@ + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_more_24.xml b/feature/mainfeature/src/main/res/drawable/ic_more_24.xml new file mode 100644 index 00000000..24045e52 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_more_24.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_my_unclicked.xml b/feature/mainfeature/src/main/res/drawable/ic_my_unclicked.xml new file mode 100644 index 00000000..67f0d5e1 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_my_unclicked.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_plus_24.xml b/feature/mainfeature/src/main/res/drawable/ic_plus_24.xml new file mode 100644 index 00000000..1aed0500 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_plus_24.xml @@ -0,0 +1,13 @@ + + + diff --git a/feature/mainfeature/src/main/res/drawable/ic_timer_unclicked.xml b/feature/mainfeature/src/main/res/drawable/ic_timer_unclicked.xml new file mode 100644 index 00000000..24c71c91 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/ic_timer_unclicked.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/img_timer_none.png b/feature/mainfeature/src/main/res/drawable/img_timer_none.png new file mode 100644 index 00000000..2309b097 Binary files /dev/null and b/feature/mainfeature/src/main/res/drawable/img_timer_none.png differ diff --git a/feature/mainfeature/src/main/res/drawable/progress_bar_horizontal.xml b/feature/mainfeature/src/main/res/drawable/progress_bar_horizontal.xml index e57518fb..89974156 100644 --- a/feature/mainfeature/src/main/res/drawable/progress_bar_horizontal.xml +++ b/feature/mainfeature/src/main/res/drawable/progress_bar_horizontal.xml @@ -3,7 +3,7 @@ - + @@ -11,8 +11,8 @@ - + - \ No newline at end of file + diff --git a/feature/mainfeature/src/main/res/drawable/sel_bnv_clip.xml b/feature/mainfeature/src/main/res/drawable/sel_bnv_clip.xml new file mode 100644 index 00000000..62303d42 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/sel_bnv_clip.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/sel_bnv_home.xml b/feature/mainfeature/src/main/res/drawable/sel_bnv_home.xml new file mode 100644 index 00000000..e0fd41e4 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/sel_bnv_home.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/sel_bnv_my.xml b/feature/mainfeature/src/main/res/drawable/sel_bnv_my.xml new file mode 100644 index 00000000..6b3dfcb6 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/sel_bnv_my.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/sel_bnv_text_color.xml b/feature/mainfeature/src/main/res/drawable/sel_bnv_text_color.xml new file mode 100644 index 00000000..cc71e979 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/sel_bnv_text_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/sel_bnv_timer.xml b/feature/mainfeature/src/main/res/drawable/sel_bnv_timer.xml new file mode 100644 index 00000000..d037ffcd --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/sel_bnv_timer.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_gray_fill_12_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_gray_fill_12_rect.xml new file mode 100644 index 00000000..afc68716 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_gray_fill_12_rect.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_neutrals200_fill_12_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_neutrals200_fill_12_rect.xml new file mode 100644 index 00000000..ddf1993b --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_neutrals200_fill_12_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_neutrals850_fill_12_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_neutrals850_fill_12_rect.xml new file mode 100644 index 00000000..d2e03697 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_neutrals850_fill_12_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_12_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_12_rect.xml new file mode 100644 index 00000000..7e887629 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_12_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_8_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_8_rect.xml new file mode 100644 index 00000000..4a905979 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_8_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_top20_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_top20_rect.xml new file mode 100644 index 00000000..f9de4df9 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_neutrals_fill_top20_rect.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_primary_fill_4_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_primary_fill_4_rect.xml new file mode 100644 index 00000000..e02acda3 --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_primary_fill_4_rect.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_primary_fill_8_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_primary_fill_8_rect.xml new file mode 100644 index 00000000..5a9aefbc --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_primary_fill_8_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/drawable/shape_white_fill_12_rect.xml b/feature/mainfeature/src/main/res/drawable/shape_white_fill_12_rect.xml new file mode 100644 index 00000000..7241ec2c --- /dev/null +++ b/feature/mainfeature/src/main/res/drawable/shape_white_fill_12_rect.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/feature/mainfeature/src/main/res/layout/activity_design_components.xml b/feature/mainfeature/src/main/res/layout/activity_design_components.xml new file mode 100644 index 00000000..edf77706 --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/activity_design_components.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/activity_main.xml b/feature/mainfeature/src/main/res/layout/activity_main.xml index 81ff76ad..ed1a79e2 100644 --- a/feature/mainfeature/src/main/res/layout/activity_main.xml +++ b/feature/mainfeature/src/main/res/layout/activity_main.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> - + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/button_block_linkmind.xml b/feature/mainfeature/src/main/res/layout/button_block_linkmind.xml index 208180c0..c4d032e7 100644 --- a/feature/mainfeature/src/main/res/layout/button_block_linkmind.xml +++ b/feature/mainfeature/src/main/res/layout/button_block_linkmind.xml @@ -2,21 +2,22 @@ + android:layout_marginHorizontal="20dp" + android:paddingVertical="21dp"> diff --git a/feature/mainfeature/src/main/res/layout/button_full_width_linkmind.xml b/feature/mainfeature/src/main/res/layout/button_full_width_linkmind.xml index cf4cfdca..543097c3 100644 --- a/feature/mainfeature/src/main/res/layout/button_full_width_linkmind.xml +++ b/feature/mainfeature/src/main/res/layout/button_full_width_linkmind.xml @@ -16,6 +16,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" + android:textAppearance="@style/Typography.suit.bold_16" app:layout_constraintTop_toTopOf="parent" tools:text="BTN" /> diff --git a/feature/mainfeature/src/main/res/layout/button_pop_up_linkmind.xml b/feature/mainfeature/src/main/res/layout/button_pop_up_linkmind.xml index 13f0cde2..43fb1597 100644 --- a/feature/mainfeature/src/main/res/layout/button_pop_up_linkmind.xml +++ b/feature/mainfeature/src/main/res/layout/button_pop_up_linkmind.xml @@ -9,18 +9,19 @@ android:id="@+id/cl_btn_half_width_linkmind" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingVertical="14dp" - android:paddingHorizontal="44dp"> + android:paddingHorizontal="44dp" + android:paddingVertical="14dp"> diff --git a/feature/mainfeature/src/main/res/layout/dialog_linkmind.xml b/feature/mainfeature/src/main/res/layout/dialog_linkmind.xml index fcccde9c..369ecf41 100644 --- a/feature/mainfeature/src/main/res/layout/dialog_linkmind.xml +++ b/feature/mainfeature/src/main/res/layout/dialog_linkmind.xml @@ -3,26 +3,39 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/black" - android:padding="24dp"> + android:layout_height="wrap_content" + android:background="@drawable/shape_neutrals_fill_12_rect" + android:paddingHorizontal="24dp" + android:paddingTop="20dp" + android:paddingBottom="24dp"> + + + android:layout_height="wrap_content" + android:background="@drawable/shape_neutrals_fill_12_rect"> @@ -25,7 +26,7 @@ android:layout_height="wrap_content" android:padding="8dp" android:src="@drawable/ic_cancle_24" - android:visibility="gone" + android:visibility="invisible" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/feature/mainfeature/src/main/res/layout/edit_text_search_box_linkmind.xml b/feature/mainfeature/src/main/res/layout/edit_text_search_box_linkmind.xml index 79379035..3ecf2663 100644 --- a/feature/mainfeature/src/main/res/layout/edit_text_search_box_linkmind.xml +++ b/feature/mainfeature/src/main/res/layout/edit_text_search_box_linkmind.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:background="@drawable/shape_neutrals_fill_12_rect"> diff --git a/feature/mainfeature/src/main/res/layout/fragment_example_time_picker.xml b/feature/mainfeature/src/main/res/layout/fragment_example_time_picker.xml new file mode 100644 index 00000000..1648cca6 --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/fragment_example_time_picker.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/fragment_modify_timer_bottom_sheet.xml b/feature/mainfeature/src/main/res/layout/fragment_modify_timer_bottom_sheet.xml new file mode 100644 index 00000000..fe60400f --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/fragment_modify_timer_bottom_sheet.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/fragment_timer.xml b/feature/mainfeature/src/main/res/layout/fragment_timer.xml index 503353e9..c72b9e77 100644 --- a/feature/mainfeature/src/main/res/layout/fragment_timer.xml +++ b/feature/mainfeature/src/main/res/layout/fragment_timer.xml @@ -1,13 +1,309 @@ - + + + android:layout_height="56dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + + + + android:layout_height="wrap_content" + android:background="@drawable/shape_gray_fill_12_rect" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/item_number_picker.xml b/feature/mainfeature/src/main/res/layout/item_number_picker.xml new file mode 100644 index 00000000..295cf4fb --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/item_number_picker.xml @@ -0,0 +1,10 @@ + + diff --git a/feature/mainfeature/src/main/res/layout/item_timer_complete.xml b/feature/mainfeature/src/main/res/layout/item_timer_complete.xml new file mode 100644 index 00000000..220769d7 --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/item_timer_complete.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/item_timer_wait.xml b/feature/mainfeature/src/main/res/layout/item_timer_wait.xml new file mode 100644 index 00000000..89556102 --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/item_timer_wait.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/layout/layout_toaster_snackbar.xml b/feature/mainfeature/src/main/res/layout/layout_toaster_snackbar.xml new file mode 100644 index 00000000..b343cd82 --- /dev/null +++ b/feature/mainfeature/src/main/res/layout/layout_toaster_snackbar.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/feature/mainfeature/src/main/res/menu/main_nav_menu.xml b/feature/mainfeature/src/main/res/menu/main_nav_menu.xml index e0ebdbc0..4f6e41b1 100644 --- a/feature/mainfeature/src/main/res/menu/main_nav_menu.xml +++ b/feature/mainfeature/src/main/res/menu/main_nav_menu.xml @@ -1,27 +1,33 @@ -

+ + android:icon="@drawable/sel_bnv_home" + android:title="@string/text_home" + app:iconPadding="4dp" /> + android:icon="@drawable/sel_bnv_clip" + android:title="@string/text_clip" + app:iconPadding="4dp" /> + android:icon="@drawable/sel_bnv_timer" + android:title="@string/text_timer" + app:iconPadding="4dp" /> + android:icon="@drawable/sel_bnv_my" + android:title="@string/text_my" + app:iconPadding="4dp" /> diff --git a/feature/mainfeature/src/main/res/values/colors.xml b/feature/mainfeature/src/main/res/values/colors.xml index a47c4624..f1c21365 100644 --- a/feature/mainfeature/src/main/res/values/colors.xml +++ b/feature/mainfeature/src/main/res/values/colors.xml @@ -3,6 +3,7 @@ #FF000000 #FFFFFFFF + #FFFFFFFF #F2F2F2 #E5E5E5 #D9D9D9 @@ -15,6 +16,7 @@ #333333 #262626 #191919 + #FF000000 #FEEFEB diff --git a/feature/mainfeature/src/main/res/values/strings.xml b/feature/mainfeature/src/main/res/values/strings.xml index 585f5b77..24d8ae8e 100644 --- a/feature/mainfeature/src/main/res/values/strings.xml +++ b/feature/mainfeature/src/main/res/values/strings.xml @@ -11,4 +11,22 @@ 이번주 열람한 링크 이번주 저장한 링크 아직 마이페이지는 추가 공사 중!\n 업데이트를 기다려주세요 :) + + TIMER + 알림 설정이 꺼져 있어요 + 타이머 기능을 이용하시려면 + 기기 설정 > 알림 + 에서 알림을 켜주세요. + 타이머를 설정하고\n원하는 때에 링크를 받아보세요 + 타이머 설정하기 + 읽으러가기 + 링크들을 + 읽기 딱 좋은 시간이에요! + 타이머 대기 중.. + 타이머가 완료되면 리마인드 드릴게요 + 완료된 타이머 + + 타이머 수정하기 + 삭제 + 수정하기 diff --git a/feature/mainfeature/src/main/res/values/themes.xml b/feature/mainfeature/src/main/res/values/themes.xml index 21917539..a864881b 100644 --- a/feature/mainfeature/src/main/res/values/themes.xml +++ b/feature/mainfeature/src/main/res/values/themes.xml @@ -7,8 +7,12 @@ @android:color/transparent true + @color/background