Skip to content

Conversation

@PIYUJIN
Copy link
Member

@PIYUJIN PIYUJIN commented Aug 12, 2025

🔎 What type of PR is this? (check all applicable)

  • Feature
  • Bug Fix
  • Refactor
  • Optimization
  • Documentation Update
  • Others: ...

🐥 What need this change?

📝 Changes

  • 그룹 생성 화면 구성 및 기능 구현
  • 그룹 수정 화면 구성 및 기능 구현
  • 그룹 입장 화면 구성 및 기능 구현

📷 Screenshots, Recordings (optional)

Summary by CodeRabbit

  • New Features

    • In-app toast notifications with icons.
    • Invite options after group creation: KakaoTalk, copy link, copy code.
    • “Edit Group Complete” screen.
    • Revamped group entry info screen after entering a code.
  • Improvements

    • Simplified group creation: optional description, multi-line input, clearer prompts; removed account/bank step.
    • Category selection UX: full-width options; Next enabled after selection.
    • Enter group flow: trimmed code input, helper message, smoother navigation; bottom tab switches to Group.
    • After creation, navigate directly to group details.
  • UI

    • Updated texts, icons, and layouts on invite and entry screens.

@PIYUJIN PIYUJIN self-assigned this Aug 12, 2025
@PIYUJIN PIYUJIN added the ✨ feat 신규 기능 구현 작업 label Aug 12, 2025
@PIYUJIN PIYUJIN linked an issue Aug 12, 2025 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link

coderabbitai bot commented Aug 12, 2025

Walkthrough

Refactors 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

Cohort / File(s) Summary of Changes
Utilities
app/.../MainUtil.kt, app/.../ui/BasicToast.kt
Added Int.toPx() extension; introduced BasicToast popup-based toast helper with top/bottom variants.
Toast assets
app/src/main/res/layout/toast_basic.xml, app/src/main/res/drawable/background_gray30_round10.xml, app/src/main/res/drawable/ic_link_basic.xml, app/src/main/res/drawable/ic_link_code.xml, app/src/main/res/drawable/ic_link_kakao.xml
New layout and drawable resources to support custom toast and invite actions.
Global state cleanup
app/.../MyApplication.kt
Removed multiple public companion object variables (group, prepay, order, code-related).
API models
app/.../api/request/group/CreateGroupRequest.kt, app/.../api/response/group/CreateGroupResponse.kt
Request: removed account/bank fields; description nullable. Response: added id to constructor.
Group creation flow (UI)
app/.../ui/group/CreateGroupCategroyFragment.kt, app/.../ui/group/CreateGroupInfoFragment.kt, app/.../ui/group/CreateGroupInviteFragment.kt, app/.../ui/group/CreateGroupCompleteFragment.kt
Shifted from global state to arguments/ViewModel; updated navigation to supportFragmentManager; Invite uses BasicToast; adjusted back handling.
Group creation layouts
app/.../res/layout/fragment_create_group_categroy.xml, app/.../res/layout/fragment_create_group_info.xml, app/.../res/layout/fragment_create_group_invite.xml, app/.../res/layout/fragment_create_group_complete.xml
Category buttons full-width; Next initially disabled; description multi-line; invite row with Kakao/Link/Code; minor text change.
Group edit complete
app/.../ui/group/EditGroupCompleteFragment.kt, app/.../res/layout/fragment_edit_group_complete.xml
New fragment and layout; handles back stack clearing and toolbar back.
Enter group flow refactor
app/.../ui/group/EnterGroupCodeFragment.kt, app/.../ui/group/EnterGroupInfoFragment.kt, app/.../ui/group/EnterCodeGroupFragment.kt (removed), app/.../res/layout/fragment_enter_group_code.xml, app/.../res/layout/fragment_enter_group_info.xml
Replaced removed EnterCodeGroupFragment with EnterGroupInfoFragment; EnterGroupCodeFragment now trims code, navigates on success; layouts restructured and error text enabled.
ViewModel callbacks
app/.../ui/group/viewModel/GroupViewModel.kt
Methods now accept parameters and onSuccess callbacks; removed internal navigation and global state writes.
CreateGroupAccount
app/.../ui/group/CreateGroupAccountFragment.kt
Removed Next button logic (no group creation/global writes).

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related issues

Possibly related PRs

  • [Feat]  #4: Also removes globals from MyApplication.kt; related refactor of shared state.

Suggested labels

♻️ refactor

Poem

hop-hop! I trimmed the globals down,
stitched flows with callbacks—no more frown.
A toast pops up with gentle glow,
codes invite friends—away we go!
New paths to groups, neat and tight—
ears up, I ship this change tonight. 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch Feat/#5

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a 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 LinearLayout

With 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 margins

Please move every literal android:text="…" in 
app/src/main/res/layout—including the two TextViews in
fragment_create_group_complete.xml (lines 20–38) and the “확인” button (lines 69–80)—into strings.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. in fragment_login_main.xml, fragment_home.xml, row_store_list.xml, etc.). Swap each for android:layout_marginStart="…dp" (and use layout_marginEnd where appropriate) to ensure proper RTL support.

