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

Jsy star #37

Merged
merged 4 commits into from
Sep 20, 2024
Merged
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
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<activity
android:name=".MainActivity"
android:exported="false"
android:windowSoftInputMode="adjustNothing"
android:screenOrientation="portrait">
</activity>
</application>
Expand Down
153 changes: 153 additions & 0 deletions app/src/main/java/com/devkor/kodaero/AddCategoryDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package com.devkor.kodaero

import android.app.Dialog
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.Gravity
import android.widget.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class AddCategoryDialog(context: Context) : Dialog(context) {

private var onCategoryAddListener: ((String, String) -> Unit)? = null
private var selectedColor: String = "red"
private var selectedButton: ImageButton? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.dialog_add_category)


// Dialog의 배경을 투명하게 설정
window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))

val addButton = findViewById<Button>(R.id.add_button)
val categoryInput = findViewById<EditText>(R.id.category_input)

categoryInput.gravity = Gravity.CENTER

categoryInput.setOnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
categoryInput.gravity = Gravity.CENTER
}
}

categoryInput.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

override fun afterTextChanged(s: Editable?) {
if (s?.length == 0) {
categoryInput.gravity = Gravity.CENTER
} else {
categoryInput.gravity = Gravity.START or Gravity.CENTER_VERTICAL
}
}
})


// Initialize color buttons
val colorButtons = mapOf(
"red" to findViewById<ImageButton>(R.id.color_red),
"orange" to findViewById<ImageButton>(R.id.color_orange),
"yellow" to findViewById<ImageButton>(R.id.color_yellow),
"green" to findViewById<ImageButton>(R.id.color_green),
"blue" to findViewById<ImageButton>(R.id.color_blue),
"purple" to findViewById<ImageButton>(R.id.color_purple),
"pink" to findViewById<ImageButton>(R.id.color_pink)
)

// Set default selected button
selectedButton = colorButtons[selectedColor]
selectedButton?.setBackgroundResource(R.drawable.icon_circle_red_selected) // 기본 선택된 상태로 설정

// Handle color button clicks
for ((color, button) in colorButtons) {
button.setOnClickListener {
selectedButton?.setBackgroundResource(getUnselectedDrawable(selectedColor)) // 이전 선택된 버튼 초기화
selectedColor = color
selectedButton = button
button.setBackgroundResource(getSelectedDrawable(color)) // 선택된 버튼으로 변경
}
}

addButton.setOnClickListener {
val category = categoryInput.text.toString()

if (category.isNotEmpty()) {
onCategoryAddListener?.invoke(category, selectedColor)
sendCategoryToServer(category, selectedColor) // API 요청
dismiss()
} else {
Toast.makeText(context, "모든 필드를 입력하세요.", Toast.LENGTH_SHORT).show()
}
}
}

fun setOnCategoryAddListener(listener: (String, String) -> Unit) {
onCategoryAddListener = listener
}

// API 요청을 보내는 메서드
private fun sendCategoryToServer(category: String, color: String) {
val apiService = RetrofitClient.instance
val accessToken = TokenManager.getAccessToken()


val requestData = CategoryItemRequest(category, color, "") // 데이터 객체 생성

if (accessToken != null) {
apiService.addCategory("$accessToken", requestData).enqueue(object : Callback<ApiResponse<Any>> {
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
if (response.isSuccessful) {
Toast.makeText(context, "카테고리가 추가되었습니다.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "카테고리 추가에 실패했습니다: ${response.code()}", Toast.LENGTH_SHORT).show()
}
}

override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
}
})
} else {
Toast.makeText(context, "액세스 토큰이 없습니다.", Toast.LENGTH_SHORT).show()
}
}


// 선택된 버튼의 드로어블을 반환하는 메서드
private fun getSelectedDrawable(color: String): Int {
return when (color) {
"red" -> R.drawable.icon_circle_red_selected
"orange" -> R.drawable.icon_circle_orange_selected
"yellow" -> R.drawable.icon_circle_yellow_selected
"green" -> R.drawable.icon_circle_green_selected
"blue" -> R.drawable.icon_circle_blue_selected
"purple" -> R.drawable.icon_circle_purple_selected
"pink" -> R.drawable.icon_circle_pink_selected
else -> R.drawable.icon_circle_red_selected
}
}

// 선택되지 않은 버튼의 드로어블을 반환하는 메서드
private fun getUnselectedDrawable(color: String): Int {
return when (color) {
"red" -> R.drawable.icon_circle_red
"orange" -> R.drawable.icon_circle_orange
"yellow" -> R.drawable.icon_circle_yellow
"green" -> R.drawable.icon_circle_green
"blue" -> R.drawable.icon_circle_blue
"purple" -> R.drawable.icon_circle_purple
"pink" -> R.drawable.icon_circle_pink
else -> R.drawable.icon_circle_red
}
}
}
62 changes: 62 additions & 0 deletions app/src/main/java/com/devkor/kodaero/ApiService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.devkor.kodaero

import retrofit2.Call
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.PATCH
Expand Down Expand Up @@ -92,12 +93,42 @@ interface ApiService {
@Body suggestionRequest: SuggestionRequest
): Call<Void>

@GET("categories")
fun getCategories(
@Query("type") type: String,
@Query("id") id: Int
): Call<ApiResponse<CategoryResponse>>

@POST("categories")
fun addCategory(
@Header("AccessToken") accessToken: String,
@Body categoryItem: CategoryItemRequest
): Call<ApiResponse<Any>>


@POST("/api/bookmarks")
fun addBookmarks(@Body request: BookmarkRequest): Call<ApiResponse<Any>>


