Skip to content

Commit b901197

Browse files
authored
Merge pull request #37 from DevKor-github/jsy_star
Jsy star
2 parents 5c2c3c5 + bf18bec commit b901197

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2041
-21
lines changed

app/src/main/AndroidManifest.xml

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
<activity
5252
android:name=".MainActivity"
5353
android:exported="false"
54+
android:windowSoftInputMode="adjustNothing"
5455
android:screenOrientation="portrait">
5556
</activity>
5657
</application>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package com.devkor.kodaero
2+
3+
import android.app.Dialog
4+
import android.content.Context
5+
import android.graphics.Color
6+
import android.graphics.drawable.ColorDrawable
7+
import android.os.Bundle
8+
import android.text.Editable
9+
import android.text.TextWatcher
10+
import android.view.Gravity
11+
import android.widget.*
12+
import retrofit2.Call
13+
import retrofit2.Callback
14+
import retrofit2.Response
15+
16+
class AddCategoryDialog(context: Context) : Dialog(context) {
17+
18+
private var onCategoryAddListener: ((String, String) -> Unit)? = null
19+
private var selectedColor: String = "red"
20+
private var selectedButton: ImageButton? = null
21+
22+
override fun onCreate(savedInstanceState: Bundle?) {
23+
super.onCreate(savedInstanceState)
24+
setContentView(R.layout.dialog_add_category)
25+
26+
27+
// Dialog의 배경을 투명하게 설정
28+
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
29+
30+
val addButton = findViewById<Button>(R.id.add_button)
31+
val categoryInput = findViewById<EditText>(R.id.category_input)
32+
33+
categoryInput.gravity = Gravity.CENTER
34+
35+
categoryInput.setOnFocusChangeListener { _, hasFocus ->
36+
if (hasFocus) {
37+
categoryInput.gravity = Gravity.CENTER
38+
}
39+
}
40+
41+
categoryInput.addTextChangedListener(object : TextWatcher {
42+
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
43+
44+
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
45+
46+
override fun afterTextChanged(s: Editable?) {
47+
if (s?.length == 0) {
48+
categoryInput.gravity = Gravity.CENTER
49+
} else {
50+
categoryInput.gravity = Gravity.START or Gravity.CENTER_VERTICAL
51+
}
52+
}
53+
})
54+
55+
56+
// Initialize color buttons
57+
val colorButtons = mapOf(
58+
"red" to findViewById<ImageButton>(R.id.color_red),
59+
"orange" to findViewById<ImageButton>(R.id.color_orange),
60+
"yellow" to findViewById<ImageButton>(R.id.color_yellow),
61+
"green" to findViewById<ImageButton>(R.id.color_green),
62+
"blue" to findViewById<ImageButton>(R.id.color_blue),
63+
"purple" to findViewById<ImageButton>(R.id.color_purple),
64+
"pink" to findViewById<ImageButton>(R.id.color_pink)
65+
)
66+
67+
// Set default selected button
68+
selectedButton = colorButtons[selectedColor]
69+
selectedButton?.setBackgroundResource(R.drawable.icon_circle_red_selected) // 기본 선택된 상태로 설정
70+
71+
// Handle color button clicks
72+
for ((color, button) in colorButtons) {
73+
button.setOnClickListener {
74+
selectedButton?.setBackgroundResource(getUnselectedDrawable(selectedColor)) // 이전 선택된 버튼 초기화
75+
selectedColor = color
76+
selectedButton = button
77+
button.setBackgroundResource(getSelectedDrawable(color)) // 선택된 버튼으로 변경
78+
}
79+
}
80+
81+
addButton.setOnClickListener {
82+
val category = categoryInput.text.toString()
83+
84+
if (category.isNotEmpty()) {
85+
onCategoryAddListener?.invoke(category, selectedColor)
86+
sendCategoryToServer(category, selectedColor) // API 요청
87+
dismiss()
88+
} else {
89+
Toast.makeText(context, "모든 필드를 입력하세요.", Toast.LENGTH_SHORT).show()
90+
}
91+
}
92+
}
93+
94+
fun setOnCategoryAddListener(listener: (String, String) -> Unit) {
95+
onCategoryAddListener = listener
96+
}
97+
98+
// API 요청을 보내는 메서드
99+
private fun sendCategoryToServer(category: String, color: String) {
100+
val apiService = RetrofitClient.instance
101+
val accessToken = TokenManager.getAccessToken()
102+
103+
104+
val requestData = CategoryItemRequest(category, color, "") // 데이터 객체 생성
105+
106+
if (accessToken != null) {
107+
apiService.addCategory("$accessToken", requestData).enqueue(object : Callback<ApiResponse<Any>> {
108+
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
109+
if (response.isSuccessful) {
110+
Toast.makeText(context, "카테고리가 추가되었습니다.", Toast.LENGTH_SHORT).show()
111+
} else {
112+
Toast.makeText(context, "카테고리 추가에 실패했습니다: ${response.code()}", Toast.LENGTH_SHORT).show()
113+
}
114+
}
115+
116+
override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
117+
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
118+
}
119+
})
120+
} else {
121+
Toast.makeText(context, "액세스 토큰이 없습니다.", Toast.LENGTH_SHORT).show()
122+
}
123+
}
124+
125+
126+
// 선택된 버튼의 드로어블을 반환하는 메서드
127+
private fun getSelectedDrawable(color: String): Int {
128+
return when (color) {
129+
"red" -> R.drawable.icon_circle_red_selected
130+
"orange" -> R.drawable.icon_circle_orange_selected
131+
"yellow" -> R.drawable.icon_circle_yellow_selected
132+
"green" -> R.drawable.icon_circle_green_selected
133+
"blue" -> R.drawable.icon_circle_blue_selected
134+
"purple" -> R.drawable.icon_circle_purple_selected
135+
"pink" -> R.drawable.icon_circle_pink_selected
136+
else -> R.drawable.icon_circle_red_selected
137+
}
138+
}
139+
140+
// 선택되지 않은 버튼의 드로어블을 반환하는 메서드
141+
private fun getUnselectedDrawable(color: String): Int {
142+
return when (color) {
143+
"red" -> R.drawable.icon_circle_red
144+
"orange" -> R.drawable.icon_circle_orange
145+
"yellow" -> R.drawable.icon_circle_yellow
146+
"green" -> R.drawable.icon_circle_green
147+
"blue" -> R.drawable.icon_circle_blue
148+
"purple" -> R.drawable.icon_circle_purple
149+
"pink" -> R.drawable.icon_circle_pink
150+
else -> R.drawable.icon_circle_red
151+
}
152+
}
153+
}

