Skip to content
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

강원대 Android_주민철_1주차 과제(1,2단계) 제출 #25

Open
wants to merge 57 commits into
base: joominchul
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
e57d2d3
Docs[README.md]: 추가 리드미
joominchul Jun 24, 2024
eb1bce6
Design[activity_main.xml]: 추가 얼굴 아이콘
joominchul Jun 24, 2024
98e5055
Design[colors.xml]: 추가 민트색
joominchul Jun 24, 2024
8ac5326
Design[face.png]: 추가 얼굴 이미지
joominchul Jun 24, 2024
8c14ce2
Design[text_background.xml]: 추가 입력창 백그라운드
joominchul Jun 24, 2024
53b6d61
Design[activity_main.xml]: 추가 activity_main.xml
joominchul Jun 24, 2024
a433b8c
Design[activity_main.xml]: 변경 more.xml 부분을 메인으로 합병
joominchul Jun 24, 2024
b6ca6dc
Design[activity_main.xml]: 변경 생일
joominchul Jun 24, 2024
5c36af2
Design[activity_main.xml]: 변경
joominchul Jun 25, 2024
439e8bf
Feat[]: 추가 데이터베이스
joominchul Jun 25, 2024
a1a780a
Design[face.png]: 변경 얼굴 이미지
joominchul Jun 25, 2024
7cbdbaf
Feat[MainActivity.kt]: 추가 데이터베이스, 달력, 더보기
joominchul Jun 25, 2024
23f9cd2
Feat[MainActivity.kt]: 변경 전화번호 타입
joominchul Jun 25, 2024
1875c09
Refactor[MainActivity.kt]: 변경 UI 선언 위치
joominchul Jun 25, 2024
5f098c6
Feat[MainActivity.kt]: 추가 성별 선택
joominchul Jun 25, 2024
29e0327
Feat[MainActivity.kt]: 추가 데이터베이스 저장
joominchul Jun 25, 2024
7e92386
Feat[MainActivity.kt]: 추가 loadAllData()
joominchul Jun 25, 2024
538061a
Design[activity_main.xml]: 변경 스크롤뷰로
joominchul Jun 26, 2024
393bb25
Docs[README.md]: 추가 2단계 설명
joominchul Jun 27, 2024
85887d7
Refactor[]: 변경 파일들 이름
joominchul Jun 27, 2024
11d8e7f
Design[colors.xml]: 추가 금색
joominchul Jun 27, 2024
71aca7a
Design[activity_main.xml]: 추가 초기 화면
joominchul Jun 27, 2024
6744323
Design[activity_main.xml]: 변경 추가 버튼 ID
joominchul Jun 27, 2024
70a22ee
Feat[MainActivity.kt]: 추가 addButton 클릭 리스너
joominchul Jun 27, 2024
8c815ea
Design[name_first_background.xml]: 추가 연락처 원형 아이콘 백그라운드
joominchul Jun 27, 2024
fde6d93
Design[contact_item_background.xml]: 추가 연락처 백그라운드
joominchul Jun 27, 2024
de866a6
Design[contact_item.xml]: 추가 연락처 아이템 디자인
joominchul Jun 27, 2024
485b61d
Design[contact_item.xml]: 추가 연락처 이름 ID
joominchul Jun 27, 2024
f780401
Feat[RecyclerViewAdapter.kt]: 추가 리사이클러뷰 어댑터
joominchul Jun 27, 2024
6a61f37
Refactor[]: 이동 MIGRATION_1_2
joominchul Jun 27, 2024
9d1325b
Refactor[]: 변경 phone 타입
joominchul Jun 27, 2024
2cbba18
Refactor[]: 추가 MIGRATION_2_3
joominchul Jun 27, 2024
5eb63bb
Feat[ContactDao.kt]: 추가 deleteAll
joominchul Jun 27, 2024
3cb8dfa
Design[activity_main.xml]: 추가 초기 안내 문구 ID
joominchul Jun 27, 2024
f5352e0
Feat[MainActivity.kt]: 추가 연락처 추가 후 연락처 목록 보이는 기능, 연락처 추가 후 초기 안내 문구 안…
joominchul Jun 27, 2024
55257d1
Design[activity_contact_detail.xml]: 추가 연락처 상세 정보 UI
joominchul Jun 28, 2024
38604d8
Feat[RecyclerViewAdapter.kt]: 추가 연락처 상세 정보 불러오기
joominchul Jun 28, 2024
00f28d4
Feat[AddContact.kt]: 추가 loadAllData 메모
joominchul Jun 28, 2024
7bf2f1d
Fix[RecyclerViewAdapter.kt]: 추가 startActivity
joominchul Jun 28, 2024
6112333
Feat[ContactDetail.kt]: 추가 연락처 세부 정보 액티비티
joominchul Jun 28, 2024
1d930df
Feat[AddContact.kt]: 추가 저장 시 액티비티 종료
joominchul Jun 28, 2024
a0d1fc0
Feat[AddContact.kt]: 추가 취소 시 액티비티 종료
joominchul Jun 28, 2024
ce8f675
Design[popup_message.xml]: 추가 연락처 작성 시 확인 팝업
joominchul Jun 28, 2024
27e456d
Feat[AddContact.kt]: 추가 연락처 작성 시 확인 팝업 기능
joominchul Jun 28, 2024
30f8c13
Refactor[AddContact.kt]: 변경 onKeyDown
joominchul Jun 28, 2024
9f528c8
Refactor[AddContact.kt]: 변경 showDatePickerDialog
joominchul Jun 28, 2024
fd1029d
Feat[MainActivity.kt]: 추가 onKeyDown
joominchul Jun 28, 2024
1d0f244
Chore
joominchul Jun 28, 2024
956c975
Comment: 추가 주석 및 설명
joominchul Jun 28, 2024
0f33cb8
Fix[AddContact.kt]: 수정 onKeyDown
joominchul Jun 28, 2024
9e33dcc
Chore[build.gradle.kts]: 삭제 의존성
joominchul Jul 2, 2024
201f87c
Rename[AddContactActivity.kt]: 변경 AddContactActivity 이름
joominchul Jul 2, 2024
54c2d8c
Refactor[AddContactActivity.kt]: 분리 뷰 객체 할당
joominchul Jul 2, 2024
5f00c86
fix: 매니페스트 메인 액티비티2 등록 오류 수정
joominchul Nov 3, 2024
14c9c0a
실행 화면들 추가
joominchul Nov 3, 2024
68c4c2a
Update README.md
joominchul Nov 3, 2024
ba6053e
Update README.md
joominchul Nov 3, 2024
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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
# android-contacts
# 1단계 - 연락처 추가
## 기능 요구사항
간단한 연락처를 구현한다.
- 연락처를 추가한다.
- 이름과 전화번호는 필수 값이다.
- 입력하지 않으면 토스트 메시지를 보여준다.
- 전화번호 입력은 숫자만 가능하다.
- 더보기를 눌러 입력 폼을 확장할 수 있다.
- 생일, 성별, 메모 입력 폼이 등장한다.
- 성별을 둘 중 하나를 선택할 수 있다.
- 저장 버튼을 누르면 '저장이 완료 되었습니다' 라는 토스트 메시지를 보여준다.
- 취소 버튼을 누르면 '취소 되었습니다' 라는 토스트 메시지를 보여준다.

