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

손영준 Assignment3 제출 #64

Open
wants to merge 4 commits into
base: assignment3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions assignment-3/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions assignment-3/.idea/.name

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions assignment-3/.idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions assignment-3/.idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions assignment-3/.idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions assignment-3/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions assignment-3/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions assignment-3/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@


plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
}

android {
Expand All @@ -17,6 +21,10 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildFeatures {
viewBinding = true
}

buildTypes {
release {
isMinifyEnabled = false
Expand All @@ -35,12 +43,30 @@ android {
}
}




dependencies {

implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.fragment:fragment-ktx:1.5.7")
implementation("androidx.activity:activity-ktx:1.7.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
// OkHttp
implementation("com.squareup.okhttp3:okhttp:4.10.0")
// Retrofit
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
// moshi
implementation("com.squareup.moshi:moshi:1.14.0")
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")

implementation("com.google.dagger:hilt-android:2.47")
kapt("com.google.dagger:hilt-android-compiler:2.47")

testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
Expand Down
11 changes: 11 additions & 0 deletions assignment-3/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -11,6 +14,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Assignment3"
android:usesCleartextTraffic="true"
tools:targetApi="31">
<activity
android:name=".MainActivity"
Expand All @@ -21,6 +25,13 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:name=".DetailActivity"
android:label="Detail Activity"
android:exported="true">
</activity>
</application>


</manifest>
203 changes: 203 additions & 0 deletions assignment-3/app/src/main/java/com/jutak/assignment3/DetailActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package com.jutak.assignment3

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView.LayoutManager
import com.jutak.assignment3.databinding.ActivityDetailBinding
import com.jutak.assignment3.databinding.CustomTitleLayoutBinding
import com.jutak.assignment3.databinding.EditDialogLayoutBinding
import com.jutak.assignment3.databinding.PostDialogLayoutBinding
import com.jutak.assignment3.databinding.WordAddDialogLayoutBinding
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@AndroidEntryPoint
class DetailActivity : AppCompatActivity(), OnItemClickListener<Int> {

private lateinit var binding: ActivityDetailBinding
private val viewModel: MainViewModel by viewModels()
// valid 인증 받은 password 및 permission을 단어 삭제와 추가에도 계속 써야 하므로, DetailActivity 내 전역변수 선언
private var password = ""
private var wordInfos = emptyList<MyData.WordInfo>()


override fun onItemClick(itemId: Int) {

}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDetailBinding.inflate(layoutInflater)
setContentView(binding.root)



// Intent로 전달된 데이터 가져오기
val itemId = intent.getIntExtra("itemId", 1)

lifecycleScope.launch(Dispatchers.IO) {
// 비동기 작업을 수행할 코루틴 블록
val result = viewModel.fetchWordList(itemId)
withContext(Dispatchers.Main) {
// 메인 스레드에서 수행할 작업
if (result != "SUCCESS") {
Toast.makeText(this@DetailActivity, result, Toast.LENGTH_SHORT).show()
}
}
wordInfos = viewModel.getWordList(itemId)
withContext(Dispatchers.Main) {
// 메인 스레드에서 수행할 작업
if (result == "SUCCESS") {
val recAdapter = WordListAdapter(this@DetailActivity, wordInfos.toMutableList(), this@DetailActivity)
binding.wordInfo.adapter = recAdapter
binding.wordInfo.layoutManager = LinearLayoutManager(this@DetailActivity)

viewModel.wordListData.observe(this@DetailActivity) { wordList ->
// LiveData를 관찰하여 데이터를 얻고, adapter를 업데이트
Log.d("DetailActivity", "Word list size: ${wordList.size}") // 디버그 로그 추가
recAdapter.setItems(wordList)
recAdapter.notifyDataSetChanged()
}
}
}

}



val backButton = binding.backButton
backButton.setOnClickListener() {
val intent = Intent(this@DetailActivity, MainActivity::class.java)
startActivity(intent)
}

val editButton = binding.detailEditBtn
editButton.setOnClickListener() {
showVerifyDialog(itemId)
viewModel.permission.observe(this@DetailActivity) { permission ->
// LiveData를 관찰하여 데이터를 얻고, adapter를 업데이트
if (permission) {
editButton.visibility = View.GONE
binding.detailRemoveBtn.visibility = View.VISIBLE
binding.detailAddBtn.visibility = View.VISIBLE
}
}

}

val detailRemoveButton = binding.detailRemoveBtn
detailRemoveButton.setOnClickListener() {
lifecycleScope.launch(Dispatchers.IO) {
// 비동기 작업을 수행할 코루틴 블록
val result = viewModel.deleteWordList(itemId, password)
withContext(Dispatchers.Main) {
// 메인 스레드에서 수행할 작업
if (result != "SUCCESS") {
Toast.makeText(this@DetailActivity, result, Toast.LENGTH_SHORT).show()
}
Comment on lines +109 to +111
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기서 return 해야되지 않나요? ㅎㅎ

// 요청 성공 시 메인화면으로 <- 요청 성공/실패 체크]
val intent = Intent(this@DetailActivity, MainActivity::class.java)
startActivity(intent)
Comment on lines +112 to +114
Copy link
Collaborator

Choose a reason for hiding this comment

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

이렇게 하면 메인화면으로 "되돌아가는 것" 이 아니라, MainActivity화면을 새로 만들어서 위에 쌓는 구조가 됩니다. (단어장 삭제하고 나서 뒤로가기를 눌러보면, 스택처럼 두 개의 MainActivity가 쌓여있는 것을 볼 수 있습니다)

그래서 삭제를 했을 때 MainActivity가 그걸 activityResultListener로 전달받은 게 아니라 (챌린지 스펙의 내용), 그냥 새 MainActivitty가 떠서 다시 fetch list를 한 상황입니다.

}
}
}

val detailAddButton = binding.detailAddBtn
detailAddButton.setOnClickListener() {
// 다이얼로그 생성 후 해당 입력값들로 MyData.WordInfo 인스턴스 생성 및 viewModel.putWord()
showWordAddDialog(itemId)
}




}
// Dialog abstract class를 만들어도 될듯...? itemId와 Binding 받아서...
Copy link
Collaborator

Choose a reason for hiding this comment

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

좋은 생각입니다

Dialog를 상속해서 자신의 앱에서 쭉 사용할 커스텀 다이얼로그를 만들어서 사용하는 게 일반적입니다. 레이아웃을 설정하고, 등장/퇴장 및 애니메이션까지 커스텀해서 사용하면 편하죠

private fun showVerifyDialog(itemId: Int) {
val dialogBinding = EditDialogLayoutBinding.inflate(layoutInflater)
val customTitleLayoutBinding = CustomTitleLayoutBinding.inflate(layoutInflater)

val dialog = AlertDialog.Builder(this)
.setView(dialogBinding.root)
.setCustomTitle(customTitleLayoutBinding.root)
.setTitle("수정 권한 확인하기")
.setPositiveButton("Submit") { dialog, _ ->
val inputPassword = dialogBinding.inputPassword.text.toString()
password = inputPassword
// 비밀번호 검증 (뷰모델로 password 전송하고, 뷰모델은 모델로 password 전송.
lifecycleScope.launch(Dispatchers.IO) {
// 비동기 작업을 수행할 코루틴 블록
val result = viewModel.pushPassword(itemId, inputPassword)

withContext(Dispatchers.Main) {
// 메인 스레드에서 수행할 작업
if (result != "SUCCESS") {
// 왜 text가 안 뜨지?
Toast.makeText(this@DetailActivity, result, Toast.LENGTH_SHORT).show()

}
dialog.dismiss()
}
}
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
.create()

dialog.show()
}

private fun showWordAddDialog(itemId: Int) {
val dialogBinding = WordAddDialogLayoutBinding.inflate(layoutInflater)
val customTitleLayoutBinding = CustomTitleLayoutBinding.inflate(layoutInflater)

val dialog = AlertDialog.Builder(this)
.setView(dialogBinding.root)
.setCustomTitle(customTitleLayoutBinding.root)
.setTitle("단어 추가하기")
.setPositiveButton("Submit") { dialog, _ ->
val spell = dialogBinding.dialogWordSpell.text.toString()
val meaning = dialogBinding.dialogWordMeaning.text.toString()
val synonym = dialogBinding.dialogWordSynonym.text?.toString() ?: null
val antonym = dialogBinding.dialogWordAntonym.text?.toString() ?: null
val sentence = dialogBinding.dialogWordSentence.text?.toString() ?: null
val wordDetail = MyData.WordInfo(spell, meaning, synonym, antonym, sentence)
val word = MyData.WordPutInfo(password, wordDetail)
lifecycleScope.launch(Dispatchers.IO) {
// 비동기 작업을 수행할 코루틴 블록
val result = viewModel.putWord(itemId, word)
withContext(Dispatchers.Main) {
// 메인 스레드에서 수행할 작업
if (result != "SUCCESS") {
Toast.makeText(this@DetailActivity, result, Toast.LENGTH_SHORT).show()
}
}
}

dialog.dismiss()
}
.setNegativeButton("Cancel") { dialog, _ ->
dialog.dismiss()
}
.create()

dialog.show()
}


}
Loading