app/src/main/java/com/devkor/kodaero/ApiService.kt

+62
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.devkor.kodaero
22

33
import retrofit2.Call
44
import retrofit2.http.Body
5+
import retrofit2.http.DELETE
56
import retrofit2.http.GET
67
import retrofit2.http.Header
78
import retrofit2.http.PATCH
@@ -92,12 +93,42 @@ interface ApiService {
9293
@Body suggestionRequest: SuggestionRequest
9394
): Call<Void>
9495

96+
@GET("categories")
97+
fun getCategories(
98+
@Query("type") type: String,
99+
@Query("id") id: Int
100+
): Call<ApiResponse<CategoryResponse>>
101+
102+
@POST("categories")
103+
fun addCategory(
104+
@Header("AccessToken") accessToken: String,
105+
@Body categoryItem: CategoryItemRequest
106+
): Call<ApiResponse<Any>>
107+
108+
109+
@POST("/api/bookmarks")
110+
fun addBookmarks(@Body request: BookmarkRequest): Call<ApiResponse<Any>>
111+
112+
113+
@GET("/api/categories/{categoryId}/bookmarks")
114+
fun getBookmarks(@Path("categoryId") categoryId: Int): Call<ApiResponse<BookmarkResponse>>
115+
116+
// 북마크 삭제 API 메서드
117+
@DELETE("/api/bookmarks/{bookmarkId}")
118+
fun deleteBookmark(
119+
@Path("bookmarkId") bookmarkId: Int
120+
): Call<ApiResponse<Any>>
121+
122+
@DELETE("/api/categories/{categoryId}")
123+
fun deleteCategory(@Path("categoryId") categoryId: Int): Call<ApiResponse<Any>>
124+
95125
@PATCH("users/username")
96126
fun editUserName(
97127
@Header("AccessToken") accessToken: String,
98128
@Header("RefreshToken") refreshToken: String,
99129
@Query("username") username: String
100130
): Call<Void>
131+
101132
}
102133

