From 310ca1335689ea0e79029cf535a075adaba622cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=A0=EC=A7=84=ED=98=B8?= <90592183+wktkdandp@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:19:23 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EC=82=AD=EC=A0=9C=20d?= =?UTF-8?q?ialog=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/placereview/PlaceReviewFragment.kt | 16 +++++++++++++--- app/src/main/res/layout/row_place_review.xml | 7 +++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/petpal/mungmate/ui/placereview/PlaceReviewFragment.kt b/app/src/main/java/com/petpal/mungmate/ui/placereview/PlaceReviewFragment.kt index 6f6edaf5..e86b7ccc 100644 --- a/app/src/main/java/com/petpal/mungmate/ui/placereview/PlaceReviewFragment.kt +++ b/app/src/main/java/com/petpal/mungmate/ui/placereview/PlaceReviewFragment.kt @@ -1,5 +1,7 @@ package com.petpal.mungmate.ui.placereview +import android.app.AlertDialog +import android.app.Dialog import android.content.Context import android.os.Bundle import android.util.Log @@ -147,17 +149,25 @@ class PlaceReviewFragment : Fragment() { bundle.putString("reviewImageURL", review.imageRes) bundle.putString("placeId",placeId) mainActivity.navigate(R.id.action_placeReviewFragment_to_placeReviewModifyFragment,bundle) - // Toast.makeText(holder.itemView.context, "Modify clicked for position $position", Toast.LENGTH_SHORT).show() } holder.placeReviewDelete.setOnClickListener { - viewModel.deleteReviewForPlace(placeId, reviewId) + val builder = AlertDialog.Builder(it.context) + builder.setTitle("멍메이트") + builder.setMessage("정말로 이 리뷰를 삭제하시겠습니까?") + builder.setPositiveButton("확인") { dialog, _ -> + viewModel.deleteReviewForPlace(placeId, reviewId) + dialog.dismiss() + } + builder.setNegativeButton("취소") { dialog, _ -> + dialog.dismiss() + } + builder.create().show() } } override fun getItemCount(): Int = reviews.size - // This function updates the reviews list and notifies the RecyclerView of the changes fun updateReviews(newReviews: List) { reviews = newReviews notifyDataSetChanged() diff --git a/app/src/main/res/layout/row_place_review.xml b/app/src/main/res/layout/row_place_review.xml index d4210a15..13eae89e 100644 --- a/app/src/main/res/layout/row_place_review.xml +++ b/app/src/main/res/layout/row_place_review.xml @@ -69,8 +69,8 @@ + android:gravity="end" + android:orientation="horizontal"> From 89f902ddd2caf1172b065fde9edca559e6f3488b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B3=A0=EC=A7=84=ED=98=B8?= <90592183+wktkdandp@users.noreply.github.com> Date: Wed, 20 Sep 2023 17:06:59 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EC=82=B0=EC=B1=85=20=EC=8B=9C=EC=9E=91?= =?UTF-8?q?=EB=B6=80=ED=84=B0=20=EC=8B=9C=EA=B0=84,=EA=B1=B0=EB=A6=AC=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0/=EB=82=B4=20=ED=98=84=EC=9E=AC=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=8B=A4=EC=8B=9C=EA=B0=84=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8/=EC=82=B0=EC=B1=85=EA=B8=B0=EB=A1=9D(?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=ED=8F=AC=ED=95=A8)=20db=EC=97=90=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/petpal/mungmate/model/WalkRecord.kt | 14 ++ .../mungmate/ui/WalkReviewWriteFragment.kt | 163 ++++++++++++++++++ .../petpal/mungmate/ui/walk/WalkFragment.kt | 109 +++++++++++- app/src/main/res/drawable/stop.png | Bin 0 -> 8352 bytes app/src/main/res/layout/fragment_walk.xml | 3 +- .../res/layout/fragment_walk_review_write.xml | 93 ++++++++++ app/src/main/res/navigation/nav_graph.xml | 12 ++ 7 files changed, 391 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/petpal/mungmate/model/WalkRecord.kt create mode 100644 app/src/main/java/com/petpal/mungmate/ui/WalkReviewWriteFragment.kt create mode 100644 app/src/main/res/drawable/stop.png create mode 100644 app/src/main/res/layout/fragment_walk_review_write.xml diff --git a/app/src/main/java/com/petpal/mungmate/model/WalkRecord.kt b/app/src/main/java/com/petpal/mungmate/model/WalkRecord.kt new file mode 100644 index 00000000..46344ca1 --- /dev/null +++ b/app/src/main/java/com/petpal/mungmate/model/WalkRecord.kt @@ -0,0 +1,14 @@ +package com.petpal.mungmate.model + +data class WalkRecord( + val walkRecorduid:String, //산책 기록자 uid + val walkRecordDate:String, //기록한 날짜 + val walkRecordStartTime:String, //시작시간 + val walkRecordEndTime:String, //종료시간 + val walkDuration:String, //소요시간 + val walkDistance:Double, //거리 + val walkMatchingId:String?=null, //산책 상대 uid + val walkMemo:String, //메모 + val walkPhoto:String?=null //사진 + +) diff --git a/app/src/main/java/com/petpal/mungmate/ui/WalkReviewWriteFragment.kt b/app/src/main/java/com/petpal/mungmate/ui/WalkReviewWriteFragment.kt new file mode 100644 index 00000000..af2ee7bf --- /dev/null +++ b/app/src/main/java/com/petpal/mungmate/ui/WalkReviewWriteFragment.kt @@ -0,0 +1,163 @@ +package com.petpal.mungmate.ui + +import android.app.Activity +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.fragment.app.Fragment +import com.bumptech.glide.Glide +import com.bumptech.glide.request.target.SimpleTarget +import com.bumptech.glide.request.transition.Transition +import com.google.android.material.snackbar.Snackbar +import com.google.firebase.auth.ktx.auth +import com.google.firebase.firestore.FieldValue +import com.google.firebase.firestore.FirebaseFirestore +import com.google.firebase.ktx.Firebase +import com.google.firebase.storage.FirebaseStorage +import com.petpal.mungmate.MainActivity + +import com.petpal.mungmate.R +import com.petpal.mungmate.databinding.FragmentWalkReviewWriteBinding +import com.petpal.mungmate.model.Place +import com.petpal.mungmate.model.Review +import com.petpal.mungmate.model.WalkRecord +import com.petpal.mungmate.ui.placereview.WritePlaceReviewFragment +import java.io.ByteArrayOutputStream +import java.util.UUID + + +class WalkReviewWriteFragment : Fragment() { + private lateinit var fragmentWalkReviewWriteBinding: FragmentWalkReviewWriteBinding + private lateinit var mainActivity: MainActivity + private val db = FirebaseFirestore.getInstance() + private val storage = FirebaseStorage.getInstance() + private var selectedImageUri: Uri? = null + private val auth = Firebase.auth + private lateinit var userId:String + + + + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + fragmentWalkReviewWriteBinding= FragmentWalkReviewWriteBinding.inflate(layoutInflater) + mainActivity=activity as MainActivity + + val user=auth.currentUser + userId=user!!.uid + val walkRecorduid=arguments?.getString("walkRecorduid") + val walkRecordDate=arguments?.getString("walkRecordDate") + val walkRecordStartTime=arguments?.getString("walkRecordStartTime") + val walkRecordEndTime=arguments?.getString("walkRecordEndTime") + val walkDuration=arguments?.getString("walkDuration") + val walkDistance=arguments?.getString("walkDistance") + val walkMatchingId=arguments?.getString("walkMatchingId") + + fragmentWalkReviewWriteBinding.imageViewWalk.setOnClickListener { + selectImageFromGallery() + } + + fragmentWalkReviewWriteBinding.buttonWalkReviewSubmit.setOnClickListener { + val walkMemo=fragmentWalkReviewWriteBinding.editTextWalkContent.text.toString() + val walkPhoto=selectedImageUri + if (walkPhoto != null) { + uploadImageToStorage(walkPhoto) { walkPhoto -> + val walkReview=WalkRecord(walkRecorduid!!,walkRecordDate!!,walkRecordStartTime!!,walkRecordEndTime!!,walkDuration!!,walkDistance!!.toDouble(),walkMatchingId, + walkMemo,walkPhoto) + addWalkReview(userId,walkReview) + // 리뷰 등록 후 Navigation 이동 + mainActivity.navigate(R.id.action_WriteWalkReviewFragment_to_mainFragment) + } + } + } + + + return fragmentWalkReviewWriteBinding.root + } + + fun addWalkReview(userId: String, walkReview: WalkRecord) { + val userRef = db.collection("users").document(userId) + + userRef.update("walkRecordList", FieldValue.arrayUnion(walkReview)) + .addOnSuccessListener { + Toast.makeText(context, "리뷰가 성공적으로 등록되었습니다.", Toast.LENGTH_SHORT).show() + } + .addOnFailureListener { e -> + + Toast.makeText(context, "리뷰 등록 실패 .", Toast.LENGTH_SHORT).show() + } + } + + + + private fun selectImageFromGallery() { + val intent = Intent(Intent.ACTION_PICK) + intent.type = "image/*" + startActivityForResult(intent, IMAGE_PICK_CODE1) + } + + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + + if (resultCode == Activity.RESULT_OK && requestCode == IMAGE_PICK_CODE1) { + selectedImageUri = data?.data + fragmentWalkReviewWriteBinding.imageViewWalk.setImageURI(selectedImageUri) + } + } + + private fun uploadImageToStorage(uri: Uri, onSuccess: (String) -> Unit) { + showProgress() + + Glide.with(this) + .asBitmap() + .load(uri) + .override(400, 400) + .into(object : SimpleTarget() { + override fun onResourceReady(resource: Bitmap, transition: Transition?) { + val byteArrayOutputStream = ByteArrayOutputStream() + resource.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream) + val byteArray = byteArrayOutputStream.toByteArray() + + val ref = storage.reference.child("reviews/${UUID.randomUUID()}.jpg") + ref.putBytes(byteArray) + .addOnSuccessListener { + ref.downloadUrl.addOnSuccessListener { + hideProgress() + onSuccess(it.toString()) + //showSnackbar("리뷰가 성공적으로 등록되었습니다.") + Toast.makeText(context, "리뷰가 성공적으로 등록되었습니다.", Toast.LENGTH_SHORT).show() + } + } + .addOnFailureListener { + fragmentWalkReviewWriteBinding.progressBarWalk.visibility = View.GONE + Toast.makeText(context, "이미지 업로드 실패", Toast.LENGTH_SHORT).show() + } + } + }) + } + + private fun showSnackbar(message: String) { + Snackbar.make(fragmentWalkReviewWriteBinding.root, message, Snackbar.LENGTH_SHORT).show() + } + fun showProgress() { + fragmentWalkReviewWriteBinding.progressBarWalk.visibility = View.VISIBLE + fragmentWalkReviewWriteBinding.progressBackgroundWalk.visibility = View.VISIBLE + } + + fun hideProgress() { + fragmentWalkReviewWriteBinding.progressBarWalk.visibility = View.GONE + fragmentWalkReviewWriteBinding.progressBackgroundWalk.visibility = View.GONE + } + companion object { + private const val IMAGE_PICK_CODE1 = 1000 + } + +} diff --git a/app/src/main/java/com/petpal/mungmate/ui/walk/WalkFragment.kt b/app/src/main/java/com/petpal/mungmate/ui/walk/WalkFragment.kt index d6216921..82f27384 100644 --- a/app/src/main/java/com/petpal/mungmate/ui/walk/WalkFragment.kt +++ b/app/src/main/java/com/petpal/mungmate/ui/walk/WalkFragment.kt @@ -1,6 +1,7 @@ package com.petpal.mungmate.ui.walk import android.Manifest +import android.annotation.SuppressLint import android.app.Dialog import android.content.pm.PackageManager import android.graphics.Color @@ -9,6 +10,7 @@ import android.location.Location import android.os.Bundle import android.os.Handler import android.os.Looper +import android.text.format.DateUtils.formatElapsedTime import android.util.Log import android.view.LayoutInflater import android.view.View @@ -32,6 +34,9 @@ import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationCallback +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationResult import com.google.android.gms.location.LocationServices import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog @@ -51,6 +56,10 @@ import kotlinx.coroutines.launch import net.daum.mf.map.api.MapPOIItem import net.daum.mf.map.api.MapPoint import net.daum.mf.map.api.MapView +import java.security.AccessController.checkPermission +import java.text.SimpleDateFormat +import java.util.Date + class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListener, net.daum.mf.map.api.MapView.CurrentLocationEventListener, net.daum.mf.map.api.MapView.MapViewEventListener { private lateinit var fragmentWalkBinding: FragmentWalkBinding @@ -69,6 +78,20 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene private val auth = Firebase.auth private lateinit var userId:String private lateinit var userNickname:String + private var lastLocation: Location? = null + private var totalDistance = 0.0 + private var elapsedTime = 0L + private var userLocationMarker: MapPOIItem? = null + private var startTimestamp: String ="0" + private val handler = Handler(Looper.getMainLooper()) + val timerRunnable = object : Runnable { + override fun run() { + elapsedTime += 1 + fragmentWalkBinding.textViewWalkTime.text = formatElapsedTime(elapsedTime) + handler.postDelayed(this, 1000) + } + } + companion object { const val REQUEST_LOCATION_PERMISSION = 1 @@ -89,6 +112,7 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene bottomSheetDialog = BottomSheetDialog(requireContext()) bottomSheetDialog.setContentView(initialBottomSheetView) + setupMapView() setupButtonListeners() observeViewModel() @@ -114,7 +138,6 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene fragmentWalkBinding.mapView.setPOIItemEventListener(this) fragmentWalkBinding.mapView.setCurrentLocationEventListener(this) fragmentWalkBinding.mapView.setMapViewEventListener(this) - requestLocationPermissionIfNeeded() } @@ -122,11 +145,16 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene fragmentWalkBinding.buttonWalk.setOnClickListener { toggleVisibility(fragmentWalkBinding.LinearLayoutOnWalk, fragmentWalkBinding.LinearLayoutOffWalk) fragmentWalkBinding.imageViewWalkToggle.setImageResource(R.drawable.dog_walk) + handler.post(timerRunnable) + startLocationUpdates() + startTimestamp = timestampToString(System.currentTimeMillis()) + } fragmentWalkBinding.chipMapFilter.setOnClickListener { fragmentWalkBinding.drawerLayout.setScrimColor(Color.parseColor("#FFFFFF")) fragmentWalkBinding.drawerLayout.openDrawer(GravityCompat.END) + } fragmentWalkBinding.buttonFilterSubmit.setOnClickListener { @@ -155,6 +183,21 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene fragmentWalkBinding.buttonStopWalk.setOnClickListener { toggleVisibility(fragmentWalkBinding.LinearLayoutOffWalk, fragmentWalkBinding.LinearLayoutOnWalk) fragmentWalkBinding.imageViewWalkToggle.setImageResource(R.drawable.dog_home) + + val endTimestamp=timestampToString(System.currentTimeMillis()) + val bundle=Bundle() + bundle.putString("walkRecorduid",userId) + bundle.putString("walkRecordDate",getCurrentDate()) + bundle.putString("walkRecordStartTime",startTimestamp) + bundle.putString("walkRecordEndTime",endTimestamp) + bundle.putString("walkDuration",elapsedTime.toString()) + bundle.putString("walkDistance",totalDistance.toString()) + bundle.putString("walkMatchingId","idid") + stopLocationUpdates() + handler.removeCallbacks(timerRunnable) + elapsedTime = 0L + fragmentWalkBinding.textViewWalkTime.text = formatElapsedTime(elapsedTime) + mainActivity.navigate(R.id.action_mainFragment_to_WriteWalkReviewFragment,bundle) } fragmentWalkBinding.imageViewMylocation.setOnClickListener { getCurrentLocation() @@ -162,7 +205,12 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene LastKnownLocation.longitude= null } } - + @SuppressLint("SimpleDateFormat") + fun timestampToString(timestamp: Long): String { + val date = Date(timestamp) + val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + return formatter.format(date) + } private fun observeViewModel() { @@ -243,10 +291,60 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene } } } + private fun checkPermission(permission: String): Boolean { return ActivityCompat.checkSelfPermission(requireContext(), permission) == PackageManager.PERMISSION_GRANTED } + private fun showUserLocationOnMap(location: Location) { + userLocationMarker?.let { + fragmentWalkBinding.mapView.removePOIItem(it) + } + userLocationMarker = MapPOIItem().apply { + itemName = "Current Location" + mapPoint = MapPoint.mapPointWithGeoCoord(location.latitude, location.longitude) + markerType = MapPOIItem.MarkerType.BluePin + //selectedMarkerType = MapPOIItem.MarkerType.RedPin + } + fragmentWalkBinding.mapView.addPOIItem(userLocationMarker) + fragmentWalkBinding.mapView.setMapCenterPointAndZoomLevel(MapPoint.mapPointWithGeoCoord(location.latitude, location.longitude), 1, true) + } + + private val locationCallback = object : LocationCallback() { + override fun onLocationResult(p0: LocationResult) { + p0 ?: return + for (location in p0.locations) { + // 정확도 체크 + if (location.accuracy <= 3) { + lastLocation?.let { + totalDistance += it.distanceTo(location).toDouble() + // UI 업데이트 + fragmentWalkBinding.textViewWalkDistance.text = "${String.format("%.1f", totalDistance)} m" + } + showUserLocationOnMap(location) + lastLocation = location + } + } + } + } + private fun startLocationUpdates() { + if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { + val locationRequest = LocationRequest.create().apply { + interval = 2000 // 10 seconds + fastestInterval = 5000 // 5 seconds + priority = LocationRequest.PRIORITY_HIGH_ACCURACY + } + fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper()) + } + } + + private fun stopLocationUpdates() { + fusedLocationClient.removeLocationUpdates(locationCallback) + totalDistance=0.0 + fragmentWalkBinding.textViewWalkDistance.text=totalDistance.toString() + } + + //LastKnownLocation의 위치에서 검색 마킹(원래 있던 마커들 지우고) private fun getLastLocation() { if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION) @@ -271,6 +369,7 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene } } } + //거리 필터의 값에 따라 ㄱ private fun getLastLocationFilter(radius:Int) { if (checkPermission(Manifest.permission.ACCESS_FINE_LOCATION) @@ -322,6 +421,12 @@ class WalkFragment : Fragment(), net.daum.mf.map.api.MapView.POIItemEventListene private fun showSnackbar(message: String) { Snackbar.make(fragmentWalkBinding.root, message, Snackbar.LENGTH_LONG).show() } + @SuppressLint("SimpleDateFormat") + fun getCurrentDate(): String { + val current = Date() + val formatter = SimpleDateFormat("yyyy-MM-dd") // 년-월-일 + return formatter.format(current) + } //맵의 마커 클릭시 override fun onPOIItemSelected(p0: net.daum.mf.map.api.MapView?, p1: MapPOIItem?) { diff --git a/app/src/main/res/drawable/stop.png b/app/src/main/res/drawable/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..84233e7d58fbe717b03d54e793f4064735b19bf1 GIT binary patch literal 8352 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 diff --git a/app/src/main/res/layout/fragment_walk_review_write.xml b/app/src/main/res/layout/fragment_walk_review_write.xml new file mode 100644 index 00000000..4d25b821 --- /dev/null +++ b/app/src/main/res/layout/fragment_walk_review_write.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + +