Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
178 commits
Select commit Hold shift + click to select a range
146331b
feat: 스플래쉬 화면 구현
stopstone Sep 3, 2025
c3f5233
feat: 로그인 상태 확인 로직 구현
stopstone Sep 3, 2025
fa77703
feat: 앱 아이콘 변경
stopstone Sep 3, 2025
c25aa6b
refactor: img_bg.png 이미지 dpi별 drawable 분리
stopstone Sep 3, 2025
1ee5d34
refactor: 스플래시 화면 ViewModel 로직 수정
stopstone Sep 3, 2025
9b25c62
feat: 온보딩 버튼 컴포넌트 추가
stopstone Sep 4, 2025
172e602
feat: 온보딩 페이지 인디케이터 UI 구현
stopstone Sep 4, 2025
be8479c
feat: 온보딩 페이지 모델 구현
stopstone Sep 4, 2025
61d1fd4
feat: 온보딩 화면 구현
stopstone Sep 4, 2025
22c741b
feat: 온보딩 네비게이션 설정
stopstone Sep 4, 2025
717b0d4
feat: 온보딩 페이지 이미지 추가
stopstone Sep 5, 2025
ad971ef
feat: 온보딩 화면 이미지 추가
stopstone Sep 5, 2025
c4ff3c9
feat: 온보딩 UI 이미지 영역 수정
stopstone Sep 5, 2025
262ded3
feat: 온보딩 페이지 백그라운드 구성
stopstone Sep 5, 2025
45513a1
feat: 온보딩 배경 추가
stopstone Sep 5, 2025
7768d8f
refactor: 불필요한 패딩 제거
stopstone Sep 5, 2025
846bbce
feat: 온보딩 백그라운드 색상 변경
stopstone Sep 5, 2025
a053f43
refactor: OnboardingScreen UI 레이아웃 조정
stopstone Sep 5, 2025
18af35b
refactor: AnnotatedString.Builder 확장 함수 추가
stopstone Sep 5, 2025
037f6d3
feat: DataStore 분리 및 Qualifier 적용
stopstone Sep 5, 2025
ffc32db
feat: 온보딩 DataStore 추가
stopstone Sep 5, 2025
0e3ff5e
feat: 온보딩 완료 상태 저장 로직 추가
stopstone Sep 5, 2025
98a8d85
feat: 온보딩 버튼 로딩 상태 추가
stopstone Sep 5, 2025
fa5cadc
feat: 온보딩 화면 UI 상태 및 사이드 이펙트 처리
stopstone Sep 5, 2025
25f2f20
feat: 온보딩 버튼 텍스트 stirng.xml 리소스 추가
stopstone Sep 6, 2025
22ec755
feat: 온보딩 레포지토리 추가 및 ViewModel 연동
stopstone Sep 6, 2025
a99de45
refactor: 온보딩 페이지 데이터 - remember로 성능 최적화
stopstone Sep 6, 2025
2ae7148
refactor: 온보딩 Preferences 키 상수화
stopstone Sep 6, 2025
1fa484d
refactor: 불필요한 코드 제거
stopstone Sep 6, 2025
508f2d5
refactor: 온보딩 페이지 데이터 모델 변경
stopstone Sep 6, 2025
5845e24
refactor: 디자인 가이드에 맞게 텍스트 수정
stopstone Sep 6, 2025
588a6a3
feat: Color.kt 온보딩 배경 장식 색상 추가
stopstone Sep 6, 2025
7360949
refactor: 불필요한 import문 제거
stopstone Sep 6, 2025
02458a5
feat: 마이페이지 구성에 필요한 리소스 추가
stopstone Sep 10, 2025
6510ade
feat: LoginType enum 추가
stopstone Sep 10, 2025
6f5853b
feat: 마이프로필 컴포넌트 추가
stopstone Sep 10, 2025
ade3245
feat: 내 정보 화면 UI 및 ViewModel 구현
stopstone Sep 10, 2025
c76569d
refactor: LoginType model 패키지로 이동
stopstone Sep 10, 2025
b1dc0b3
feat: 내 프로필 화면 navigation 구현
stopstone Sep 10, 2025
6cc6646
feat: Glide Compose 라이브러리 추가
stopstone Sep 10, 2025
6e5b3d4
feat: 회원 정보 API 연동
stopstone Sep 10, 2025
4beb928
feat: MyProfile 에러 핸들링 추가
stopstone Sep 10, 2025
1e45366
feat: 프로필 이미지 로딩 구현
stopstone Sep 10, 2025
0cc546e
feat: 멤버 정보 API 연동
stopstone Sep 10, 2025
7fa85de
feat: 내 정보 화면 및 API 연동
stopstone Sep 10, 2025
7ce6782
feat: 마이프로필 뒤로가기 기능 구현
stopstone Sep 10, 2025
ee361dd
refactor: 마이프로필 정보에 매퍼 적용 및 UI 연결
stopstone Sep 10, 2025
edcedd1
feat: 마이프로필 로그아웃 기능 구현
stopstone Sep 10, 2025
58f927e
refactor: 로딩 화면 범위 수정
stopstone Sep 10, 2025
175c29b
refactor: MyProfileScreen 부분별 코드 분리
stopstone Sep 10, 2025
8748685
feat: 연락처 데이터 호출 로직 작업
rhkrwngud445 Sep 7, 2025
309ff01
feat: 연락처 권한 추가
rhkrwngud445 Sep 9, 2025
f43e836
feat: 연락처 데이처 호출 로직 적용
rhkrwngud445 Sep 10, 2025
7c3ccdf
feat: 연락처 기념일, 라벨 등 추가정보 적용
rhkrwngud445 Sep 10, 2025
4392caf
feat: 연락처 화면 파일 추가
rhkrwngud445 Sep 10, 2025
6e6b67a
feat: 회원탈퇴 화면 이동 로직 구현
stopstone Sep 10, 2025
3e6ea19
feat: 회원탈퇴 API 연동
stopstone Sep 10, 2025
3dd19fe
feat: 회원탈퇴 사유 모델 추가
stopstone Sep 11, 2025
6418d94
feat: 회원탈퇴 화면 TopAppBar 구현
stopstone Sep 11, 2025
761beb1
feat: 회원탈퇴 화면 및 기능 구현
stopstone Sep 11, 2025
b71d500
feat: NearOutlinedTextField 컴포넌트 추가
stopstone Sep 11, 2025
d515da3
refactor: NearOutlinedTextField 키보드 상호작용 개선
stopstone Sep 12, 2025
6e24a77
feat: 연락처 상단바 추가
rhkrwngud445 Sep 14, 2025
28bb3c8
feat: Near 검색 TextField 추가
rhkrwngud445 Sep 14, 2025
37e03ee
feat: 화면 검색 컴포넌트 추가
rhkrwngud445 Sep 14, 2025
c59da14
feat: 연락처 리스트 화면 구성
rhkrwngud445 Sep 14, 2025
d56a591
fix: 연락처 리스트 마진 수정
rhkrwngud445 Sep 14, 2025
0afb0de
feat: 연락처 로딩 및 체크박스 인터렉션 적용
rhkrwngud445 Sep 14, 2025
3afbe18
fix: 쌍자음도 기본 자음에 포함되도록 수정
rhkrwngud445 Sep 14, 2025
648c33e
feat: navigation 화면 정의
rhkrwngud445 Sep 14, 2025
d8ee179
feat: 검색 인터렉션 구현
rhkrwngud445 Sep 14, 2025
39360fe
feat: 한글, 영어, 특수문자 순으로 연락처 정렬
rhkrwngud445 Sep 16, 2025
12eb9e6
fix: 이름만 검색되도록 수정
rhkrwngud445 Sep 16, 2025
2df8fe4
feat: 우측 자음 인덱스바 추가
rhkrwngud445 Sep 16, 2025
7eea373
feat: permission 요청 컴포저블 추가
rhkrwngud445 Sep 16, 2025
567636c
feat: 텍스트필드 에러 상태 및 관련 기능 추가
stopstone Sep 16, 2025
5e1e55a
feat: 회원탈퇴 화면 UI 및 로직 개선
stopstone Sep 16, 2025
d7f9a59
style: 회원탈퇴 화면 앱바 패딩 수정
stopstone Sep 16, 2025
18c3095
refactor: 의견 입력 에러 메시지 수정
stopstone Sep 16, 2025
abc4337
feat: 회원탈퇴 화면 뒤로가기 추가
stopstone Sep 16, 2025
5c245f4
feat: 회원탈퇴 화면에 닉네임 전달 로직 추가
stopstone Sep 16, 2025
427e766
chore: 탈퇴 화면 하단 패딩 수정
stopstone Sep 16, 2025
5505311
chore: NearServiceInfoRow 클릭 범위 수정 및 리플효과 제거
stopstone Sep 16, 2025
47c1b92
refactor: WithdrawTopAppBar → NearCancelTopAppBar로 이름 변경 및 이동
stopstone Sep 16, 2025
634872a
chore: 미사용 import 제거
stopstone Sep 16, 2025
08ad462
refactor: Modifier 파라미터 적용
stopstone Sep 16, 2025
66a6488
chore: build.gradle.kts 약관 URL 정보 추가
stopstone Sep 16, 2025
b0fe6b1
feat: 약관 및 정책 웹뷰 화면 연동
stopstone Sep 16, 2025
3917c00
refactor: 약관 및 정책 목록 표시 로직 개선
stopstone Sep 16, 2025
ad35f20
refactor: 약관 페이지 title 형식 수정
stopstone Sep 16, 2025
59d478a
feat: MY 페이지, 탈퇴하기 화면 string.xml 리소스 적용
stopstone Sep 16, 2025
fddabb7
remove: 회원 탈퇴 사유 모델 제거
stopstone Sep 16, 2025
f2ab07f
refactor: LoginType의 프로퍼티명 수정
stopstone Sep 16, 2025
32c70d8
Merge branch 'dev' into feat/splash-auto-login
stopstone Sep 16, 2025
83e9aba
feat: 스플래시 화면 관련 파일 삭제
stopstone Sep 16, 2025
90e1696
feat: 스플래시 화면 라이브러리 추가
stopstone Sep 16, 2025
d291865
feat: themes.xml에서 스플래시 화면 구현
stopstone Sep 16, 2025
8fb3bfc
feat: 스플래시 로고 이미지 추가
stopstone Sep 16, 2025
a669663
feat: 스플래시 화면 API 적용 및 로그인 상태에 따른 화면 분기 처리
stopstone Sep 16, 2025
fb48a73
chore: 내 정보 화면 배경색 추가
stopstone Sep 18, 2025
6165bff
Merge pull request #26 from near-Contact-Reminder/feat/splash-auto-login
stopstone Sep 18, 2025
2fac627
feat: 온보딩 버튼 컴포넌트 추가
stopstone Sep 4, 2025
ea7a2e1
feat: 온보딩 페이지 인디케이터 UI 구현
stopstone Sep 4, 2025
0c22958
feat: 온보딩 페이지 모델 구현
stopstone Sep 4, 2025
c536bf4
feat: 온보딩 화면 구현
stopstone Sep 4, 2025
dcfd9ca
feat: 온보딩 네비게이션 설정
stopstone Sep 4, 2025
3396b74
feat: 온보딩 페이지 이미지 추가
stopstone Sep 5, 2025
48834cf
feat: 온보딩 화면 이미지 추가
stopstone Sep 5, 2025
d3c08c5
feat: 온보딩 UI 이미지 영역 수정
stopstone Sep 5, 2025
d58d85d
feat: 온보딩 페이지 백그라운드 구성
stopstone Sep 5, 2025
9d1453b
feat: 온보딩 배경 추가
stopstone Sep 5, 2025
7b59095
refactor: 불필요한 패딩 제거
stopstone Sep 5, 2025
fc190f0
feat: 온보딩 백그라운드 색상 변경
stopstone Sep 5, 2025
1577a75
refactor: OnboardingScreen UI 레이아웃 조정
stopstone Sep 5, 2025
1596dab
refactor: AnnotatedString.Builder 확장 함수 추가
stopstone Sep 5, 2025
2834cc6
feat: DataStore 분리 및 Qualifier 적용
stopstone Sep 5, 2025
b749fcb
feat: 온보딩 DataStore 추가
stopstone Sep 5, 2025
c19802b
feat: 온보딩 완료 상태 저장 로직 추가
stopstone Sep 5, 2025
e798851
feat: 온보딩 버튼 로딩 상태 추가
stopstone Sep 5, 2025
5cf9a2f
feat: 온보딩 화면 UI 상태 및 사이드 이펙트 처리
stopstone Sep 5, 2025
dc46da1
feat: 온보딩 버튼 텍스트 stirng.xml 리소스 추가
stopstone Sep 6, 2025
66ac5a1
feat: 온보딩 레포지토리 추가 및 ViewModel 연동
stopstone Sep 6, 2025
93ab1c3
refactor: 온보딩 페이지 데이터 - remember로 성능 최적화
stopstone Sep 6, 2025
b6577d0
refactor: 온보딩 Preferences 키 상수화
stopstone Sep 6, 2025
f30d851
refactor: 불필요한 코드 제거
stopstone Sep 6, 2025
4d59fef
refactor: 온보딩 페이지 데이터 모델 변경
stopstone Sep 6, 2025
d8a4c99
refactor: 디자인 가이드에 맞게 텍스트 수정
stopstone Sep 6, 2025
cd47e30
feat: Color.kt 온보딩 배경 장식 색상 추가
stopstone Sep 6, 2025
b29e18c
refactor: 불필요한 import문 제거
stopstone Sep 6, 2025
38045f7
feat: 앱 시작 시 온보딩 및 로그인 상태에 따른 화면 분기 처리
stopstone Sep 19, 2025
25b7bd9
Merge remote-tracking branch 'origin/feat/onboarding-pages' into feat…
stopstone Sep 19, 2025
a35fba3
refactor: 온보딩 관련 중복 문자열 제거
stopstone Sep 19, 2025
3d9cb34
Merge branch 'dev' into feat/my-profile
stopstone Sep 19, 2025
7be0ca9
refactor: 인증 헤더 삭제
stopstone Sep 19, 2025
3c9719e
refactor: MyProfileUiState에서 사용하지 않는 error 필드 제거
stopstone Sep 19, 2025
a89f88e
refactor: RouteWebView에서 toRoute 사용하도록 수정
stopstone Sep 19, 2025
00bb29d
feat: WebView 뒤로가기 기능 추가
stopstone Sep 19, 2025
c192e13
refactor: Glide의 contentScale 옵션 제거
stopstone Sep 19, 2025
c186b40
fix: `NearOutlinedTextField` 에러 상태 반영 오류 수정
stopstone Sep 19, 2025
2b1acb2
refactor: 회원탈퇴 화면 에러 처리 방식 변경
stopstone Sep 19, 2025
ce51d82
chore: ci 워크플로우에 개인정보처리방침 URL 추가
stopstone Sep 19, 2025
c389154
Merge pull request #29 from near-Contact-Reminder/feat/onboarding-pages
stopstone Sep 19, 2025
2b4c1d0
refactor: Glide에서 Coil로 이미지 로딩 라이브러리 변경
stopstone Sep 23, 2025
4522d0f
refactor: API 응답 타입 수정
stopstone Sep 23, 2025
fc13f52
feat: flow를 간소화 한 apiCallFlow 추가
stopstone Sep 23, 2025
5ecc798
refactor: API 호출 Flow 적용
stopstone Sep 23, 2025
b652c3e
refactor: 회원 탈퇴 API 에러 핸들링 수정
stopstone Sep 23, 2025
9e6aad2
refactor: Member 관련 데이터 모델 네이밍 및 매퍼 구조 변경
stopstone Sep 23, 2025
dc3acc4
refactor: `WithdrawRequest` 모델의 위치 변경 및 관련 로직 수정
stopstone Sep 23, 2025
96dec0a
fix: 중복된 NavGraph 정의 제거
stopstone Sep 23, 2025
158ecc5
refactor: `MyProfileInfo`를 `MyProfileInfoUIModel`로 이름 변경
stopstone Sep 23, 2025
2b53a2c
refactor: 불필요한 import 제거
stopstone Sep 23, 2025
0494397
Merge branch 'feat/my-profile' into dev
stopstone Sep 23, 2025
3522577
refactor: `NearNavHost` 시작 화면 `startDestination`으로 통합
stopstone Sep 23, 2025
d0ae5ce
feat: 로딩 인디케이터 색상 변경
rhkrwngud445 Sep 23, 2025
b70979a
Merge pull request #33 from near-Contact-Reminder/feat/my-profile
stopstone Sep 23, 2025
cfbbc4b
fix: 인덱스바 상단 마진 축소
rhkrwngud445 Sep 23, 2025
ee6145b
feat: 연락처 데이터 호출 로직 작업
rhkrwngud445 Sep 7, 2025
a47d238
feat: 연락처 권한 추가
rhkrwngud445 Sep 9, 2025
7b1828d
feat: 연락처 데이처 호출 로직 적용
rhkrwngud445 Sep 23, 2025
cd854b6
feat: 연락처 기념일, 라벨 등 추가정보 적용
rhkrwngud445 Sep 10, 2025
81da523
feat: 연락처 화면 파일 추가
rhkrwngud445 Sep 10, 2025
0da3c58
feat: 연락처 상단바 추가
rhkrwngud445 Sep 23, 2025
aefff08
feat: Near 검색 TextField 추가
rhkrwngud445 Sep 14, 2025
d6b4f59
feat: 화면 검색 컴포넌트 추가
rhkrwngud445 Sep 14, 2025
9d21859
feat: 연락처 리스트 화면 구성
rhkrwngud445 Sep 14, 2025
c582edd
fix: 연락처 리스트 마진 수정
rhkrwngud445 Sep 14, 2025
0acc37a
feat: 연락처 로딩 및 체크박스 인터렉션 적용
rhkrwngud445 Sep 14, 2025
96e03fc
fix: 쌍자음도 기본 자음에 포함되도록 수정
rhkrwngud445 Sep 14, 2025
db84722
feat: navigation 화면 정의
rhkrwngud445 Sep 23, 2025
9dd5e70
feat: 검색 인터렉션 구현
rhkrwngud445 Sep 23, 2025
fd603ed
feat: 한글, 영어, 특수문자 순으로 연락처 정렬
rhkrwngud445 Sep 16, 2025
b3eef63
fix: 이름만 검색되도록 수정
rhkrwngud445 Sep 16, 2025
c563b9d
feat: 우측 자음 인덱스바 추가
rhkrwngud445 Sep 16, 2025
7bea36d
feat: permission 요청 컴포저블 추가
rhkrwngud445 Sep 16, 2025
4d0853e
feat: 로딩 인디케이터 색상 변경
rhkrwngud445 Sep 23, 2025
4c95de4
fix: 인덱스바 상단 마진 축소
rhkrwngud445 Sep 23, 2025
0550298
Merge remote-tracking branch 'origin/feat/#27-contact_picker_data_lay…
rhkrwngud445 Sep 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Near/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />

