diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 30a1d21..329a7ef 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,8 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) id("kotlin-kapt") + id("com.google.gms.google-services") + } android { @@ -51,6 +53,14 @@ dependencies { // SendBird 라이브러리 추가 implementation(libs.sendbird.chat) + // Firebase 라이브러리 추가 + implementation(platform("com.google.firebase:firebase-bom:33.1.2")) + implementation("com.google.firebase:firebase-analytics") + + // Firebase Database 라이브러리 추가 + implementation(platform("com.google.firebase:firebase-bom:32.3.1")) + implementation("com.google.firebase:firebase-database-ktx") + implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..2a62be8 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "878525628040", + "project_id": "manolja-ef51e", + "storage_bucket": "manolja-ef51e.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:878525628040:android:d0bb43763de81365eba93d", + "android_client_info": { + "package_name": "com.example.manolja" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyAlWZzeOgPSr_KOxMBDFCeMVhWeWBnCNBs" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0068b78..9f400a8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> + - + + + + diff --git a/app/src/main/java/com/example/manolja/data/Coupon.kt b/app/src/main/java/com/example/manolja/data/Coupon.kt new file mode 100644 index 0000000..9dc9088 --- /dev/null +++ b/app/src/main/java/com/example/manolja/data/Coupon.kt @@ -0,0 +1,8 @@ +package com.example.manolja.data + +data class Coupon( + val name: String, + val location: String, + val discount: String, + val content: String +) diff --git a/app/src/main/java/com/example/manolja/data/Message.kt b/app/src/main/java/com/example/manolja/data/Message.kt new file mode 100644 index 0000000..e138f14 --- /dev/null +++ b/app/src/main/java/com/example/manolja/data/Message.kt @@ -0,0 +1,8 @@ +package com.example.manolja.data + +data class Message( + val sender: String = "", + val text: String = "", + val timestamp: Long = System.currentTimeMillis(), + val isCurrentUser: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/example/manolja/data/Quest.kt b/app/src/main/java/com/example/manolja/data/Quest.kt new file mode 100644 index 0000000..421c26f --- /dev/null +++ b/app/src/main/java/com/example/manolja/data/Quest.kt @@ -0,0 +1,7 @@ +package com.example.manolja.data + +data class Quest( + val name: String, + val location: String, + val reward: String +) diff --git a/app/src/main/java/data/RecordItem.kt b/app/src/main/java/com/example/manolja/data/RecordItem.kt similarity index 74% rename from app/src/main/java/data/RecordItem.kt rename to app/src/main/java/com/example/manolja/data/RecordItem.kt index 8a06e67..22be918 100644 --- a/app/src/main/java/data/RecordItem.kt +++ b/app/src/main/java/com/example/manolja/data/RecordItem.kt @@ -1,4 +1,4 @@ -package data +package com.example.manolja.data data class RecordItem( val date: String, diff --git a/app/src/main/java/com/example/manolja/domain/Coupon.kt b/app/src/main/java/com/example/manolja/domain/Coupon.kt new file mode 100644 index 0000000..3e86c5d --- /dev/null +++ b/app/src/main/java/com/example/manolja/domain/Coupon.kt @@ -0,0 +1,8 @@ +package com.example.manolja.domain + +data class Coupon( + val name: String, + val location: String, + val discount: String, + val content: String +) diff --git a/app/src/main/java/com/example/manolja/domain/Message.kt b/app/src/main/java/com/example/manolja/domain/Message.kt new file mode 100644 index 0000000..8ad7b2d --- /dev/null +++ b/app/src/main/java/com/example/manolja/domain/Message.kt @@ -0,0 +1,8 @@ +package com.example.manolja.domain + +data class Message( + val sender: String = "", + val text: String = "", + val timestamp: Long = System.currentTimeMillis(), + val isCurrentUser: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/example/manolja/domain/Quest.kt b/app/src/main/java/com/example/manolja/domain/Quest.kt new file mode 100644 index 0000000..18a2f16 --- /dev/null +++ b/app/src/main/java/com/example/manolja/domain/Quest.kt @@ -0,0 +1,7 @@ +package com.example.manolja.domain + +data class Quest( + val name: String, + val location: String, + val reward: String +) diff --git a/app/src/main/java/com/example/manolja/domain/RecordItem.kt b/app/src/main/java/com/example/manolja/domain/RecordItem.kt new file mode 100644 index 0000000..f1b3804 --- /dev/null +++ b/app/src/main/java/com/example/manolja/domain/RecordItem.kt @@ -0,0 +1,7 @@ +package com.example.manolja.domain + +data class RecordItem( + val date: String, + var caption: String, + val imageUrl: String +) diff --git a/app/src/main/java/com/example/manolja/ui/activity/CertActivity.kt b/app/src/main/java/com/example/manolja/ui/activity/CertActivity.kt new file mode 100644 index 0000000..bac4691 --- /dev/null +++ b/app/src/main/java/com/example/manolja/ui/activity/CertActivity.kt @@ -0,0 +1,93 @@ +package com.example.manolja.ui.activity + +import android.app.ProgressDialog +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.provider.MediaStore +import android.widget.Button +import android.widget.ImageView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.example.manolja.R +import java.io.IOException + +class CertActivity : AppCompatActivity() { + + private val PICK_IMAGE_REQUEST = 1 + private lateinit var imageViewPreview: ImageView + private var imageUri: Uri? = null + private lateinit var progressDialog: ProgressDialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_cert) + + imageViewPreview = findViewById(R.id.imageViewPreview) + val buttonUpload: Button = findViewById(R.id.buttonUpload) + val buttonConfirm: Button = findViewById(R.id.buttonConfirm) + val buttonCancel: Button = findViewById(R.id.buttonCancel) + + // ProgressDialog 초기화 + progressDialog = ProgressDialog(this).apply { + setMessage("인증 중...") + setCancelable(false) + } + + // 사진 업로드 버튼 클릭 리스너 + buttonUpload.setOnClickListener { openFileChooser() } + + // 확인 버튼 클릭 리스너 + buttonConfirm.setOnClickListener { + if (imageUri != null) { + // 로딩 창 표시 + progressDialog.show() + + // 사진 업로드 로직 구현 (예: 서버에 업로드) + // 여기서는 업로드가 완료된 후 로직을 직접 호출합니다. + uploadImageAndFinish() + } else { + Toast.makeText(this, "사진을 선택해 주세요.", Toast.LENGTH_SHORT).show() + } + } + + // 취소 버튼 클릭 리스너 + buttonCancel.setOnClickListener { finish() } // 액티비티 종료 + } + + private fun openFileChooser() { + val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + intent.type = "image/*" + startActivityForResult(intent, PICK_IMAGE_REQUEST) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null && data.data != null) { + imageUri = data.data + try { + val bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri) + imageViewPreview.setImageBitmap(bitmap) + } catch (e: IOException) { + e.printStackTrace() + } + } + } + + private fun uploadImageAndFinish() { + // 업로드 시뮬레이션을 위해 3초 지연 + Handler(Looper.getMainLooper()).postDelayed({ + // 로딩 창 닫기 + progressDialog.dismiss() + + // 결과 전달: 인증 성공 + val resultIntent = Intent() + resultIntent.putExtra("upload_success", true) + setResult(RESULT_OK, resultIntent) + finish() // CertActivity 종료 + }, 3000) // 3000ms = 3초 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/manolja/ui/activity/ChatActivity.kt b/app/src/main/java/com/example/manolja/ui/activity/ChatActivity.kt new file mode 100644 index 0000000..35e5e1b --- /dev/null +++ b/app/src/main/java/com/example/manolja/ui/activity/ChatActivity.kt @@ -0,0 +1,89 @@ +package com.example.manolja.ui.activity + +import android.os.Bundle +import android.widget.Button +import android.widget.EditText +import androidx.appcompat.app.AppCompatActivity +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.example.manolja.R +import com.example.manolja.domain.Message +import com.example.manolja.ui.adapter.MessageAdapter +import com.google.firebase.database.* + +class ChatActivity : AppCompatActivity() { + + private lateinit var messageRecyclerView: RecyclerView + private lateinit var messageAdapter: MessageAdapter + private lateinit var messageInput: EditText + private lateinit var sendButton: Button + + private lateinit var database: DatabaseReference + private lateinit var messages: MutableList + private val currentUser = "김정희" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_chat) + + messageRecyclerView = findViewById(R.id.message_recycler_view) + messageInput = findViewById(R.id.message_input) + sendButton = findViewById(R.id.send_button) + + messages = mutableListOf() + messageAdapter = MessageAdapter(messages) + messageRecyclerView.layoutManager = LinearLayoutManager(this) + messageRecyclerView.adapter = messageAdapter + + // Firebase 데이터베이스 참조 설정 + database = FirebaseDatabase.getInstance().getReference("messages") + + // 메시지 전송 버튼 클릭 리스너 + sendButton.setOnClickListener { + val text = messageInput.text.toString().trim() + if (text.isNotEmpty()) { + sendMessage(text) + } + } + + // Firebase 데이터베이스에서 메시지 불러오기 + loadMessages() + } + + private fun loadMessages() { + database.addChildEventListener(object : ChildEventListener { + override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) { + val message = snapshot.getValue(Message::class.java) + message?.let { + val isCurrentUser = it.sender == currentUser + val newMessage = it.copy(isCurrentUser = isCurrentUser) + messages.add(newMessage) + messageAdapter.notifyItemInserted(messages.size - 1) + messageRecyclerView.scrollToPosition(messages.size - 1) + } + } + + override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) { + // 메시지가 수정된 경우 처리 + } + + override fun onChildRemoved(snapshot: DataSnapshot) { + // 메시지가 삭제된 경우 처리 + } + + override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) { + // 메시지가 이동된 경우 처리 + } + + override fun onCancelled(error: DatabaseError) { + // 데이터 로드 취소 시 처리할 코드 + } + }) + } + + private fun sendMessage(text: String) { + val message = Message(sender = currentUser, text = text, isCurrentUser = true) + database.push().setValue(message) + messageInput.text.clear() + } +} diff --git a/app/src/main/java/com/example/manolja/ui/adapter/MessageAdapter.kt b/app/src/main/java/com/example/manolja/ui/adapter/MessageAdapter.kt new file mode 100644 index 0000000..d5b3b70 --- /dev/null +++ b/app/src/main/java/com/example/manolja/ui/adapter/MessageAdapter.kt @@ -0,0 +1,41 @@ +package com.example.manolja.ui.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.manolja.R +import com.example.manolja.domain.Message + +class MessageAdapter(private val messages: List) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageViewHolder { + val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false) + return MessageViewHolder(view) + } + + override fun onBindViewHolder(holder: MessageViewHolder, position: Int) { + val message = messages[position] + holder.bind(message) + } + + override fun getItemCount(): Int = messages.size + + override fun getItemViewType(position: Int): Int { + val message = messages[position] + return if (message.isCurrentUser) R.layout.item_message_right else R.layout.item_message_left + } + + class MessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val messageText: TextView = itemView.findViewById(R.id.message_text) + private val messageTime: TextView = itemView.findViewById(R.id.message_time) + private val messageSender: TextView = itemView.findViewById(R.id.message_sender) + + fun bind(message: Message) { + messageText.text = message.text + messageTime.text = android.text.format.DateFormat.format("HH:mm", message.timestamp) + messageSender.text = message.sender + } + } +} diff --git a/app/src/main/java/com/example/manolja/ui/adapter/QuestAdapter.kt b/app/src/main/java/com/example/manolja/ui/adapter/QuestAdapter.kt new file mode 100644 index 0000000..16607ec --- /dev/null +++ b/app/src/main/java/com/example/manolja/ui/adapter/QuestAdapter.kt @@ -0,0 +1,65 @@ +package com.example.manolja.ui.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.manolja.R +import com.example.manolja.domain.Coupon + +class QuestAdapter( + private val couponItems: MutableList = mutableListOf(), + private var itemsEnabled: Boolean = false +) : RecyclerView.Adapter() { + + private var itemClickListener: ((String) -> Unit)? = null + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val tvCoupon: TextView = itemView.findViewById(R.id.tvCoupon) + val tvLocation: TextView = itemView.findViewById(R.id.tvLocation) + val tvDiscount: TextView = itemView.findViewById(R.id.tvDiscount) + val tvContent: TextView = itemView.findViewById(R.id.tvContent) + + init { + itemView.setOnClickListener { + if (itemsEnabled) { + val couponName = tvCoupon.text.toString() + itemClickListener?.invoke(couponName) + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.quest_item, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = couponItems[position] + holder.tvCoupon.text = item.name + holder.tvLocation.text = item.location + holder.tvDiscount.text = item.discount + holder.tvContent.text = item.content + holder.itemView.isEnabled = itemsEnabled + } + + override fun getItemCount(): Int = couponItems.size + + fun setItemsEnabled(enabled: Boolean) { + itemsEnabled = enabled + notifyDataSetChanged() + } + + fun setOnItemClickListener(listener: (String) -> Unit) { + itemClickListener = listener + } + + fun addCoupons(newCoupons: List) { + couponItems.clear() + couponItems.addAll(newCoupons) + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/manolja/ui/fragment/RecordAdapter.kt b/app/src/main/java/com/example/manolja/ui/adapter/RecordAdapter.kt similarity index 97% rename from app/src/main/java/com/example/manolja/ui/fragment/RecordAdapter.kt rename to app/src/main/java/com/example/manolja/ui/adapter/RecordAdapter.kt index 9363e92..5b00789 100644 --- a/app/src/main/java/com/example/manolja/ui/fragment/RecordAdapter.kt +++ b/app/src/main/java/com/example/manolja/ui/adapter/RecordAdapter.kt @@ -1,3 +1,5 @@ +package com.example.manolja.ui.adapter + import android.app.AlertDialog import android.view.LayoutInflater import android.view.View @@ -8,7 +10,7 @@ import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.example.manolja.R -import data.RecordItem +import com.example.manolja.domain.RecordItem class RecordAdapter(private val recordItems: List) : RecyclerView.Adapter() { diff --git a/app/src/main/java/com/example/manolja/ui/fragment/HomeFragment.kt b/app/src/main/java/com/example/manolja/ui/fragment/HomeFragment.kt index 1fb7cff..ece322a 100644 --- a/app/src/main/java/com/example/manolja/ui/fragment/HomeFragment.kt +++ b/app/src/main/java/com/example/manolja/ui/fragment/HomeFragment.kt @@ -1,18 +1,111 @@ package com.example.manolja.ui.fragment +import android.app.AlertDialog +import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView import androidx.fragment.app.Fragment import com.example.manolja.R class HomeFragment : Fragment() { + private lateinit var ivMail: ImageView + private lateinit var tvQuestPrompt: TextView + private lateinit var textContainer: LinearLayout + private lateinit var ivCoupon: ImageView + private lateinit var couponDialog: Dialog + private lateinit var layoutTextContainer: LinearLayout + private lateinit var tvNoCoupons: TextView + private var isCouponUsed: Boolean = false + private lateinit var ivHpBar: ImageView + private lateinit var ivBoogiEgg: ImageView + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_home, container, false) + val view = inflater.inflate(R.layout.fragment_home, container, false) + var hp: Int = 1 + + ivMail = view.findViewById(R.id.ivMail) + tvQuestPrompt = view.findViewById(R.id.tvQuestPrompt) + textContainer = view.findViewById(R.id.textContainer) + ivCoupon = view.findViewById(R.id.ivCoupon) + ivHpBar = view.findViewById(R.id.ivHpBar) + ivBoogiEgg = view.findViewById(R.id.ivBoogiEgg) + + ivMail.setOnClickListener { + tvQuestPrompt.visibility = View.INVISIBLE + ivMail.visibility = View.INVISIBLE + textContainer.visibility = View.VISIBLE + } + + ivCoupon.setOnClickListener { + showCouponDialog() + } + + updateImages(hp) + return view + } + + private fun showCouponDialog() { + val dialogView = LayoutInflater.from(context).inflate(R.layout.coupon_dialog, null) + val btnUseCoupon = dialogView.findViewById