JSON 파일 보내기!
package com.example.madcamp_week2 import FavoriteAdapter import User import android.R import android.app.AlertDialog import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.Toast import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import com.example.madcamp_week2.databinding.FragmetStockSearchBinding import okhttp3.ResponseBody import retrofit2.Call import retrofit2.Callback import retrofit2.Response class StockSearchFragment: Fragment() { private var _binding: FragmetStockSearchBinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmetStockSearchBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val searchAdapter = StockDataHolder.stockList?.let { ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, it) } binding.stockSearchSearchInputACTV.setAdapter(searchAdapter) binding.stockSearchSearchInputACTV.setOnItemClickListener { adapterView, view, i, l -> val selectedStock = adapterView.getItemAtPosition(i) as Stock openStockDetailFragment(selectedStock) } // Initialize RecyclerView with LinearLayoutManager binding.stockSearchFavoriteRV.layoutManager = LinearLayoutManager(requireContext()) // Get favorite list from FavoriteHolder val favoriteList = FavoriteHolder.favoriteList // Fetch user from server and compare IDs val userId = UserDataHolder.getUser()?.id ?: return ApiClient.apiService.getUserById(userId).enqueue(object : Callback<User> { override fun onResponse(call: Call<User>, response: Response<User>) { if (response.isSuccessful) { val serverUser = response.body() if (serverUser?.id == userId) { // IDs match, update favorite list on the server ApiClient.apiService.updateFavoriteList(userId, favoriteList).enqueue(object : Callback<ResponseBody> { override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) { if (response.isSuccessful) { // Ensure the context is not null context?.let { Toast.makeText(it, "Favorite list updated successfully", Toast.LENGTH_SHORT).show() } } else { val errorMessage = "Failed to update favorite list: ${response.message()}" context?.let { Toast.makeText(it, errorMessage, Toast.LENGTH_SHORT).show() Log.e("StockSearchFragment", errorMessage) } } } override fun onFailure(call: Call<ResponseBody>, t: Throwable) { val error = "Error: ${t.message}" context?.let { Toast.makeText(it, error, Toast.LENGTH_SHORT).show() Log.e("StockSearchFragment", error, t) } } }) } else { // Handle ID mismatch case context?.let { Toast.makeText(it, "User ID mismatch", Toast.LENGTH_SHORT).show() } } } else { // Handle error case for fetching user val errorMessage = "Failed to fetch user: ${response.message()}" context?.let { Toast.makeText(it, errorMessage, Toast.LENGTH_SHORT).show() Log.e("StockSearchFragment", errorMessage) } } } override fun onFailure(call: Call<User>, t: Throwable) { val error = "Error: ${t.message}" context?.let { Toast.makeText(it, error, Toast.LENGTH_SHORT).show() Log.e("StockSearchFragment", error, t) } } }) // Create adapter and set it to RecyclerView val adapter = FavoriteAdapter(favoriteList) binding.stockSearchFavoriteRV.adapter = adapter adapter.setOnItemClickListener(object : FavoriteAdapter.OnItemClickListener { override fun onCardViewClick(view: View, favoriteItem: String, pos: Int) { showDialog(favoriteItem) } }) } fun showDialog(stockId: String) { val stock = StockDataHolder.stockList?.find { it.id == stockId } if (stock != null) { val stockName = stock.name val dialogBuilder = AlertDialog.Builder(requireContext()) dialogBuilder.setTitle("즐겨찾기 상세 정보") dialogBuilder.setMessage("주식 ID: $stockId\n주식 이름: $stockName") dialogBuilder.setPositiveButton("확인") { dialog, _ -> // 확인 버튼 클릭 시 처리할 로직 (예: 다이얼로그 닫기) dialog.dismiss() } val dialog = dialogBuilder.create() dialog.show() } else { // 주식 객체를 찾을 수 없는 경우에 대한 처리 } } private fun openStockDetailFragment(stock: Stock) { val stockDetailFragment = StockDetailFragment().apply { arguments = Bundle().apply { putString("STOCK_ID", stock.id) putString("STOCK_NAME", stock.name) } } parentFragmentManager.beginTransaction() .replace(com.example.madcamp_week2.R.id.blank_container, stockDetailFragment) .addToBackStack(null) .commit() } }
FavoriteList Holder
import android.util.Log object FavoriteHolder { private val _favoriteList: MutableSet<String> = mutableSetOf() val favoriteList: List<String> get() = _favoriteList.toList() fun addFavorite(stockId: String) { _favoriteList.add(stockId) } fun addAllFavorites(stockIds: List<String>) { _favoriteList.addAll(stockIds) } }
loginActivity
package com.example.madcamp_week2 import User import UserDataHolder import android.content.Intent import android.os.Bundle import android.util.Log import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.google.android.gms.auth.api.Auth import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInAccount import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.auth.api.signin.GoogleSignInClient import com.google.android.gms.auth.api.signin.GoogleSignInResult import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes import com.google.android.gms.common.api.ApiException import com.google.android.gms.tasks.Task import com.google.gson.Gson import com.google.gson.reflect.TypeToken import retrofit2.Call import retrofit2.Callback import retrofit2.Response class LoginActivity : AppCompatActivity() { private lateinit var googleSignInClient: GoogleSignInClient private lateinit var signInButton: Button private val RC_SIGN_IN = 1 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) signInButton = findViewById<Button>(R.id.sign_in_button) signInButton.setOnClickListener { signIn() } // GoogleSignInOptions 설정 val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(getString(R.string.default_web_client_id)) .requestEmail() .build() // GoogleSignInClient 초기화 googleSignInClient = GoogleSignIn.getClient(this, gso) } private fun signIn() { val signInIntent = googleSignInClient.signInIntent startActivityForResult(signInIntent, RC_SIGN_IN) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == RC_SIGN_IN) { val task = GoogleSignIn.getSignedInAccountFromIntent(data) handleSignInResult(task) } } private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) { try { val account = completedTask.getResult(ApiException::class.java) // 구글 로그인 성공 처리 Toast.makeText(this, "구글 로그인 성공: ${account?.displayName}", Toast.LENGTH_SHORT).show() // 사용자 정보 구글 서버에서 받아서 서버에 전송 UserDataHolder.setUser(account) val user = UserDataHolder.getUser() if (user != null) { // 사용자가 이미 서버에 등록되어 있는지 확인 후 처리 checkUserOnServer(user) } else { Log.e("handleSignInResult", "User data is null") Toast.makeText(this, "사용자 데이터를 가져올 수 없습니다.", Toast.LENGTH_SHORT).show() } // MainActivity로 이동 val intent = Intent(this, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK startActivity(intent) finish() // LoginActivity 종료 } catch (e: ApiException) { // 구글 로그인 실패 처리 Log.w("GoogleSignIn", "fail to login: ${e.message}") Toast.makeText(this, "구글 로그인 실패: ${e.message}", Toast.LENGTH_SHORT).show() } } private fun checkUserOnServer(user: User) { // 서버에서 사용자 정보 확인 ApiClient.apiService.getUserById(user.id).enqueue(object : Callback<User> { override fun onResponse(call: Call<User>, response: Response<User>) { if (response.isSuccessful) { // 이미 등록된 사용자인 경우 val serverUser = response.body() serverUser?.favorites?.let { favoritesJson -> val gson = Gson() val type = object : TypeToken<List<String>>() {}.type val favoritesList: List<String> = gson.fromJson(favoritesJson, type) UserDataHolder.addAllFavorites(favoritesList) Log.d("StockSearchFragment_inLogin", "Favorite list: ${UserDataHolder.favoriteList}") } Log.d("checkUserOnServer", "User already exists on server: ${response.body()}") Toast.makeText(this@LoginActivity, "서버에 이미 등록된 사용자입니다.", Toast.LENGTH_SHORT).show() } else { // 등록되지 않은 사용자인 경우 Log.d("checkUserOnServer", "User not found on server, sending user data...") sendUserDataToServer(user) } } override fun onFailure(call: Call<User>, t: Throwable) { // 서버 요청 실패 처리 Log.e("checkUserOnServer", "Failed to check user on server: ${t.message}") Toast.makeText(this@LoginActivity, "서버 사용자 확인 실패: ${t.message}", Toast.LENGTH_SHORT).show() } }) } private fun sendUserDataToServer(user: User) { Log.d("sendUserDataToServer", "Sending user data to server: $user") val call = ApiClient.apiService.createUser(user) call.enqueue(object : Callback<User> { override fun onResponse(call: Call<User>, response: Response<User>) { if (response.isSuccessful) { Log.d("sendUserDataToServer", "서버에 사용자 정보 저장 성공: ${response.body()}") Toast.makeText(this@LoginActivity, "서버에 사용자 정보 저장 성공", Toast.LENGTH_SHORT).show() } else { Log.d("sendUserDataToServer", "fail to save: ${response.code()}") Toast.makeText(this@LoginActivity, "서버에 사용자 정보 저장 실패: ${response.code()}", Toast.LENGTH_SHORT).show() } } override fun onFailure(call: Call<User>, t: Throwable) { Log.e("sendUserDataToServer", "fail to request: ${t.message}") Toast.makeText(this@LoginActivity, "서버 요청 실패: ${t.message}", Toast.LENGTH_SHORT).show() } }) } }
search keep
package com.example.madcamp_week2 import FavoriteAdapter import User import UserDataHolder import android.R import android.app.AlertDialog import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.Toast import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import com.example.madcamp_week2.databinding.FragmetStockSearchBinding import com.google.gson.Gson import com.google.gson.reflect.TypeToken import okhttp3.ResponseBody import retrofit2.Call import retrofit2.Callback import retrofit2.Response class StockSearchFragment: Fragment() { private var _binding: FragmetStockSearchBinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmetStockSearchBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val searchAdapter = StockDataHolder.stockList?.let { ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, it) } binding.stockSearchSearchInputACTV.setAdapter(searchAdapter) binding.stockSearchSearchInputACTV.setOnItemClickListener { adapterView, view, i, l -> val selectedStock = adapterView.getItemAtPosition(i) as Stock openStockDetailFragment(selectedStock) } // Get favorite list from FavoriteHolder // val favoriteList = UserDataHolder.favoriteList val favoriteNameList = UserDataHolder.favoriteList.mapNotNull { stock -> stock.name } Log.d("StockSearchFragment", "Favorite list: $favoriteNameList") // Create adapter and set it to RecyclerView val adapter = FavoriteAdapter(favoriteList) binding.stockSearchFavoriteRV.layoutManager = LinearLayoutManager(requireContext()) binding.stockSearchFavoriteRV.adapter = adapter adapter.setOnItemClickListener(object : FavoriteAdapter.OnItemClickListener { override fun onCardViewClick(view: View, favoriteItem: String, pos: Int) { } }) } private fun openStockDetailFragment(stock: Stock) { val stockDetailFragment = StockDetailFragment().apply { arguments = Bundle().apply { putString("STOCK_ID", stock.id) putString("STOCK_NAME", stock.name) putString("STOCK_MARKET", stock.market) } } parentFragmentManager.beginTransaction() .replace(com.example.madcamp_week2.R.id.blank_container, stockDetailFragment) .addToBackStack(null) .commit() } }
adapter Keep
import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.cardview.widget.CardView import androidx.recyclerview.widget.RecyclerView import com.example.madcamp_week2.R class FavoriteAdapter(private var itemList: List<String>) : RecyclerView.Adapter<FavoriteAdapter.Holder>() { interface OnItemClickListener { fun onCardViewClick(view: View, favoriteItem: String, pos: Int) } private var listener: OnItemClickListener? = null fun setOnItemClickListener(listener: OnItemClickListener) { this.listener = listener } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { val view = LayoutInflater.from(parent.context).inflate(R.layout.favorite_list, parent, false) return Holder(view) } override fun getItemCount(): Int { return itemList.size } override fun onBindViewHolder(holder: Holder, position: Int) { val favoriteItem = itemList[position] holder.bind(favoriteItem, position) } inner class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val favoriteName: TextView = itemView.findViewById(R.id.favorite_name_TV) private val cardView: CardView = itemView.findViewById(R.id.favorite_view_CV) fun bind(favoriteItem: String, position: Int) { favoriteName.text = favoriteItem cardView.setOnClickListener { listener?.onCardViewClick(itemView, favoriteItem, position) } } } }
nevigation bar keep
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/tab1" android:enabled="true" android:title="search" app:showAsAction="ifRoom" android:background="@drawable/baseline_search_24"/> <item android:id="@+id/tab2" android:enabled="true" android:title="add strategy" app:showAsAction="ifRoom" android:background="@drawable/baseline_add_24"/> <item android:id="@+id/tab3" android:enabled="true" android:title="my strategy" app:showAsAction="ifRoom" android:background="@drawable/baseline_list_24"/> </menu>
ToDo
DB 옮기기
JSON 파일 읽기
Null일 때 eror
- UI 디자인
- 즐겨 찾기 업그레이드 → 버튼: 즐겨 찾기 추가!, 즐겨 찾기 추가 취소!
