-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 그룹 생성 및 입장 화면 구성 및 기능 구현 #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughRefactors group creation/entry flows to remove global state, introduce ViewModel callbacks, and update navigation. Adds BasicToast utility and UI assets. Modifies API models (request/response). Updates multiple fragments and layouts, removes EnterCodeGroupFragment, and adds EditGroupCompleteFragment and EnterGroupInfoFragment. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Category as CreateGroupCategroyFragment
participant Info as CreateGroupInfoFragment
participant VM as GroupViewModel
participant Invite as CreateGroupInviteFragment
participant Complete as CreateGroupCompleteFragment
participant Nav as FragmentManager
User->>Category: Select category
Category->>Info: Navigate with {category, isEdit}
User->>Info: Enter name/description, tap Next
Info->>VM: createGroup(type, name, desc, onSuccess)
VM-->>Info: onSuccess(code)
Info->>Invite: Navigate with {code}
User->>Invite: Copy/share, tap Next
Invite->>Complete: Navigate
Complete->>Nav: Navigate to GroupDetailFragment
sequenceDiagram
actor User
participant Code as EnterGroupCodeFragment
participant VM as GroupViewModel
participant Info as EnterGroupInfoFragment
participant Nav as BottomNav
User->>Code: Enter code, tap Next
Code->>VM: getGroupInfoWithCode(code, onSuccess)
VM-->>Code: onSuccess()
Code->>Info: Navigate with {code}
User->>Info: Tap Enter Group
Info->>VM: enterGroup(code, onSuccess)
VM-->>Info: onSuccess()
Info->>Nav: Switch to Group tab
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related issues
Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 21
🔭 Outside diff range comments (9)
app/src/main/res/layout/fragment_create_group_complete.xml (3)
62-71: Fix weight usage: use 0dp width when using layout_weight in a horizontal LinearLayoutWith layout_width="match_parent" and layout_weight="1", the first button will consume the full width, breaking the two-column layout. Set width to 0dp to allow weights to distribute space correctly.
<TextView android:id="@+id/button_my_group" style="@style/ButtonGray70" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:backgroundTint="@null" android:text="@string/button_go_to_created_group" android:textAlignment="center" />
72-80: Fix weight usage and RTL support for margin
- Same weight issue: use width 0dp.
- Replace deprecated/RTL-unsafe layout_marginLeft with layout_marginStart.
<TextView android:id="@+id/button_accept" style="@style/ButtonGray20" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginLeft="12dp" + android:layout_marginStart="12dp" android:layout_weight="1" android:text="확인" android:textAlignment="center" />
20-38: Externalize all hardcoded strings & switch to RTL-safe marginsPlease move every literal
android:text="…"inapp/src/main/res/layout—including the two TextViews in
fragment_create_group_complete.xml(lines 20–38) and the “확인” button (lines 69–80)—intostrings.xml, then reference them with@string/…. For example:
- “그룹 생성 완료!” →
<string name="group_create_complete_title">그룹 생성 완료!</string>- “이제 매장을 선택해서\n장부 결제를 진행할 수 있어요” →
<string name="group_create_complete_desc">…</string>- “확인” →
<string name="ok">확인</string>You’ll also need to replace all LTR-only margins across your layouts. A quick scan shows ~47 occurrences of
android:layout_marginLeft="…dp"(e.g. infragment_login_main.xml,fragment_home.xml,row_store_list.xml, etc.). Swap each forandroid:layout_marginStart="…dp"(and uselayout_marginEndwhere appropriate) to ensure proper RTL support.• fragment_create_group_complete.xml
– Lines 20–38: hardcodedandroid:text
– Lines 69–80: hardcoded “확인” +layout_marginLeftat line 77
• All other layouts underapp/src/main/res/layoutusing:
–android:text="…"without@string/
–android:layout_marginLeft="…dp"Helper commands:
# List text attrs not using @string grep -R --no-heading -n 'android:text=' app/src/main/res/layout | grep -v '@string/' # Confirm remaining LTR-only margins rg --no-heading -n 'layout_marginLeft' app/src/main/res/layoutapp/src/main/java/com/project/jangburich/MainUtil.kt (1)
35-41: Bug: top padding is overwritten; status bar padding is lostTwo consecutive
setPaddingcalls on Line 36 and Line 39 override each other. The second call resets the top padding to 0, eliminating the status bar inset. Combine them to set both top and bottom at once (and preserve left/right).Apply this diff:
- // 상태바 위로부터 시작 (상단 패딩 설정) - view.setPadding(0, statusBarHeight, 0, 0) - - // 네비게이션 바를 침범하지 않도록 하단 패딩 설정 - view.setPadding(0, 0, 0, navigationBarHeight) + // 상태바/네비게이션 바 패딩을 동시에 적용 + val left = view.paddingLeft + val right = view.paddingRight + view.setPadding(left, statusBarHeight, right, navigationBarHeight)Optional: The else-branch (API 29 and below) sets layout flags to draw under system bars but does not apply padding for status/navigation bars, which can cause overlap. Consider unifying both branches via WindowInsetsCompat for consistent inset handling across APIs.
app/src/main/java/com/project/jangburich/ui/group/viewModel/GroupViewModel.kt (4)
42-79: Improve error handling and avoid exposing implementation details in logsThe createGroup method has several issues:
- Error responses are not propagated to the callback
- Sensitive data might be logged in production
- Using
toString()on nullable UUID without null checkApply this diff to improve error handling:
-fun createGroup(activity: MainActivity, type: String, groupName: String, groupDescription: String?, onSuccess: (code: String) -> Unit) { +fun createGroup(activity: MainActivity, type: String, groupName: String, groupDescription: String?, onSuccess: (code: String) -> Unit, onError: ((String) -> Unit)? = null) { val apiClient = ApiClient(activity) val tokenManager = TokenManager(activity) var groupInfo = CreateGroupRequest(type, groupName, groupDescription) apiClient.apiService.createGroup("Bearer ${tokenManager.getAccessToken()}", groupInfo).enqueue(object : Callback<BaseResponse<CreateGroupResponse>> { override fun onResponse( call: Call<BaseResponse<CreateGroupResponse>>, response: Response<BaseResponse<CreateGroupResponse>> ) { - Log.d("##", "onResponse 성공: " + response.body().toString()) if (response.isSuccessful) { // 정상적으로 통신이 성공된 경우 val result: BaseResponse<CreateGroupResponse>? = response.body() - Log.d("##", "onResponse 성공: " + result?.toString()) + val uuid = result?.data?.uuid + if (uuid != null) { + onSuccess(uuid) + } else { + onError?.invoke("Failed to get group code") + } - onSuccess(result?.data?.uuid.toString()) } else { // 통신이 실패한 경우(응답코드 3xx, 4xx 등) - var result: BaseResponse<CreateGroupResponse>? = response.body() - Log.d("##", "onResponse 실패") - Log.d("##", "onResponse 실패: " + response.code()) - Log.d("##", "onResponse 실패: " + response.body()) - val errorBody = response.errorBody()?.string() // 에러 응답 데이터를 문자열로 얻음 - Log.d("##", "Error Response: $errorBody") + val errorBody = response.errorBody()?.string() + onError?.invoke(errorBody ?: "Unknown error occurred") } } override fun onFailure(call: Call<BaseResponse<CreateGroupResponse>>, t: Throwable) { // 통신 실패 - Log.d("##", "onFailure 에러: " + t.message.toString()) + onError?.invoke(t.message ?: "Network error occurred") } }) }
252-289: Add error callback to getGroupInfoWithCodeThe method only handles success but doesn't provide error feedback to callers.
Consider adding an error callback similar to other methods:
-fun getGroupInfoWithCode(activity: MainActivity, code: String, onSuccess: () -> Unit) { +fun getGroupInfoWithCode(activity: MainActivity, code: String, onSuccess: () -> Unit, onError: ((String) -> Unit)? = null) { // ... existing code ... } else { // 통신이 실패한 경우(응답코드 3xx, 4xx 등) var result: BaseResponse<GetGroupInfoWithCodeResponse>? = response.body() Log.d("##", "onResponse 실패") Log.d("##", "onResponse 실패: " + response.code()) Log.d("##", "onResponse 실패: " + response.body()) val errorBody = response.errorBody()?.string() Log.d("##", "Error Response: $errorBody") + onError?.invoke(errorBody ?: "Failed to get group info") } } override fun onFailure(call: Call<BaseResponse<GetGroupInfoWithCodeResponse>>, t: Throwable) { // 통신 실패 Log.d("##", "onFailure 에러: " + t.message.toString()) + onError?.invoke(t.message ?: "Network error") }
291-326: Add error callback to enterGroup methodSimilar to other methods, enterGroup should provide error feedback.
-fun enterGroup(activity: MainActivity, code: String, onSuccess: () -> Unit) { +fun enterGroup(activity: MainActivity, code: String, onSuccess: () -> Unit, onError: ((String) -> Unit)? = null) { // ... existing code ... } else { // 통신이 실패한 경우(응답코드 3xx, 4xx 등) var result: BaseResponse<MessageResponse>? = response.body() Log.d("##", "onResponse 실패") Log.d("##", "onResponse 실패: " + response.code()) Log.d("##", "onResponse 실패: " + response.body()) val errorBody = response.errorBody()?.string() Log.d("##", "Error Response: $errorBody") + onError?.invoke(errorBody ?: "Failed to enter group") } } override fun onFailure(call: Call<BaseResponse<MessageResponse>>, t: Throwable) { // 통신 실패 Log.d("##", "onFailure 에러: " + t.message.toString()) + onError?.invoke(t.message ?: "Network error") }
55-69: Remove or guard debug logging for productionDebug logs with sensitive data should not be present in production builds.
Consider using BuildConfig to conditionally log:
-Log.d("##", "onResponse 성공: " + response.body().toString()) +if (BuildConfig.DEBUG) { + Log.d("GroupViewModel", "createGroup response: ${response.code()}") +}Apply this pattern throughout the file for all Log statements.
app/src/main/java/com/project/jangburich/ui/group/CreateGroupCategroyFragment.kt (1)
49-57: Incorrect icon resource for group2 unselected stateThe unselected icon for group2 is using group1's resource, which appears to be a copy-paste error.
val buttonUnselectedIcons = listOf( R.drawable.ic_group1_unselected, // 각 버튼의 비선택 상태 이미지 - R.drawable.ic_group1_unselected, + R.drawable.ic_group2_unselected, R.drawable.ic_group3_unselected, R.drawable.ic_group4_unselected, R.drawable.ic_group5_unselected, R.drawable.ic_group6_unselected, R.drawable.ic_group7_unselected, R.drawable.ic_group8_unselected )
🧹 Nitpick comments (21)
app/src/main/res/layout/fragment_create_group_complete.xml (2)
69-70: Make the label action-oriented and externalize to strings.xml"생성한 그룹으로" reads a bit incomplete. Consider "생성한 그룹으로 이동" (or "생성한 그룹 보기") and move it to a string resource for i18n.
Apply this diff in the layout:
- android:text="생성한 그룹으로" + android:text="@string/button_go_to_created_group"Add to res/values/strings.xml (outside this file):
<string name="button_go_to_created_group">생성한 그룹으로 이동</string>
62-71: Prefer semantic buttons for accessibility (MaterialButton over TextView)These are actionable controls styled as TextView. For TalkBack semantics, focus handling, ripple, and state, prefer MaterialButton (or Button) unless the style enforces TextView for a reason. If you must keep TextView, ensure clickable/focusable and proper accessibility labels.
Example for one button:
<com.google.android.material.button.MaterialButton android:id="@+id/button_my_group" style="@style/ButtonGray70" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="@string/button_go_to_created_group" />Also applies to: 72-80
app/src/main/res/drawable/ic_link_kakao.xml (1)
1-13: Brand icon addition looks good; add accessibility at usage sites.Vector structure and colors match Kakao branding. Since drawables can’t carry content descriptions, ensure ImageViews using this asset set an appropriate android:contentDescription (e.g., “Invite via KakaoTalk”) to meet accessibility requirements. Consider stateful tinting only if branding allows it.
Would you like me to scan layouts for usages of this drawable and flag missing contentDescription attributes?
app/src/main/res/layout/fragment_create_group_categroy.xml (1)
59-59: Minor UI consistency: vertical padding differs between rows.Row 1 uses paddingVertical="19dp" while row 2 uses "18dp". If not intentional, align them for consistent tap targets.
Also applies to: 94-94, 129-129, 164-164, 206-206, 241-241, 276-276, 311-311
app/src/main/res/drawable/ic_link_code.xml (1)
1-16: Icon asset addition looks solid; remember accessibility at usage sites.Vector and grouping are fine. As with other icons, set android:contentDescription on the ImageView using this drawable (e.g., “Copy invite code”). Avoid tinting if the design relies on fixed colors.
Want me to scan layouts for where this drawable is used and flag missing content descriptions?
app/src/main/res/drawable/ic_link_basic.xml (1)
6-26: Use color resources/theme attributes instead of hardcoded hex to support dark mode and theming
android:fillColor="#EFF1F5"andandroid:strokeColor="#71788C"are hardcoded. This won’t adapt in dark theme or to dynamic themes.Recommended changes:
- Move colors to
@color/...resources and provide dark-mode variants invalues-night.- Optionally use theme attrs (e.g.,
?attr/colorOnSurfaceVariant) if they match your design system.Example:
- android:fillColor="#EFF1F5"/> + android:fillColor="@color/icon_link_bg"/> ... - android:strokeColor="#71788C" + android:strokeColor="@color/icon_link_stroke"Then define
icon_link_bgandicon_link_strokeinvaluesandvalues-night.Not blocking merge if you have a static light-only palette, but this will improve UX and consistency.
app/src/main/java/com/project/jangburich/api/request/group/CreateGroupRequest.kt (1)
3-7: ValidateteamTypedomain; consider a type-safe representation
teamTypeasStringrisks invalid values being sent. If it’s an enum on the backend, model it as a Kotlin enum (with proper JSON annotations) or a sealed class to catch errors at compile time.If an enum is feasible:
enum class TeamType { FRIENDS, FAMILY, COMPANY /* ... */ } data class CreateGroupRequest( val teamType: TeamType, val teamName: String, val description: String? = null )Add appropriate serialization (e.g., Moshi’s
@Json(name = "...")) to match the backend’s expected strings.app/src/main/res/drawable/background_gray30_round10.xml (1)
6-6: Avoid hardcoding corner radius; centralize in dimens for consistencyHardcoding
10dpduplicates design tokens and makes global changes harder.Preferred:
- <corners android:radius="10dp"/> + <corners android:radius="@dimen/corner_radius_10dp"/>Add to
res/values/dimens.xml:<resources> <dimen name="corner_radius_10dp">10dp</dimen> </resources>app/src/main/res/layout/toast_basic.xml (1)
1-34: Consider toast layout accessibility and reusability.The layout structure is well-designed with proper data binding setup. However, there are a few areas for improvement:
- The
android:gravity="start"on Line 11 is redundant sinceLinearLayoutwithhorizontalorientation already aligns children to start by default.- Consider adding
android:contentDescriptionto theImageViewfor accessibility.Apply these improvements:
<LinearLayout android:id="@+id/layout_tooltip" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" android:background="@drawable/background_gray30_round10" - android:gravity="start" android:orientation="horizontal" android:paddingVertical="14dp" android:paddingHorizontal="15dp" android:visibility="visible"> <ImageView android:id="@+id/imageView_tooltip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginEnd="10dp" + android:contentDescription="@string/toast_icon_description" android:src="@drawable/ic_check_selected" />app/src/main/java/com/project/jangburich/ui/group/CreateGroupInviteFragment.kt (1)
40-46: Placeholder implementations need completion.The
buttonLinkKakaoandbuttonLinkBasicclick listeners contain only placeholder comments. These should either be implemented or temporarily disabled to avoid confusing user interactions.Would you like me to help implement these invite functionalities or should these buttons be temporarily disabled until the features are ready?
app/src/main/java/com/project/jangburich/ui/group/CreateGroupInfoFragment.kt (1)
74-79: Simplify button enable logicThe enable check can be simplified using Kotlin's extension functions.
Apply this diff to simplify the logic:
- if(editTextName.text.isNotEmpty()) { - buttonNext.isEnabled = true - } else { - buttonNext.isEnabled = false - } + buttonNext.isEnabled = editTextName.text.isNotEmpty()app/src/main/java/com/project/jangburich/ui/group/EnterGroupInfoFragment.kt (1)
59-59: Extract string resourceHardcoded string should be moved to string resources.
-textViewTitle.text = "그룹 입장하기" +textViewTitle.text = getString(R.string.enter_group_title)app/src/main/java/com/project/jangburich/ui/BasicToast.kt (2)
49-56: Potential memory leak if view is detached before timeoutThe OnAttachStateChangeListener properly dismisses the popup when the view is detached, but the postDelayed callback might still try to dismiss an already dismissed popup.
Consider canceling the delayed runnable when the view is detached:
+ val dismissRunnable = Runnable { + if (popupWindow.isShowing) { + popupWindow.dismiss() + } + } + // 뷰가 detach될 때 PopupWindow 해제 anchorView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) {} override fun onViewDetachedFromWindow(v: View) { if (popupWindow.isShowing) { popupWindow.dismiss() } + anchorView.removeCallbacks(dismissRunnable) } }) - binding.root.postDelayed({ - if (popupWindow.isShowing) { - popupWindow.dismiss() - } - }, 2000) + anchorView.postDelayed(dismissRunnable, 2000)
87-88: Remove unnecessary commentThe Korean comment at line 87 appears to be a development note that should be removed.
- // 💡 여기 변경: anchorView 위치 기반이 아닌 화면 전체 기준 popupWindow.showAtLocation(anchorView, Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL, 0, 30.toPx())app/src/main/java/com/project/jangburich/ui/group/CreateGroupCategroyFragment.kt (1)
88-98: Consider using enum or constants for category valuesThe category string mappings are hardcoded. Consider using an enum or constants for better maintainability.
Create a companion object with constants:
companion object { private const val CATEGORY_INDIVIDUAL = "INDIVIDUAL" private const val CATEGORY_GATHERING = "GATHERING" private const val CATEGORY_FAMILY = "FAMILY" private const val CATEGORY_COMPANY = "COMPANY" private const val CATEGORY_CLUB = "CLUB" private const val CATEGORY_ALUMNI = "ALUMNI" private const val CATEGORY_STUDENT_ASSOCIATION = "STUDENT_ASSOCIATION" private const val CATEGORY_OTHER = "OTHER" }Then use in the when expression:
category = when (index) { - 0 -> "INDIVIDUAL" + 0 -> CATEGORY_INDIVIDUAL // ... etc }app/src/main/java/com/project/jangburich/ui/group/EnterGroupCodeFragment.kt (2)
40-42: Unnecessary variable declarationThe
nextFragmentvariable is declared asvarbut never reassigned. It can be simplified.-var nextFragment = EnterGroupInfoFragment().apply { +val nextFragment = EnterGroupInfoFragment().apply { arguments = bundle }
51-53: Consider adding input validationThe text change listener updates the character count but doesn't validate the input. Consider adding validation for the 10-character limit and enabling/disabling the Next button accordingly.
editTextCode.addTextChangedListener { textViewCodeCount.text = "${editTextCode.text.length}/10" + val isValidLength = editTextCode.text.length == 10 + buttonNext.isEnabled = isValidLength + if (editTextCode.text.length > 10) { + editTextCode.setText(editTextCode.text.substring(0, 10)) + editTextCode.setSelection(10) + } }app/src/main/res/layout/fragment_create_group_invite.xml (2)
62-62: Use margin start/end for RTL supportThe button uses
android:layout_marginRightwhich doesn't support RTL layouts properly.-android:layout_marginRight="15dp" +android:layout_marginEnd="15dp"
89-119: Consider extracting repeated button layouts into a style or custom viewThe three invitation button layouts have identical structure with only the IDs, icons, and text differing. This could be simplified using a custom view or included layout with parameters.
Consider creating a reusable component
layout_invite_button.xml:<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/background_group_unselected" android:paddingHorizontal="5dp" android:paddingVertical="10dp"> <ImageView android:id="@+id/imageView_icon" android:layout_width="25dp" android:layout_height="25dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView_label" style="@style/body5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="6dp" android:textAlignment="center" android:textColor="@color/gray_30" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/imageView_icon" /> </androidx.constraintlayout.widget.ConstraintLayout>Then use data binding or programmatically set the values in the fragment. This would reduce code duplication and make maintenance easier.
Also applies to: 126-155, 162-191
app/src/main/res/layout/fragment_enter_group_info.xml (2)
87-92: Inconsistent margin values between avatarsThe avatar images have
android:layout_marginEnd="17dp"but the last one has bothandroid:layout_marginStart="17dp"andandroid:layout_marginEnd="5dp". This creates uneven spacing.Consider using consistent spacing between all avatars:
<de.hdodenhof.circleimageview.CircleImageView android:id="@+id/imageView_group_member_profile3" android:layout_width="22dp" android:layout_height="22dp" - android:layout_marginStart="17dp" - android:layout_marginEnd="5dp" + android:layout_marginEnd="8dp"Also applies to: 98-103, 110-117
159-159: Use margin start instead of margin left for RTL supportThe button uses
android:layout_marginLeftwhich doesn't support RTL layouts.-android:layout_marginLeft="12dp" +android:layout_marginStart="12dp"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (27)
app/src/main/java/com/project/jangburich/MainUtil.kt(1 hunks)app/src/main/java/com/project/jangburich/MyApplication.kt(0 hunks)app/src/main/java/com/project/jangburich/api/request/group/CreateGroupRequest.kt(1 hunks)app/src/main/java/com/project/jangburich/api/response/group/CreateGroupResponse.kt(1 hunks)app/src/main/java/com/project/jangburich/ui/BasicToast.kt(1 hunks)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt(0 hunks)app/src/main/java/com/project/jangburich/ui/group/CreateGroupCategroyFragment.kt(4 hunks)app/src/main/java/com/project/jangburich/ui/group/CreateGroupCompleteFragment.kt(2 hunks)app/src/main/java/com/project/jangburich/ui/group/CreateGroupInfoFragment.kt(3 hunks)app/src/main/java/com/project/jangburich/ui/group/CreateGroupInviteFragment.kt(4 hunks)app/src/main/java/com/project/jangburich/ui/group/EditGroupCompleteFragment.kt(1 hunks)app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt(0 hunks)app/src/main/java/com/project/jangburich/ui/group/EnterGroupCodeFragment.kt(2 hunks)app/src/main/java/com/project/jangburich/ui/group/EnterGroupInfoFragment.kt(1 hunks)app/src/main/java/com/project/jangburich/ui/group/viewModel/GroupViewModel.kt(6 hunks)app/src/main/res/drawable/background_gray30_round10.xml(1 hunks)app/src/main/res/drawable/ic_link_basic.xml(1 hunks)app/src/main/res/drawable/ic_link_code.xml(1 hunks)app/src/main/res/drawable/ic_link_kakao.xml(1 hunks)app/src/main/res/layout/fragment_create_group_categroy.xml(17 hunks)app/src/main/res/layout/fragment_create_group_complete.xml(1 hunks)app/src/main/res/layout/fragment_create_group_info.xml(2 hunks)app/src/main/res/layout/fragment_create_group_invite.xml(3 hunks)app/src/main/res/layout/fragment_edit_group_complete.xml(1 hunks)app/src/main/res/layout/fragment_enter_group_code.xml(1 hunks)app/src/main/res/layout/fragment_enter_group_info.xml(2 hunks)app/src/main/res/layout/toast_basic.xml(1 hunks)
💤 Files with no reviewable changes (3)
- app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt
- app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt
- app/src/main/java/com/project/jangburich/MyApplication.kt
🧰 Additional context used
🧬 Code Graph Analysis (11)
app/src/main/java/com/project/jangburich/api/response/group/CreateGroupResponse.kt (5)
app/src/main/java/com/project/jangburich/api/response/group/GetGroupResponse.kt (1)
teamId(3-12)app/src/main/java/com/project/jangburich/api/response/group/GetGroupDetailResponse.kt (2)
storeId(20-26)teamId(3-18)app/src/main/java/com/project/jangburich/api/response/group/GetGroupInfoWithCodeResponse.kt (1)
teamName(3-10)app/src/main/java/com/project/jangburich/api/response/group/GetGroupStoreDetailResponse.kt (1)
storeId(3-17)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)
app/src/main/res/layout/toast_basic.xml (2)
app/src/main/java/com/project/jangburich/ui/store/OrderCompleteFragment.kt (2)
showToolTip(77-105)balloon(78-104)app/src/main/java/com/project/jangburich/ui/store/StoreMapFragment.kt (1)
showToolTip(132-159)
app/src/main/java/com/project/jangburich/ui/group/EditGroupCompleteFragment.kt (4)
app/src/main/java/com/project/jangburich/ui/group/PrePaymentCompleteFragment.kt (1)
binding(12-38)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (1)
binding(17-69)app/src/main/java/com/project/jangburich/ui/group/GroupDetailFragment.kt (2)
binding(23-156)onCreateView(32-109)
app/src/main/java/com/project/jangburich/ui/group/CreateGroupInviteFragment.kt (2)
app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (1)
binding(17-69)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)
app/src/main/java/com/project/jangburich/api/request/group/CreateGroupRequest.kt (2)
app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)app/src/main/java/com/project/jangburich/api/response/group/GetGroupDetailResponse.kt (1)
teamId(3-18)
app/src/main/java/com/project/jangburich/ui/group/CreateGroupCompleteFragment.kt (4)
app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (1)
toolbar(60-67)app/src/main/java/com/project/jangburich/ui/group/GroupDetailFragment.kt (1)
toolbar(147-153)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
mainActivity(56-64)app/src/main/java/com/project/jangburich/ui/group/PrePaymentCompleteFragment.kt (1)
buttonAccept(27-34)
app/src/main/java/com/project/jangburich/ui/group/EnterGroupInfoFragment.kt (4)
app/src/main/java/com/project/jangburich/ui/group/EnterGroupCodeFragment.kt (1)
initView(59-70)app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (2)
binding(17-69)onCreateView(23-57)app/src/main/java/com/project/jangburich/ui/group/GroupFragment.kt (1)
binding(26-115)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)
app/src/main/res/layout/fragment_create_group_info.xml (1)
app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)
app/src/main/java/com/project/jangburich/ui/group/viewModel/GroupViewModel.kt (2)
app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (2)
binding(17-69)viewModel(51-53)app/src/main/java/com/project/jangburich/ui/group/CreateGroupAccountFragment.kt (1)
binding(19-73)
app/src/main/java/com/project/jangburich/ui/group/EnterGroupCodeFragment.kt (3)
app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (1)
binding(17-69)app/src/main/java/com/project/jangburich/ui/group/GroupDetailFragment.kt (1)
binding(23-156)app/src/main/java/com/project/jangburich/ui/group/GroupFragment.kt (1)
binding(26-115)
app/src/main/res/layout/fragment_enter_group_info.xml (2)
app/src/main/java/com/project/jangburich/ui/group/EnterCodeGroupFragment.kt (1)
binding(17-69)app/src/main/java/com/project/jangburich/ui/group/GroupDetailFragment.kt (2)
binding(23-156)onCreateView(32-109)
🔇 Additional comments (15)
app/src/main/res/layout/fragment_create_group_categroy.xml (1)
342-342: Good: Next button disabled by default until selection.This aligns with the updated flow (category nullable, enable on selection). No action needed.
app/src/main/res/layout/fragment_create_group_info.xml (1)
113-113: Enable logic in CreateGroupInfoFragment is correctly wired– In initView(),
buttonNext.isEnabledis initially false.
– You’ve added
•editTextName.addTextChangedListener { checkEnable(); … }
•editTextDescription.addTextChangedListener { … }
– AndcheckEnable()togglesbuttonNext.isEnabled = editTextName.text.isNotEmpty().No dead-end UX here: the Next button enables as soon as the group name is non-empty.
app/src/main/java/com/project/jangburich/ui/group/CreateGroupCompleteFragment.kt (1)
53-53: Consistent navigation pattern with other fragments.Good improvement using
parentFragmentManager.popBackStack()which aligns with the navigation patterns used in other fragments likeCreateGroupInviteFragmentandEditGroupCompleteFragment.app/src/main/res/layout/fragment_edit_group_complete.xml (1)
1-50: Well-structured completion screen layout.The layout follows a clean, consistent structure with:
- Proper constraint relationships for responsive design
- Appropriate margins and padding for visual hierarchy
- Reuse of existing toolbar and styling components
- Clear semantic element naming
The layout correctly references the new fragment in the
tools:contextattribute and uses appropriate Korean text for the completion message.app/src/main/java/com/project/jangburich/ui/group/CreateGroupInviteFragment.kt (2)
75-75: Improved user feedback with BasicToast.Good improvement replacing the standard Android Toast with the custom
BasicToastimplementation. This provides a more consistent and branded user experience with the visual feedback including an icon.
84-84: Consistent navigation pattern improvement.Good change using
parentFragmentManager.popBackStack()which aligns with the consistent navigation pattern being established across all fragment classes in this PR.app/src/main/java/com/project/jangburich/ui/group/EditGroupCompleteFragment.kt (2)
28-28: Clean navigation approach for completion flow.Good use of
POP_BACK_STACK_INCLUSIVEto clear the entire back stack when the user confirms completion. This prevents users from navigating back into the edit flow after completion.
35-46: Consistent fragment structure and navigation.The
initView()method follows the established pattern from other fragments in the project with proper bottom navigation hiding and consistentparentFragmentManagerusage for back navigation.app/src/main/java/com/project/jangburich/ui/group/CreateGroupInfoFragment.kt (1)
20-22: No action needed: Activity-scoped ViewModel usage is already consistentWe verified that every fragment sharing GroupViewModel—including CreateGroupInfoFragment—initializes it via the activity’s ViewModelProvider. Using requireActivity() here aligns with the existing pattern and correctly scopes the ViewModel to the Activity’s lifecycle.
app/src/main/java/com/project/jangburich/ui/group/EnterGroupInfoFragment.kt (1)
18-20: ViewModel initialization pattern matches other fragmentsThe ViewModel initialization using
requireActivity()is consistent with other fragments in the codebase.app/src/main/java/com/project/jangburich/ui/BasicToast.kt (1)
16-64: Well-structured toast utility with proper cleanupThe implementation includes proper cleanup via OnAttachStateChangeListener and auto-dismiss timer. Good use of DataBinding and proper width calculation.
app/src/main/java/com/project/jangburich/ui/group/EnterGroupCodeFragment.kt (1)
19-21: Good refactoring to lazy initializationThe change from a public
lateinit varto a private lazy property is a good improvement. This ensures the ViewModel is initialized only when needed and prevents external modification.app/src/main/res/layout/fragment_create_group_invite.xml (2)
24-24: Good UX improvement with clearer instructionsThe updated text provides better clarity about the randomly generated secret code, improving user understanding of the invitation process.
79-192: Well-structured invitation options layoutThe three invitation methods (KakaoTalk, Link Copy, Secret Code Copy) are well-organized with consistent styling and proper spacing. The use of
ConstraintLayoutwithinLinearLayoutwith weights ensures equal distribution.app/src/main/res/layout/fragment_enter_group_info.xml (1)
9-9: Context reference correctly updatedThe tools:context has been properly updated to reference the new
EnterGroupInfoFragment.
| val description: String, | ||
| val teamLeaderAccountNumber: String, | ||
| val bankName: String | ||
| val description: String? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Make description default to null to reduce call-site friction and avoid accidental “null” serialization
With description: String? and no default, all call sites must explicitly pass null when there’s no description. Also, some serializers may include "description": null in the payload which some backends reject.
Apply this diff:
- val description: String?
+ val description: String? = nullVerification:
- Ensure your JSON adapter omits nulls if the backend expects the field to be absent when empty (Moshi can do this with custom adapters or
@JsonClass(generateAdapter = true)and configuration). - Check call sites that construct
CreateGroupRequestso they don’t break on the signature change (should be source-compatible).
Run:
🏁 Script executed:
#!/bin/bash
# Find where CreateGroupRequest is constructed and ensure no stale args (bankName/accountNumber) remain.
rg -n --no-heading $'CreateGroupRequest\\s*\\(' -A 5 -B 2Length of output: 1852
🏁 Script executed:
#!/bin/bash
echo "=== Searching for Moshi configuration ==="
rg -n 'Moshi\.Builder' -A3 -B3
echo "=== Searching for Gson configuration ==="
rg -n 'GsonBuilder' -A3 -B3
echo "=== Searching for Retrofit converter factories ==="
rg -n 'addConverterFactory' -A3 -B3Length of output: 2307
🏁 Script executed:
#!/bin/bash
# Locate the declaration of groupDescription in GroupViewModel.kt
rg -n 'groupDescription' -C2 app/src/main/java/com/project/jangburich/ui/group/viewModel/GroupViewModel.ktLength of output: 571
Add a default null value for description to streamline call sites
The project uses Gson (via GsonConverterFactory.create(gson)) without calling .serializeNulls(), so fields with null values are omitted by default. Introducing a default parameter for description is source-compatible in Kotlin and will not break existing constructors.
• File: app/src/main/java/com/project/jangburich/api/request/group/CreateGroupRequest.kt
Apply:
data class CreateGroupRequest(
val teamType: String,
val teamName: String,
- val description: String?
+ val description: String? = null
)No other call sites need updating (only one in GroupViewModel.kt), and JSON serialization will drop description when it’s null.
🤖 Prompt for AI Agents
In
app/src/main/java/com/project/jangburich/api/request/group/CreateGroupRequest.kt
around line 6, the nullable description property lacks a default value; add a
default of null (e.g., description: String? = null) so callers can omit it,
keeping Gson serialization behavior unchanged and avoiding updates at most call
sites (only GroupViewModel.kt may be affected).
| data class CreateGroupResponse( | ||
| val id: Int, | ||
| val uuid: String | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Unify ID typing and naming across group responses.
Other responses use both Int and Long for teamId (e.g., GetGroupResponse.teamId: Long vs GetGroupDetailResponse.teamId: Int). Introducing CreateGroupResponse.id: Int increases inconsistency and can cause subtle bugs if backend IDs exceed Int or if call sites expect Long. Recommend standardizing IDs to Long and consistent naming (e.g., teamId) across models.
Apply this change here (and align call sites/serialization accordingly):
-data class CreateGroupResponse(
- val id: Int,
- val uuid: String
-)
+data class CreateGroupResponse(
+ val id: Long,
+ val uuid: String
+)Run this to find all occurrences to align types and names:
If you confirm the desired canonical name/type (teamId: Long or id: Long), I can generate a follow-up patch updating models and usages.
🏁 Script executed:
#!/bin/bash
# Find all response models with ids to check types/names
rg -n "data class .*Response" -A 20 | rg -n "id:|teamId:"
# Find usages of CreateGroupResponse.id to ensure type compatibility
rg -n "CreateGroupResponse" -A 5Length of output: 6860
Standardize group ID naming & types to teamId: Long
Current models are inconsistent:
GetHomeDataResponse.kt:teamId: IntCreateGroupResponse.kt:id: IntGetGroupDetailResponse.kt:teamId: IntGetGroupResponse.kt:teamId: Long(already correct)
Please update as follows:
- Change all Int-typed team IDs to
Long - Rename
CreateGroupResponse.id→teamId: Long(add a serialization annotation to map JSON"id"if needed) - Adjust any call sites (e.g., in
ApiService,GroupViewModel) to useteamIdinstead ofid
Example diff for CreateGroupResponse.kt:
+ import com.google.gson.annotations.SerializedName
data class CreateGroupResponse(
- val id: Int,
+ @SerializedName("id") val teamId: Long,
val uuid: String
)Bullet-point locations to update:
- app/src/main/java/com/project/jangburich/api/response/home/GetHomeDataResponse.kt
- app/src/main/java/com/project/jangburich/api/response/group/CreateGroupResponse.kt
- app/src/main/java/com/project/jangburich/api/response/group/GetGroupDetailResponse.kt
- Verify
ApiService.ktandui/group/viewModel/GroupViewModel.ktreferences to useteamId: Long
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| data class CreateGroupResponse( | |
| val id: Int, | |
| val uuid: String | |
| ) | |
| import com.google.gson.annotations.SerializedName | |
| data class CreateGroupResponse( | |
| @SerializedName("id") val teamId: Long, | |
| val uuid: String | |
| ) |
🤖 Prompt for AI Agents
In
app/src/main/java/com/project/jangburich/api/response/group/CreateGroupResponse.kt
around lines 3-6, rename the Int field id to teamId of type Long and add a
serialization annotation mapping the JSON "id" to the new property (e.g.,
@SerializedName("id") or equivalent used in the project); then change all other
group/team ID fields across the codebase from Int to Long
(GetHomeDataResponse.kt, GetGroupDetailResponse.kt) and update any references in
ApiService.kt and ui/group/viewModel/GroupViewModel.kt to use the new teamId:
Long property name and type so call sites compile and JSON mapping remains
correct.
|
|
||
| object MainUtil { | ||
|
|
||
| fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).roundToInt() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Member extension scope bug: Int.toPx() inside MainUtil won’t be callable as a normal extension from other files
An extension declared as a member of an object requires that object as a dispatch receiver. You can’t call 16.toPx() from arbitrary code unless you’re inside with(MainUtil) { ... } or similar. Given the PR mentions BasicToast uses this extension, this likely won’t compile or will be cumbersome to use.
Recommend moving it to a top-level extension (or at least make usage explicit).
Apply this diff to remove the member extension:
- fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).roundToInt()Add this top-level extension in the same file (outside the MainUtil object):
package com.project.jangburich
import android.content.res.Resources
import kotlin.math.roundToInt
// dp -> px
fun Int.dpToPx(resources: Resources = Resources.getSystem()): Int =
(this * resources.displayMetrics.density).roundToInt()Follow-up:
- Update call sites to
16.dpToPx(context.resources)(preferred) or16.dpToPx()if you explicitly want system resources. - If you must keep the original name, use
toPxinstead ofdpToPx, but be explicit in KDoc that it converts dp to px.
🤖 Prompt for AI Agents
In app/src/main/java/com/project/jangburich/MainUtil.kt around line 14, the
Int.toPx() extension is currently declared as a member of the MainUtil object
which makes it callable only with that dispatch receiver; remove the member
extension from MainUtil and add a top-level extension function (outside the
object) that accepts an optional Resources parameter (defaulting to
Resources.getSystem()) and uses displayMetrics.density with roundToInt to
convert dp to px, name it dpToPx (or keep toPx if you prefer but update KDoc),
and then update all call sites (e.g., BasicToast) to use
16.dpToPx(context.resources) or 16.dpToPx() if system resources are intended.
🛠️ Refactor suggestion
Use context/display resources instead of Resources.getSystem() by default
Resources.getSystem() may not reflect per-display density or overridden configurations. Prefer passing context.resources (or resources from a View/Fragment/Activity) to be accurate.
If you retain the function name, adjust the signature to accept resources, with a sane default:
- fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).roundToInt()
+ fun Int.toPx(resources: Resources = Resources.getSystem()): Int =
+ (this * resources.displayMetrics.density).roundToInt()Note: This still suffers from the “member extension” scoping issue above; best combined with making it top-level.
| val bundle = Bundle().apply { | ||
| putString("category", category) | ||
| putBoolean("isEdit", arguments?.getBoolean("isEdit") == true) | ||
| } | ||
|
|
||
| val nextFragment = CreateGroupInfoFragment() | ||
| var nextFragment = CreateGroupInfoFragment().apply { | ||
| arguments = bundle | ||
| } | ||
|
|
||
| val transaction = mainActivity.manager.beginTransaction() | ||
| transaction.replace(R.id.fragmentContainerView_main, nextFragment) | ||
| transaction.addToBackStack(null) | ||
| transaction.commit() | ||
| mainActivity.supportFragmentManager.beginTransaction() | ||
| .replace(R.id.fragmentContainerView_main, nextFragment) | ||
| .addToBackStack(null) | ||
| .commit() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add null safety check for category before navigation
The Next button can be enabled, but category might still be null if no selection was made (though unlikely with current flow).
buttonNext.setOnClickListener {
+ if (category == null) {
+ // Handle no category selected
+ return@setOnClickListener
+ }
+
val bundle = Bundle().apply {
putString("category", category)
putBoolean("isEdit", arguments?.getBoolean("isEdit") == true)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| val bundle = Bundle().apply { | |
| putString("category", category) | |
| putBoolean("isEdit", arguments?.getBoolean("isEdit") == true) | |
| } | |
| val nextFragment = CreateGroupInfoFragment() | |
| var nextFragment = CreateGroupInfoFragment().apply { | |
| arguments = bundle | |
| } | |
| val transaction = mainActivity.manager.beginTransaction() | |
| transaction.replace(R.id.fragmentContainerView_main, nextFragment) | |
| transaction.addToBackStack(null) | |
| transaction.commit() | |
| mainActivity.supportFragmentManager.beginTransaction() | |
| .replace(R.id.fragmentContainerView_main, nextFragment) | |
| .addToBackStack(null) | |
| .commit() | |
| } | |
| buttonNext.setOnClickListener { | |
| if (category == null) { | |
| // Handle no category selected, e.g. show a Toast or Snackbar | |
| return@setOnClickListener | |
| } | |
| val bundle = Bundle().apply { | |
| putString("category", category) | |
| putBoolean("isEdit", arguments?.getBoolean("isEdit") == true) | |
| } | |
| var nextFragment = CreateGroupInfoFragment().apply { | |
| arguments = bundle | |
| } | |
| mainActivity.supportFragmentManager.beginTransaction() | |
| .replace(R.id.fragmentContainerView_main, nextFragment) | |
| .addToBackStack(null) | |
| .commit() | |
| } |
🤖 Prompt for AI Agents
In
app/src/main/java/com/project/jangburich/ui/group/CreateGroupCategroyFragment.kt
around lines 103 to 116, add a null-safety guard for the category before
building the Bundle and navigating: check if category is null (or blank) and if
so show a user-facing message (e.g., Toast or Snackbar) and return early; only
create the Bundle, set arguments, and commit the fragment transaction when
category is non-null, to avoid passing a null value into the next fragment and
potential NPEs.
| textViewIntro.text = | ||
| if(arguments?.getBoolean("isEdit") == true) { | ||
| "그룹의 유형을\n수정해주세요" | ||
| } else { | ||
| "생성할 그룹의\n유형을 선택해주세요" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Extract string resources for better localization
Hardcoded strings should be moved to string resources.
textViewIntro.text =
if(arguments?.getBoolean("isEdit") == true) {
- "그룹의 유형을\n수정해주세요"
+ getString(R.string.group_category_edit_intro)
} else {
- "생성할 그룹의\n유형을 선택해주세요"
+ getString(R.string.group_category_create_intro)
}| android:layout_width="match_parent" | ||
| android:layout_height="match_parent" | ||
| android:layout_weight="1" | ||
| android:background="@drawable/background_group_unselected" | ||
| android:paddingHorizontal="25dp" | ||
| android:paddingHorizontal="5dp" | ||
| android:paddingVertical="19dp"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Incorrect use of layout_weight with match_parent width in a horizontal LinearLayout.
In a horizontal LinearLayout, children participating in weight distribution must use width=0dp with layout_weight set. Using match_parent will cause measurement/layout issues (overlap, clipping, or uneven sizing). Set width=0dp for all weighted buttons.
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"
...
- android:layout_width="match_parent"
+ android:layout_width="0dp"If you prefer ConstraintLayout, an alternative is a 4-element horizontal chain with spreadInside and 7dp spacers as margins instead of Space views.
Also applies to: 89-95, 124-129, 159-164, 201-206, 236-241, 271-276, 306-311
🤖 Prompt for AI Agents
In app/src/main/res/layout/fragment_create_group_categroy.xml around lines 54-59
(and also update the same pattern at 89-95, 124-129, 159-164, 201-206, 236-241,
271-276, 306-311), the children inside a horizontal LinearLayout are using
android:layout_width="match_parent" together with android:layout_weight="1",
which is invalid and causes layout issues; change each weighted child to
android:layout_width="0dp" while keeping android:layout_weight="1" so they share
available horizontal space correctly (or alternatively convert the row to a
ConstraintLayout horizontal chain with spreadInside and 7dp spacing if you
prefer that approach).
| android:layout_marginTop="10dp" | ||
| android:ems="10" | ||
| android:maxLength="100" | ||
| android:inputType="text" | ||
| android:gravity="top" | ||
| android:hint="간단한 그룹 설명을 입력해주세요 (선택)" | ||
| android:inputType="text|textMultiLine" | ||
| android:lines="3" | ||
| android:maxLength="100" | ||
| android:maxLines="3" | ||
| android:minLines="3" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Multi-line EditText conflicts with “OneLineEditText” style and fixed height
- The style named
OneLineEditTextlikely enforces single-line paddings/lineHeight, which can fight withtextMultiLine. android:layout_height="100dp"pluslines/minLines/maxLinescauses inconsistent rendering across densities and fonts.
Recommended changes:
- Use a multi-line appropriate style (or override single-line attributes in this view).
- Let the content determine height; avoid fixed dp height for text fields.
Proposed XML updates (outside this hunk, but applied to this view):
<!-- Prefer wrap_content and remove ems for clarity -->
android:layout_height="wrap_content"
android:minLines="3"
android:maxLines="3"
android:lines="3"If OneLineEditText style sets singleLine=true or maxLines=1, consider a new style (e.g., @style/MultiLineEditText) for this view.
Also good to add:
android:scrollbars="vertical"android:overScrollMode="ifContentScrolls"android:imeOptions="flagNoEnterAction"
🤖 Prompt for AI Agents
In app/src/main/res/layout/fragment_create_group_info.xml around lines 82 to 90,
the EditText is using a OneLineEditText style and a fixed height which conflicts
with textMultiLine and lines/minLines/maxLines; replace the single-line style
with a multi-line appropriate style (or create @style/MultiLineEditText that
does not set singleLine/maxLines=1), remove the fixed
android:layout_height="100dp" and android:ems attribute and set
android:layout_height="wrap_content" together with android:minLines="3"
android:lines="3" android:maxLines="3"; additionally add
android:scrollbars="vertical" android:overScrollMode="ifContentScrolls" and
android:imeOptions="flagNoEnterAction" (or explicitly override
singleLine/lineHeight attributes from the OneLineEditText style) so the field
renders consistently and scrolls correctly.
| <TextView | ||
| android:id="@+id/textView_code_error" | ||
| android:layout_width="wrap_content" | ||
| android:layout_height="wrap_content" | ||
| android:layout_marginTop="5dp" | ||
| android:textColor="@color/main_orange" | ||
| android:text="영문, 숫자 포함 5~10자 이내로 입력해주세요" | ||
| app:layout_constraintStart_toStartOf="@+id/editText_code" | ||
| app:layout_constraintTop_toBottomOf="@+id/editText_code" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error message is always visible; hide by default and toggle on validation.
Currently this shows an error in orange before any user input. Hide it initially and reveal on invalid input. Optionally link it to the input for accessibility.
<TextView
android:id="@+id/textView_code_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textColor="@color/main_orange"
android:text="영문, 숫자 포함 5~10자 이내로 입력해주세요"
+ android:visibility="gone"
+ android:labelFor="@id/editText_code"
+ tools:visibility="visible"
app:layout_constraintStart_toStartOf="@+id/editText_code"
app:layout_constraintTop_toBottomOf="@+id/editText_code" />Consider using TextInputLayout to leverage built-in helper/error text behavior and accessibility semantics.
| <de.hdodenhof.circleimageview.CircleImageView | ||
| android:id="@+id/imageView_group_member_profile1" | ||
| android:layout_width="22dp" | ||
| android:layout_height="22dp" | ||
| android:layout_marginEnd="17dp" | ||
| android:src="@drawable/example" | ||
| app:layout_constraintBottom_toBottomOf="parent" | ||
| app:layout_constraintEnd_toEndOf="@+id/imageView_group_member_profile2" | ||
| app:layout_constraintTop_toTopOf="parent" | ||
| app:layout_constraintVertical_bias="0.0" /> | ||
|
|
||
| <de.hdodenhof.circleimageview.CircleImageView | ||
| android:id="@+id/imageView_group_member_profile2" | ||
| android:layout_width="22dp" | ||
| android:layout_height="22dp" | ||
| android:layout_marginEnd="17dp" | ||
| android:src="@drawable/example" | ||
| app:layout_constraintBottom_toBottomOf="@+id/imageView_group_member_profile1" | ||
| app:layout_constraintEnd_toEndOf="@+id/imageView_group_member_profile3" | ||
| app:layout_constraintTop_toTopOf="@+id/imageView_group_member_profile1" | ||
| app:layout_constraintVertical_bias="0.0" /> | ||
|
|
||
| <de.hdodenhof.circleimageview.CircleImageView | ||
| android:id="@+id/imageView_group_member_profile3" | ||
| android:layout_width="22dp" | ||
| android:layout_height="22dp" | ||
| android:layout_marginStart="17dp" | ||
| android:layout_marginEnd="5dp" | ||
| android:src="@drawable/example" | ||
| app:layout_constraintBottom_toBottomOf="@+id/imageView_group_member_profile2" | ||
| app:layout_constraintEnd_toStartOf="@+id/textView_group_member_num" | ||
| app:layout_constraintHorizontal_bias="1.0" | ||
| app:layout_constraintStart_toStartOf="@+id/imageView_group_member_profile2" | ||
| app:layout_constraintTop_toTopOf="@+id/imageView_group_member_profile2" | ||
| app:layout_constraintVertical_bias="0.0" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Complex constraint chains in avatar layout
The avatar images use a complex chain of constraints where each image is constrained to the next one. This creates a fragile layout that could break if any element is removed or hidden.
Consider using a ChainStyle or a simpler horizontal arrangement:
-<de.hdodenhof.circleimageview.CircleImageView
- android:id="@+id/imageView_group_member_profile1"
- android:layout_width="22dp"
- android:layout_height="22dp"
- android:layout_marginEnd="17dp"
- android:src="@drawable/example"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@+id/imageView_group_member_profile2"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintVertical_bias="0.0" />
+<de.hdodenhof.circleimageview.CircleImageView
+ android:id="@+id/imageView_group_member_profile1"
+ android:layout_width="22dp"
+ android:layout_height="22dp"
+ android:src="@drawable/example"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@+id/imageView_group_member_profile2"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toEndOf="@+id/textView_group_type"
+ app:layout_constraintTop_toTopOf="parent" />Apply similar changes to the other profile images to create a proper horizontal chain.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <de.hdodenhof.circleimageview.CircleImageView | |
| android:id="@+id/imageView_group_member_profile1" | |
| android:layout_width="22dp" | |
| android:layout_height="22dp" | |
| android:layout_marginEnd="17dp" | |
| android:src="@drawable/example" | |
| app:layout_constraintBottom_toBottomOf="parent" | |
| app:layout_constraintEnd_toEndOf="@+id/imageView_group_member_profile2" | |
| app:layout_constraintTop_toTopOf="parent" | |
| app:layout_constraintVertical_bias="0.0" /> | |
| <de.hdodenhof.circleimageview.CircleImageView | |
| android:id="@+id/imageView_group_member_profile2" | |
| android:layout_width="22dp" | |
| android:layout_height="22dp" | |
| android:layout_marginEnd="17dp" | |
| android:src="@drawable/example" | |
| app:layout_constraintBottom_toBottomOf="@+id/imageView_group_member_profile1" | |
| app:layout_constraintEnd_toEndOf="@+id/imageView_group_member_profile3" | |
| app:layout_constraintTop_toTopOf="@+id/imageView_group_member_profile1" | |
| app:layout_constraintVertical_bias="0.0" /> | |
| <de.hdodenhof.circleimageview.CircleImageView | |
| android:id="@+id/imageView_group_member_profile3" | |
| android:layout_width="22dp" | |
| android:layout_height="22dp" | |
| android:layout_marginStart="17dp" | |
| android:layout_marginEnd="5dp" | |
| android:src="@drawable/example" | |
| app:layout_constraintBottom_toBottomOf="@+id/imageView_group_member_profile2" | |
| app:layout_constraintEnd_toStartOf="@+id/textView_group_member_num" | |
| app:layout_constraintHorizontal_bias="1.0" | |
| app:layout_constraintStart_toStartOf="@+id/imageView_group_member_profile2" | |
| app:layout_constraintTop_toTopOf="@+id/imageView_group_member_profile2" | |
| app:layout_constraintVertical_bias="0.0" /> | |
| <de.hdodenhof.circleimageview.CircleImageView | |
| android:id="@+id/imageView_group_member_profile1" | |
| android:layout_width="22dp" | |
| android:layout_height="22dp" | |
| android:src="@drawable/example" | |
| app:layout_constraintBottom_toBottomOf="parent" | |
| app:layout_constraintEnd_toStartOf="@+id/imageView_group_member_profile2" | |
| app:layout_constraintHorizontal_chainStyle="packed" | |
| app:layout_constraintStart_toEndOf="@+id/textView_group_type" | |
| app:layout_constraintTop_toTopOf="parent" /> |
| android:layout_width="22dp" | ||
| android:layout_height="22dp" | ||
| android:layout_marginEnd="17dp" | ||
| android:src="@drawable/example" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace hardcoded example drawable with proper placeholder
The avatar images use @drawable/example which appears to be a hardcoded test resource. This should be replaced with a proper placeholder or default avatar image.
-android:src="@drawable/example"
+android:src="@drawable/ic_default_avatar"Also ensure the actual user avatars are loaded programmatically in the fragment code.
Also applies to: 99-99, 111-111
🤖 Prompt for AI Agents
In app/src/main/res/layout/fragment_enter_group_info.xml around lines 88, 99 and
111 the layout uses a hardcoded test drawable (@drawable/example) for avatar
ImageView(s); replace those references with a proper placeholder/default avatar
drawable (e.g., @drawable/ic_avatar_placeholder) and remove any test-only
resource usage, then update the corresponding fragment/adapter code to load real
user avatars programmatically (using Glide/Picasso/Coil or similar) with the
placeholder as the fallback and call imageView.setImageResource for local
defaults only when no remote avatar exists.
🔎 What type of PR is this? (check all applicable)
🐥 What need this change?
📝 Changes
📷 Screenshots, Recordings (optional)
Summary by CodeRabbit
New Features
Improvements
UI