# 2단계 - 연락처 목록
## 기능 요구사항
간단한 연락처를 구현한다.
- 연락처 등록 화면을 구현한다.
- 연락처를 저장하면 목록에 추가된다.
- 저장된 연락처가 많을 경우 목록은 스크롤 되어야 한다.
- 추가된 연락처를 선택하여 상세 화면을 볼 수 있다.
- 연락처 작성 중 뒤로가기 버튼을 누르면 확인 팝업이 나타난다.
- 추가된 연락처는 앱을 다시 실행하면 유지되지 않는다.
35 changes: 34 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id ("org.jetbrains.kotlin.kapt")
}

android {
Expand Down Expand Up @@ -33,6 +34,9 @@ android {
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
viewBinding = true
}
}

dependencies {
Expand All @@ -41,7 +45,36 @@ dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
testImplementation("junit:junit:4.13.2")
implementation("androidx.activity:activity:1.9.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

val room_version = "2.6.1"

implementation("androidx.room:room-runtime:$room_version")
joominchul marked this conversation as resolved.
Show resolved Hide resolved
annotationProcessor("androidx.room:room-compiler:$room_version")

// To use Kotlin annotation processing tool (kapt)
kapt("androidx.room:room-compiler:$room_version")

// optional - Kotlin Extensions and Coroutines support for Room
implementation("androidx.room:room-ktx:$room_version")

// optional - RxJava2 support for Room
implementation("androidx.room:room-rxjava2:$room_version")

// optional - RxJava3 support for Room
implementation("androidx.room:room-rxjava3:$room_version")

// optional - Guava support for Room, including Optional and ListenableFuture
implementation("androidx.room:room-guava:$room_version")

// optional - Test helpers
testImplementation("androidx.room:room-testing:$room_version")

// optional - Paging 3 Integration
implementation("androidx.room:room-paging:$room_version")
}
24 changes: 23 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,37 @@
android:supportsRtl="true"
android:theme="@style/Theme.Contacts"
tools:targetApi="31">
<activity
android:name=".ContactDetail"
android:exported="false" />
<activity
android:name=".MainActivity2"
android:exported="true"
android:theme="@style/Theme.Contacts">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
joominchul marked this conversation as resolved.
Show resolved Hide resolved
</activity>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".AddContact"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
</manifest>
178 changes: 178 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/AddContact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package campus.tech.kakao.contacts

import android.app.DatePickerDialog
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.PopupWindow
import android.widget.RadioButton
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.room.Room
import campus.tech.kakao.contacts.Contact.Companion.MIGRATION_1_2
import campus.tech.kakao.contacts.Contact.Companion.MIGRATION_2_3
import java.time.Month
import java.util.Calendar

class AddContact : AppCompatActivity() {
joominchul marked this conversation as resolved.
Show resolved Hide resolved
// Declare UI elements
private lateinit var name: EditText
private lateinit var phone: EditText
private lateinit var mail: EditText
private lateinit var birth: TextView
private var sex:String? = null
private lateinit var female: RadioButton
private lateinit var male: RadioButton
private lateinit var memo: EditText
private lateinit var save: TextView
private lateinit var cancel: TextView
private lateinit var moreText: TextView
private lateinit var birthSexMemo: View


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_addcontact)
// Initialize UI elements
name = findViewById(R.id.name)
joominchul marked this conversation as resolved.
Show resolved Hide resolved
phone = findViewById(R.id.phone)
mail = findViewById(R.id.mail)
birth = findViewById(R.id.birth)
female = findViewById(R.id.female)
male = findViewById(R.id.male)
memo = findViewById(R.id.memo)
save = findViewById(R.id.save)
cancel = findViewById(R.id.cancel)
birthSexMemo = findViewById(R.id.birthSexMemo)
moreText = findViewById(R.id.moreText)
birth.setOnClickListener {
showDatePickerDialog()
}
// 더보기 버튼 클릭 시
moreText.setOnClickListener {
if (moreText.visibility == View.VISIBLE) {
joominchul marked this conversation as resolved.
Show resolved Hide resolved
moreText.visibility = View.GONE
birthSexMemo.visibility = View.VISIBLE
} else {
moreText.visibility = View.VISIBLE
birthSexMemo.visibility = View.GONE
}
}
// Initialize database
val database = Room.databaseBuilder(
applicationContext,
ContactDatabase::class.java, "database-name"
Copy link

Choose a reason for hiding this comment

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

"database-name" 으로 db 인스턴스를 생성하셨는데 현재 AddContact 외에 다른 장소에서 이 이름이 서로 다를 경우 어떻게 작동할까요?
이런 상수와 관련된 부분은 한 곳에서 관리하는게 좋아 보입니다.

).addMigrations(MIGRATION_1_2, MIGRATION_2_3).allowMainThreadQueries().build()
joominchul marked this conversation as resolved.
Show resolved Hide resolved
joominchul marked this conversation as resolved.
Show resolved Hide resolved

// 저장 버튼 클릭 시
joominchul marked this conversation as resolved.
Show resolved Hide resolved
save.setOnClickListener {
if (name.text.isEmpty()) {
Toast.makeText(this, "이름은 필수 값입니다", Toast.LENGTH_SHORT).show()
}
else if (phone.text.isEmpty()) {
Toast.makeText(this, "전화 번호는 필수 값입니다", Toast.LENGTH_SHORT).show()
}
joominchul marked this conversation as resolved.
Show resolved Hide resolved
else{
if (female.isChecked) {
Copy link

Choose a reason for hiding this comment

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

RadioGroup이 아니라 RadioButton만 사용하셨네요. RadioButton은 RadioGroup과 작성해야 default를 분명히 할 수 있습니다. 이 경우 sex 변수가 null일 케이스도 존재하겠네요.

sex = "여자"
Copy link

Choose a reason for hiding this comment

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

이러한 "여자", "남자" 로 표현할 수 있는 enum class를 만들어 관리하면 Human Error를 줄일 수 있습니다.

}
else if (male.isChecked) {
sex = "남자"
}
// Insert data into database
val contact = Contact(
name.text.toString(),
phone.text.toString(),
mail.text.toString(),
birth.text.toString(),
sex,
memo.text.toString()
)
database.contactDao().insert(contact)
Copy link

Choose a reason for hiding this comment

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

그러긴 힘들겠지만 모종의 이유로 insert 쿼리가 실행되는데 10초 이상 지연되면 어떻게 될까요?

Toast.makeText(this, "저장이 완료 되었습니다", Toast.LENGTH_SHORT).show()
finish()
}

}
// 연락처 데이터 로그에 표시
fun loadAllData(){
joominchul marked this conversation as resolved.
Show resolved Hide resolved
joominchul marked this conversation as resolved.
Show resolved Hide resolved
if (database.contactDao() == null){
Log.d("contacttest", "null")
}
joominchul marked this conversation as resolved.
Show resolved Hide resolved
else{
val list = database.contactDao().getAll()
list.forEach {
Log.d("contacttest", ""+it.id+", "+it.name+", "+it.phone+", "+it.email+", "+it.birth+", "+it.sex+", "+it.memo)
}
joominchul marked this conversation as resolved.
Show resolved Hide resolved
}
}
//취소 버튼 클릭 시
cancel.setOnClickListener {
Toast.makeText(this, "취소 되었습니다", Toast.LENGTH_SHORT).show()
loadAllData()
finish()
}




}