103134
data class LoginRequest(
@@ -286,3 +317,34 @@ data class PlaceInfoResponse(
286317
val xcoord: Int,
287318
val ycoord: Int
288319
)
320+
321+
data class CategoryResponse(
322+
val categoryList: List<CategoryItem>,
323+
val bookmarkId: Int?
324+
)
325+
326+
data class CategoryItem(
327+
val categoryId: Int,
328+
val category: String,
329+
val color: String, // 색상 값
330+
val memo: String,
331+
val bookmarkCount: Int,
332+
val bookmarked: Boolean
333+
)
334+
335+
data class CategoryItemRequest(
336+
val category: String,
337+
val color: String,
338+
val memo: String
339+
)
340+
341+
data class BookmarkResponse(
342+
val bookmarkList: List<Bookmark>
343+
)
344+
345+
data class Bookmark(
346+
val bookmarkId: Int,
347+
val locationType: String,
348+
val locationId: Int,
349+
val memo: String
350+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.devkor.kodaero
2+
3+
import okhttp3.Interceptor
4+
import okhttp3.Response
5+
6+
class AuthInterceptor : Interceptor {
7+
override fun intercept(chain: Interceptor.Chain): Response {
8+
val originalRequest = chain.request()
9+
10+
// TokenManager에서 액세스 토큰을 가져옵니다.
11+
val accessToken = TokenManager.getAccessToken()
12+
13+
// 액세스 토큰이 있는 경우 Authorization 헤더에 추가합니다.
14+
val modifiedRequest = originalRequest.newBuilder()
15+
.addHeader("AccessToken", "$accessToken")
16+
.build()
17+
18+
// 수정된 요청을 진행합니다.
19+
return chain.proceed(modifiedRequest)
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.devkor.kodaero
2+
3+
import android.content.Context
4+
import android.util.Log
5+
import android.widget.Toast
6+
import androidx.core.content.ContentProviderCompat.requireContext
7+
import retrofit2.Call
8+
import retrofit2.Callback
9+
import retrofit2.Response
10+
11+
class BookmarkManager(private val context: Context, private val apiService: ApiService) {
12+
13+
fun addBookmarks(selectedCategories: List<CategoryItem>, locationType: String, locationId: Int, memo: String) {
14+
val categoryIdList = selectedCategories.map { it.categoryId }
15+
val requestBody = BookmarkRequest(categoryIdList, locationType, locationId, memo)
16+
17+
apiService.addBookmarks(requestBody).enqueue(object : Callback<ApiResponse<Any>> {
18+
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
19+
if (response.isSuccessful) {
20+
Toast.makeText(context, "북마크가 성공적으로 저장되었습니다.", Toast.LENGTH_SHORT).show()
21+
} else {
22+
Toast.makeText(context, "북마크 추가에 실패했습니다: ${response.code()}", Toast.LENGTH_SHORT).show()
23+
Log.e("BookmarkManager", "Error: ${response.errorBody()?.string()}")
24+
}
25+
}
26+
27+
override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
28+
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
29+
Log.e("BookmarkManager", "Network failure: ${t.message}", t)
30+
}
31+
})
32+
}
33+
34+
35+
fun fetchBookmarks(categoryId: Int, callback: (List<Bookmark>?) -> Unit) {
36+
apiService.getBookmarks(categoryId).enqueue(object : Callback<ApiResponse<BookmarkResponse>> {
37+
override fun onResponse(call: Call<ApiResponse<BookmarkResponse>>, response: Response<ApiResponse<BookmarkResponse>>) {
38+
if (response.isSuccessful) {
39+
val bookmarkResponse = response.body()?.data?.bookmarkList
40+
callback(bookmarkResponse)
41+
} else {
42+
Log.e("FetchDataViewModel", "Failed to fetch bookmarks: ${response.code()}")
43+
callback(null)
44+
}
45+
}
46+
47+
override fun onFailure(call: Call<ApiResponse<BookmarkResponse>>, t: Throwable) {
48+
Log.e("FetchDataViewModel", "Network error: ${t.message}", t)
49+
callback(null)
50+
}
51+
})
52+
}
53+
54+
fun deleteBookmark(bookmarkId: Int) {
55+
val apiService = RetrofitClient.instance
56+
57+
apiService.deleteBookmark(bookmarkId).enqueue(object : Callback<ApiResponse<Any>> {
58+
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
59+
if (response.isSuccessful) {
60+
Toast.makeText(context, "북마크가 삭제되었습니다.", Toast.LENGTH_SHORT).show()
61+
} else {
62+
Toast.makeText(context, "북마크 삭제에 실패했습니다: ${response.code()}", Toast.LENGTH_SHORT).show()
63+
}
64+
}
65+
66+
override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
67+
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
68+
}
69+
})
70+
}
71+
72+
73+
// 카테고리 삭제 API 호출
74+
fun deleteCategory(categoryId: Int) {
75+
val apiService = RetrofitClient.instance
76+
77+
apiService.deleteCategory(categoryId).enqueue(object : Callback<ApiResponse<Any>> {
78+
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
79+
if (response.isSuccessful) {
80+
Toast.makeText(context, "카테고리가 삭제되었습니다.", Toast.LENGTH_SHORT).show()
81+
// 필요하다면 UI 갱신 로직 추가
82+
} else {
83+
Toast.makeText(context, "카테고리 삭제에 실패했습니다.", Toast.LENGTH_SHORT).show()
84+
}
85+
}
86+
87+
override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
88+
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
89+
}
90+
})
91+
}
92+
93+
}
94+
95+
data class BookmarkRequest(
96+
val categoryIdList: List<Int>,
97+
val locationType: String,
98+
val locationId: Int,
99+
val memo: String
100+
)

0 commit comments

Comments
 (0)