• fragment_create_group_complete.xml
– Lines 20–38: hardcoded android:text
– Lines 69–80: hardcoded “확인” + layout_marginLeft at line 77
• All other layouts under app/src/main/res/layout using:
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/layout
app/src/main/java/com/project/jangburich/MainUtil.kt (1)

35-41: Bug: top padding is overwritten; status bar padding is lost

Two consecutive setPadding calls 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 logs

The createGroup method has several issues:

  1. Error responses are not propagated to the callback
  2. Sensitive data might be logged in production
  3. Using toString() on nullable UUID without null check

Apply 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 getGroupInfoWithCode

The 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 method

Similar 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 production

Debug 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 state

The 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" and android: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 in values-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_bg and icon_link_stroke in values and values-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: Validate teamType domain; consider a type-safe representation

teamType as String risks 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 consistency

Hardcoding 10dp duplicates 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:

  1. The android:gravity="start" on Line 11 is redundant since LinearLayout with horizontal orientation already aligns children to start by default.
  2. Consider adding android:contentDescription to the ImageView for 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 buttonLinkKakao and buttonLinkBasic click 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 logic

The 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 resource

Hardcoded 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 timeout

The 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 comment

The 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 values

The 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 declaration

The nextFragment variable is declared as var but never reassigned. It can be simplified.

-var nextFragment = EnterGroupInfoFragment().apply {
+val nextFragment = EnterGroupInfoFragment().apply {
     arguments = bundle
 }

51-53: Consider adding input validation

The 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 support

The button uses android:layout_marginRight which 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 view

The 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 avatars

The avatar images have android:layout_marginEnd="17dp" but the last one has both android:layout_marginStart="17dp" and android: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 support

The button uses android:layout_marginLeft which doesn't support RTL layouts.

-android:layout_marginLeft="12dp"
+android:layout_marginStart="12dp"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c966f1c and ea05818.

📒 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.isEnabled is initially false.
– You’ve added
editTextName.addTextChangedListener { checkEnable(); … }
editTextDescription.addTextChangedListener { … }
– And checkEnable() toggles buttonNext.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 like CreateGroupInviteFragment and EditGroupCompleteFragment.

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:context attribute 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 BasicToast implementation. 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_INCLUSIVE to 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 consistent parentFragmentManager usage 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 consistent

We 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 fragments

The 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 cleanup

The 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 initialization

The change from a public lateinit var to 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 instructions

The updated text provides better clarity about the randomly generated secret code, improving user understanding of the invitation process.


79-192: Well-structured invitation options layout

The three invitation methods (KakaoTalk, Link Copy, Secret Code Copy) are well-organized with consistent styling and proper spacing. The use of ConstraintLayout within LinearLayout with weights ensures equal distribution.

app/src/main/res/layout/fragment_enter_group_info.xml (1)

9-9: Context reference correctly updated

The tools:context has been properly updated to reference the new EnterGroupInfoFragment.

val description: String,
val teamLeaderAccountNumber: String,
val bankName: String
val description: String?
Copy link

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? = null

Verification:

  • 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 CreateGroupRequest so 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 2

Length 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 -B3

Length 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.kt

Length 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).

Comment on lines 3 to 6
data class CreateGroupResponse(
val id: Int,
val uuid: String
)
Copy link

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 5

Length of output: 6860


Standardize group ID naming & types to teamId: Long

Current models are inconsistent:

  • GetHomeDataResponse.kt: teamId: Int
  • CreateGroupResponse.kt: id: Int
  • GetGroupDetailResponse.kt: teamId: Int
  • GetGroupResponse.kt: teamId: Long (already correct)

Please update as follows:

  • Change all Int-typed team IDs to Long
  • Rename CreateGroupResponse.idteamId: Long (add a serialization annotation to map JSON "id" if needed)
  • Adjust any call sites (e.g., in ApiService, GroupViewModel) to use teamId instead of id

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.kt and ui/group/viewModel/GroupViewModel.kt references to use teamId: 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.

Suggested change
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()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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) or 16.dpToPx() if you explicitly want system resources.
  • If you must keep the original name, use toPx instead of dpToPx, 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.

Comment on lines +103 to 116
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()
}
Copy link

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.

Suggested change
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.

Comment on lines +126 to +131
textViewIntro.text =
if(arguments?.getBoolean("isEdit") == true) {
"그룹의 유형을\n수정해주세요"
} else {
"생성할 그룹의\n유형을 선택해주세요"
}
Copy link

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)
     }

Comment on lines +54 to 59
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">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

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).

Comment on lines +82 to 90
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"
Copy link

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 OneLineEditText likely enforces single-line paddings/lineHeight, which can fight with textMultiLine.
  • android:layout_height="100dp" plus lines/minLines/maxLines causes 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.

Comment on lines +56 to +64
<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" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Comment on lines +83 to +117
<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" />
Copy link

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.

Suggested change
<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"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 신규 기능 구현 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 그룹 생성 및 입장 화면 구성 및 기능 구현

2 participants