<application
android:name=".NearApplication"
Expand All @@ -20,7 +21,7 @@
<meta-data
android:name="com.kakao.sdk.AppKey"
android:value="${kakaoAppKey}" />

<!-- 카카오톡 로그인 콜백을 위한 Activity -->
<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
Expand All @@ -33,7 +34,7 @@
android:scheme="kakao${kakaoAppKey}" />
</intent-filter>
</activity>

<activity
android:name=".presentation.feature.main.MainActivity"
android:exported="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.alarmy.near.data.di

import com.alarmy.near.data.repository.AuthRepository
import com.alarmy.near.data.repository.AuthRepositoryImpl
import com.alarmy.near.data.repository.ContactRepository
import com.alarmy.near.data.repository.DefaultContactRepository
import com.alarmy.near.data.repository.DefaultFriendRepository
import com.alarmy.near.data.repository.ExampleRepository
import com.alarmy.near.data.repository.ExampleRepositoryImpl
Expand All @@ -26,4 +28,8 @@ interface RepositoryModule {
@Binds
@Singleton
abstract fun bindAuthRepository(authRepositoryImpl: AuthRepositoryImpl): AuthRepository

@Binds
@Singleton
abstract fun bindContactRepository(contactRepository: DefaultContactRepository): ContactRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.alarmy.near.data.mapper

import com.alarmy.near.local.entity.ContactEntity
import com.alarmy.near.model.contact.Contact

fun ContactEntity.toModel(): Contact =
Contact(
id = id,
name = name,
phones = phones,
photoUri = photoUri,
birthDay = birthDay,
memo = memo,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.alarmy.near.data.repository

import com.alarmy.near.model.contact.Contact
import kotlinx.coroutines.flow.Flow

interface ContactRepository {
fun fetchAllContacts(): Flow<List<Contact>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.alarmy.near.data.repository

import com.alarmy.near.data.mapper.toModel
import com.alarmy.near.local.contact.ContactLocalDataSource
import com.alarmy.near.model.contact.Contact
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

class DefaultContactRepository
@Inject
constructor(
private val contactDataSource: ContactLocalDataSource,
) : ContactRepository {
override fun fetchAllContacts(): Flow<List<Contact>> =
flow {
emit(
contactDataSource.getAllContacts().map {
it.toModel()
},
)
}
Comment on lines +15 to +22

Choose a reason for hiding this comment

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

high

fetchAllContacts 함수 내에서 contactDataSource.getAllContacts()를 호출하고 있는데, 이는 디스크 I/O를 유발하는 블로킹(blocking) 작업입니다. flow 빌더는 기본적으로 수집자(collector)의 코루틴 컨텍스트에서 실행되므로, 이 코드가 메인 스레드에서 실행될 경우 UI 멈춤(ANR)을 유발할 수 있습니다. flowOn(Dispatchers.IO)를 사용하여 이 작업을 백그라운드 스레드에서 실행하도록 변경하는 것이 좋습니다.

Suggested change
override fun fetchAllContacts(): Flow<List<Contact>> =
flow {
emit(
contactDataSource.getAllContacts().map {
it.toModel()
},
)
}
override fun fetchAllContacts(): Flow<List<Contact>> =
flow {
emit(
contactDataSource.getAllContacts().map {
it.toModel()
},
)
}.flowOn(kotlinx.coroutines.Dispatchers.IO)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package com.alarmy.near.local.contact

import android.content.ContentResolver
import android.provider.ContactsContract
import com.alarmy.near.local.entity.ContactEntity
import com.alarmy.near.local.entity.ImportantDate
import javax.inject.Inject

class ContactLocalDataSource
@Inject
constructor(
private val contentResolver: ContentResolver,
) {
fun getAllContacts(): List<ContactEntity> {
val contacts = mutableListOf<ContactEntity>()

val cursor =
contentResolver.query(
ContactsContract.Contacts.CONTENT_URI,
null,
null,
null,
"${ContactsContract.Contacts.DISPLAY_NAME} ASC",
)

cursor?.use {
val idIndex = it.getColumnIndex(ContactsContract.Contacts._ID)
val nameIndex = it.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
val hasPhoneIndex = it.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)
val photoIndex = it.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)

while (it.moveToNext()) {
val id = it.getLong(idIndex)
val name = it.getString(nameIndex) ?: ""
val hasPhone = it.getInt(hasPhoneIndex) > 0
val photoUri = it.getString(photoIndex)

// 전화번호
val phones = mutableListOf<String>()
if (hasPhone) {
val phoneCursor =
contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
"${ContactsContract.CommonDataKinds.Phone.CONTACT_ID} = ?",
arrayOf(id.toString()),
null,
)
phoneCursor?.use { pc ->
val phoneIndex =
pc.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
while (pc.moveToNext()) {
phones.add(pc.getString(phoneIndex))
}
}
}

// 메모
var memo: String? = null
val noteCursor =
contentResolver.query(
ContactsContract.Data.CONTENT_URI,
arrayOf(ContactsContract.CommonDataKinds.Note.NOTE),
"${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?",
arrayOf(id.toString(), ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE),
null,
)
noteCursor?.use { nc ->
if (nc.moveToFirst()) {
memo = nc.getString(0)
}
}

// 생일
var birthDay: String? = null
val birthdayCursor =
contentResolver.query(
ContactsContract.Data.CONTENT_URI,
arrayOf(ContactsContract.CommonDataKinds.Event.START_DATE),
"${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ? AND ${ContactsContract.CommonDataKinds.Event.TYPE} = ?",
arrayOf(
id.toString(),
ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY
.toString(),
),
null,
)
birthdayCursor?.use { bc ->
if (bc.moveToFirst()) {
birthDay = bc.getString(0)
}
}

// 그룹
val groups = mutableListOf<String>()
val groupCursor =
contentResolver.query(
ContactsContract.Data.CONTENT_URI,
arrayOf(ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID),
"${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?",
arrayOf(
id.toString(),
ContactsContract.CommonDataKinds.GroupMembership.CONTENT_ITEM_TYPE,
),
null,
)
groupCursor?.use { gc ->
val groupIdIndex =
gc.getColumnIndex(
ContactsContract.CommonDataKinds.GroupMembership.GROUP_ROW_ID,
)
while (gc.moveToNext()) {
val groupId = gc.getLong(groupIdIndex)
val groupNameCursor =
contentResolver.query(
ContactsContract.Groups.CONTENT_URI,
arrayOf(ContactsContract.Groups.TITLE),
"${ContactsContract.Groups._ID} = ?",
arrayOf(groupId.toString()),
null,
)
groupNameCursor?.use { gnc ->
if (gnc.moveToFirst()) {
groups.add(gnc.getString(0))
}
}
}
}

// 중요한 날 (기념일)
val importantDates = mutableListOf<ImportantDate>()
val eventCursor =
contentResolver.query(
ContactsContract.Data.CONTENT_URI,
arrayOf(
ContactsContract.CommonDataKinds.Event.START_DATE,
ContactsContract.CommonDataKinds.Event.TYPE,
ContactsContract.CommonDataKinds.Event.LABEL,
),
"${ContactsContract.Data.CONTACT_ID} = ? AND ${ContactsContract.Data.MIMETYPE} = ?",
arrayOf(
id.toString(),
ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
),
null,
)
eventCursor?.use { ec ->
val dateIndex =
ec.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)
val typeIndex =
ec.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE)
val labelIndex =
ec.getColumnIndex(ContactsContract.CommonDataKinds.Event.LABEL)

while (ec.moveToNext()) {
val date = ec.getString(dateIndex)
val type = ec.getInt(typeIndex)
val customLabel = ec.getString(labelIndex)

val label =
when (type) {
ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY -> {
continue // 위에서 생일은 포함했으므로 스킵
}

ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY -> "기념일"
ContactsContract.CommonDataKinds.Event.TYPE_OTHER ->
customLabel
?: "기타"

else -> "알 수 없음"
}

if (date != null) {
importantDates.add(ImportantDate(label, date))
}
}
}

contacts.add(
ContactEntity(
id = id,
name = name,
phones = phones,
photoUri = photoUri,
birthDay = birthDay,
memo = memo,
groups = groups,
importantDates = importantDates,
),
)
}
}

return contacts
}
Comment on lines +14 to +197