// 생일 선택 팝업창
fun showDatePickerDialog() {
val calendar = Calendar.getInstance()
val datePicker = DatePickerDialog(
this, {DatePicker, year:Int, month:Int, dayOfMonth:Int ->
//월과 일 포맷을 00으로 설정
var Month = "${month + 1}"
joominchul marked this conversation as resolved.
Show resolved Hide resolved
if (month < 9) {
Month = "0${Month}"
}
var Day = "${dayOfMonth}"
if (dayOfMonth < 10) {
Day = "0${Day}"
}
birth.text = "${year}.${Month}.${Day}"
Copy link

Choose a reason for hiding this comment

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

SimpleDateFormat이라고 Date Format을 편하게 사용하기 위한 편의 객체가 제공되어있습니다. 이를 활용해보세요~

}, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH))
datePicker.show()
}
//팝업창 설정
fun popupMessage(){
//팝업 UI 설정
val popupView = layoutInflater.inflate(R.layout.popup_message, null)
val write = popupView.findViewById<TextView>(R.id.write)
val exit = popupView.findViewById<TextView>(R.id.exit)

val popupBuilder = AlertDialog.Builder(this).setView(popupView)
val popupWindow = popupBuilder.show()
//작성하기 버튼 클릭 시 팝업창 닫기
write.setOnClickListener {
popupWindow.dismiss()
}
//나가기 버튼 클릭 시 연락처 목록으로 이동
exit.setOnClickListener {
finish()
}
}