@GET("/api/categories/{categoryId}/bookmarks")
fun getBookmarks(@Path("categoryId") categoryId: Int): Call<ApiResponse<BookmarkResponse>>

// 북마크 삭제 API 메서드
@DELETE("/api/bookmarks/{bookmarkId}")
fun deleteBookmark(
@Path("bookmarkId") bookmarkId: Int
): Call<ApiResponse<Any>>

@DELETE("/api/categories/{categoryId}")
fun deleteCategory(@Path("categoryId") categoryId: Int): Call<ApiResponse<Any>>

@PATCH("users/username")
fun editUserName(
@Header("AccessToken") accessToken: String,
@Header("RefreshToken") refreshToken: String,
@Query("username") username: String
): Call<Void>

}

data class LoginRequest(
Expand Down Expand Up @@ -286,3 +317,34 @@ data class PlaceInfoResponse(
val xcoord: Int,
val ycoord: Int
)

data class CategoryResponse(
val categoryList: List<CategoryItem>,
val bookmarkId: Int?
)

data class CategoryItem(
val categoryId: Int,
val category: String,
val color: String, // 색상 값
val memo: String,
val bookmarkCount: Int,
val bookmarked: Boolean
)

data class CategoryItemRequest(
val category: String,
val color: String,
val memo: String
)

data class BookmarkResponse(
val bookmarkList: List<Bookmark>
)

data class Bookmark(
val bookmarkId: Int,
val locationType: String,
val locationId: Int,
val memo: String
)
21 changes: 21 additions & 0 deletions app/src/main/java/com/devkor/kodaero/AuthInterceptor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.devkor.kodaero

import okhttp3.Interceptor
import okhttp3.Response

class AuthInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val originalRequest = chain.request()

// TokenManager에서 액세스 토큰을 가져옵니다.
val accessToken = TokenManager.getAccessToken()

// 액세스 토큰이 있는 경우 Authorization 헤더에 추가합니다.
val modifiedRequest = originalRequest.newBuilder()
.addHeader("AccessToken", "$accessToken")
.build()

// 수정된 요청을 진행합니다.
return chain.proceed(modifiedRequest)
}
}
100 changes: 100 additions & 0 deletions app/src/main/java/com/devkor/kodaero/BookmarkManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.devkor.kodaero

import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.core.content.ContentProviderCompat.requireContext
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class BookmarkManager(private val context: Context, private val apiService: ApiService) {

fun addBookmarks(selectedCategories: List<CategoryItem>, locationType: String, locationId: Int, memo: String) {
val categoryIdList = selectedCategories.map { it.categoryId }
val requestBody = BookmarkRequest(categoryIdList, locationType, locationId, memo)

apiService.addBookmarks(requestBody).enqueue(object : Callback<ApiResponse<Any>> {
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
if (response.isSuccessful) {
Toast.makeText(context, "북마크가 성공적으로 저장되었습니다.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "북마크 추가에 실패했습니다: ${response.code()}", Toast.LENGTH_SHORT).show()
Log.e("BookmarkManager", "Error: ${response.errorBody()?.string()}")
}
}

override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
Log.e("BookmarkManager", "Network failure: ${t.message}", t)
}
})
}


fun fetchBookmarks(categoryId: Int, callback: (List<Bookmark>?) -> Unit) {
apiService.getBookmarks(categoryId).enqueue(object : Callback<ApiResponse<BookmarkResponse>> {
override fun onResponse(call: Call<ApiResponse<BookmarkResponse>>, response: Response<ApiResponse<BookmarkResponse>>) {
if (response.isSuccessful) {
val bookmarkResponse = response.body()?.data?.bookmarkList
callback(bookmarkResponse)
} else {
Log.e("FetchDataViewModel", "Failed to fetch bookmarks: ${response.code()}")
callback(null)
}
}

override fun onFailure(call: Call<ApiResponse<BookmarkResponse>>, t: Throwable) {
Log.e("FetchDataViewModel", "Network error: ${t.message}", t)
callback(null)
}
})
}

fun deleteBookmark(bookmarkId: Int) {
val apiService = RetrofitClient.instance

apiService.deleteBookmark(bookmarkId).enqueue(object : Callback<ApiResponse<Any>> {
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
if (response.isSuccessful) {
Toast.makeText(context, "북마크가 삭제되었습니다.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "북마크 삭제에 실패했습니다: ${response.code()}", Toast.LENGTH_SHORT).show()
}
}

override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
}
})
}


// 카테고리 삭제 API 호출
fun deleteCategory(categoryId: Int) {
val apiService = RetrofitClient.instance

apiService.deleteCategory(categoryId).enqueue(object : Callback<ApiResponse<Any>> {
override fun onResponse(call: Call<ApiResponse<Any>>, response: Response<ApiResponse<Any>>) {
if (response.isSuccessful) {
Toast.makeText(context, "카테고리가 삭제되었습니다.", Toast.LENGTH_SHORT).show()
// 필요하다면 UI 갱신 로직 추가
} else {
Toast.makeText(context, "카테고리 삭제에 실패했습니다.", Toast.LENGTH_SHORT).show()
}
}

override fun onFailure(call: Call<ApiResponse<Any>>, t: Throwable) {
Toast.makeText(context, "네트워크 오류: ${t.message}", Toast.LENGTH_SHORT).show()
}
})
}

}

data class BookmarkRequest(
val categoryIdList: List<Int>,
val locationType: String,
val locationId: Int,
val memo: String
)
Loading