Choose a reason for hiding this comment

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

high

getAllContacts 함수는 연락처 목록을 가져오기 위해 메인 루프 안에서 각 연락처에 대해 여러 번의 추가 쿼리(전화번호, 메모, 생일, 그룹 등)를 실행하고 있습니다. 이는 "N+1 쿼리 문제"를 유발하여 연락처가 많은 사용자의 경우 심각한 성능 저하를 초래할 수 있습니다. 특히 그룹 이름을 가져오는 부분은 루프 안에서 또 다른 쿼리를 실행하여 성능을 더욱 악화시킵니다.

이 문제를 해결하려면, ContactsContract.Data.CONTENT_URI에 한 번의 쿼리를 실행하여 필요한 모든 데이터를 가져온 후, 애플리케이션 코드 내에서 CONTACT_ID를 기준으로 데이터를 그룹화하는 방식으로 리팩토링하는 것을 강력히 권장합니다.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.alarmy.near.local.contact.di

import android.content.ContentResolver
import android.content.Context
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object ContactDataSourceModule {
@Provides
@Singleton
fun provideContentResolver(
@ApplicationContext context: Context,
): ContentResolver = context.contentResolver
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.alarmy.near.local.entity

data class ContactEntity(
val id: Long,
val name: String,
val phones: List<String>,
val photoUri: String?, // 사진
val birthDay: String?, // 생일
val memo: String?, // 메모
val groups: List<String>, // 그룹 (ex. 가족, 친구, 회사)
val importantDates: List<ImportantDate>,
)

data class ImportantDate(
val label: String,
val date: String,
)
14 changes: 14 additions & 0 deletions Near/app/src/main/java/com/alarmy/near/model/contact/Contact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.alarmy.near.model.contact

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class Contact(
val id: Long,
val name: String,
val phones: List<String>,
val photoUri: String?, // 사진
val birthDay: String?, // 생일
val memo: String?, // 메모
) : Parcelable
Loading