//뒤로 가기 버튼 클릭 시
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
//뒤로 가기 버튼 클릭하고 나머지 텍스트가 있을 경우
if (keyCode == KeyEvent.KEYCODE_BACK
&& (name.text.isNotEmpty() || phone.text.isNotEmpty()
|| mail.text.isNotEmpty() || birth.text.isNotEmpty()
|| (female.isChecked || male.isChecked) || memo.text.isNotEmpty())) {
popupMessage()
return true
}
else if (keyCode == KeyEvent.KEYCODE_BACK){
finish()
}
return false
}
}
71 changes: 71 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/Contact.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package campus.tech.kakao.contacts

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase

@Entity
class Contact (
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "phone") val phone: String,
@ColumnInfo(name = "email") val email: String?,
@ColumnInfo(name = "birth") val birth: String?,
@ColumnInfo(name = "sex") val sex: String?,
@ColumnInfo(name = "memo") val memo: String?
){
constructor(name: String, phone: String, email: String?, birth: String?, sex: String?, memo: String?):
this(0, name, phone, email, birth, sex, memo)

companion object {
// 테이블 변경 사항
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE new_Contact (" +
"id INTEGER PRIMARY KEY NOT NULL, " +
"name TEXT NOT NULL, " +
"phone INTEGER NOT NULL, " +
"email TEXT, " +
"birth TEXT, " +
"sex TEXT, " +
"memo TEXT)"
)
// 데이터 복사
database.execSQL(
"INSERT INTO new_Contact (id, name, phone, email, birth, sex, memo) " +
"SELECT id, name, CAST(phone AS INTEGER), email, birth, sex, memo FROM Contact")
// 이전 테이블 제거
database.execSQL("DROP TABLE Contact")

// 새 테이블 이름 바꾸기
database.execSQL("ALTER TABLE new_Contact RENAME TO Contact")
}
}
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL(
"CREATE TABLE new_Contact (" +
"id INTEGER PRIMARY KEY NOT NULL, " +
"name TEXT NOT NULL, " +
"phone TEXT NOT NULL, " +
"email TEXT, " +
"birth TEXT, " +
"sex TEXT, " +
"memo TEXT)"
)
// 데이터 복사
database.execSQL(
"INSERT INTO new_Contact (id, name, phone, email, birth, sex, memo) " +
"SELECT id, name, CAST(phone AS TEXT), email, birth, sex, memo FROM Contact")
// 이전 테이블 제거
database.execSQL("DROP TABLE Contact")

// 새 테이블 이름 바꾸기
database.execSQL("ALTER TABLE new_Contact RENAME TO Contact")
}
}
}
}
26 changes: 26 additions & 0 deletions app/src/main/java/campus/tech/kakao/contacts/ContactDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package campus.tech.kakao.contacts

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy.Companion.REPLACE
import androidx.room.Query

@Dao
interface ContactDao {
@Insert(onConflict = REPLACE)
fun insert(contact: Contact)

@Query("SELECT * FROM Contact")
fun getAll(): List<Contact>

@Query("SELECT * FROM Contact WHERE id = :id")
fun getById(id: Int): Contact

@Query("DELETE FROM Contact WHERE id = :id")
fun deleteById(id: Int)

@Query("DELETE FROM Contact")
fun deleteAll()

}

Loading