From 5ef6f061927bb244f41e275d8cbc08e42084abc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=B0=EC=A4=80=ED=98=81?= Date: Tue, 26 Nov 2024 05:31:38 +0900 Subject: [PATCH 01/37] =?UTF-8?q?feat#16:=EC=A7=80=EC=98=A4=ED=8E=9C?= =?UTF-8?q?=EC=8B=B11=EC=B0=A8=EA=B5=AC=ED=98=84,=20=EC=84=B8=EB=B6=84?= =?UTF-8?q?=ED=99=94=EC=9D=B4=EC=A0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetSelector.xml | 4 +- app/src/main/AndroidManifest.xml | 1 + .../java/com/example/mohassu/Constants.java | 35 ++++++++++++++++ .../com/example/mohassu/MapViewActivity.java | 41 ++++++++++++++++++- .../java/com/example/mohassu/PlaceInfo.java | 27 ++++++++++++ .../main/res/layout/activity_home_main.xml | 18 ++++++++ 6 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/example/mohassu/Constants.java create mode 100644 app/src/main/java/com/example/mohassu/PlaceInfo.java diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index 1b43eb4..68660bc 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,10 +4,10 @@ - - \ No newline at end of file From c645ce5a248b438a62601948b22a8b234d879512 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=B0=EC=A4=80=ED=98=81?= Date: Wed, 4 Dec 2024 09:06:51 +0900 Subject: [PATCH 03/37] =?UTF-8?q?feat#16-1:homemain=20=EB=A7=88=EC=BB=A4?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetSelector.xml | 11 -- .../MainFragment/MainHomeFragment.java | 172 +++++++++++++++++- .../main/res/layout/fragment_main_home.xml | 23 ++- 3 files changed, 188 insertions(+), 18 deletions(-) diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index 34f545b..626a65c 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,7 +4,6 @@ - - - - - - ->>>>>>> f3a4626b26d29f268658688e63787ed1c1bb2b7d \ No newline at end of file diff --git a/app/src/main/java/com/example/mohassu/MainFragment/MainHomeFragment.java b/app/src/main/java/com/example/mohassu/MainFragment/MainHomeFragment.java index 8a42f51..e02f090 100644 --- a/app/src/main/java/com/example/mohassu/MainFragment/MainHomeFragment.java +++ b/app/src/main/java/com/example/mohassu/MainFragment/MainHomeFragment.java @@ -1,29 +1,100 @@ package com.example.mohassu.MainFragment; +import android.Manifest; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.location.Location; import android.os.Bundle; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; +import com.example.mohassu.Constants; +import com.example.mohassu.PlaceInfo; import com.example.mohassu.R; +import com.google.android.gms.location.GeofencingClient; +import com.google.android.gms.location.LocationServices; +import com.naver.maps.geometry.LatLng; +import com.naver.maps.map.CameraUpdate; +import com.naver.maps.map.LocationTrackingMode; +import com.naver.maps.map.MapFragment; +import com.naver.maps.map.NaverMap; +import com.naver.maps.map.OnMapReadyCallback; +import com.naver.maps.map.overlay.LocationOverlay; +import com.naver.maps.map.overlay.OverlayImage; +import com.naver.maps.map.util.FusedLocationSource; -public class MainHomeFragment extends Fragment { +public class MainHomeFragment extends Fragment implements OnMapReadyCallback { + + private NaverMap naverMap; + private FusedLocationSource locationSource; + private static final int LOCATION_PERMISSION_REQUEST_CODE = 1000; + + private GeofencingClient geofencingClient; + + // ActivityResultLauncher for permission requests + private final ActivityResultLauncher requestPermissionLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { + if (naverMap != null) { + naverMap.setLocationTrackingMode(LocationTrackingMode.Follow); + } + } else { + Toast.makeText(requireContext(), "위치 권한이 거부되었습니다.", Toast.LENGTH_SHORT).show(); + } + }); @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_main_home, container, false); } @Override - public void onViewCreated(View view, Bundle savedInstanceState) { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + // Initialize MapFragment + MapFragment mapFragment = (MapFragment) getChildFragmentManager().findFragmentById(R.id.fragment_map); + if (mapFragment == null) { + mapFragment = MapFragment.newInstance(); + getChildFragmentManager().beginTransaction().add(R.id.fragment_map, mapFragment).commit(); + } + mapFragment.getMapAsync(this); + + // Initialize LocationSource + locationSource = new FusedLocationSource(this, LOCATION_PERMISSION_REQUEST_CODE); + + geofencingClient = LocationServices.getGeofencingClient(requireContext()); + + // Custom button to center on current location + ImageButton myLocationButton = view.findViewById(R.id.btnNowLocation); + if (myLocationButton != null) { + myLocationButton.setOnClickListener(v -> { + LatLng currentPosition = naverMap.getLocationOverlay().getPosition(); + if (currentPosition != null) { + // Move camera to the current position + naverMap.moveCamera(CameraUpdate.scrollTo(currentPosition)); + } else { // 예외처리 생략 가능 + Toast.makeText(requireContext(), "현재 위치를 찾을 수 없습니다.", Toast.LENGTH_SHORT).show(); + } + }); + } + // NavController 초기화 NavController navController = Navigation.findNavController(view); @@ -59,4 +130,99 @@ public void onViewCreated(View view, Bundle savedInstanceState) { navController.navigate(R.id.actionMyPage); }); } + + @Override + public void onMapReady(@NonNull NaverMap naverMap) { + this.naverMap = naverMap; + + // 초기 좌표를 보이지 않는 위치로 설정 (예: 바다 위의 좌표) + CameraUpdate initialUpdate = CameraUpdate.scrollTo(new LatLng(0, 0)); + naverMap.moveCamera(initialUpdate); + + // 위치 정보 가져오기 + naverMap.setLocationSource(locationSource); + + // +- 줌컨트롤 버튼 비활성화 + naverMap.getUiSettings().setZoomControlEnabled(false); + + // 위치 아이콘 활성화 + LocationOverlay locationOverlay = naverMap.getLocationOverlay(); + locationOverlay.setVisible(true); // Make LocationOverlay visible + + + // 첫 번째 위치 업데이트 여부를 판단하는 플래그 + final boolean[] isFirstUpdate = {true}; + + // 위치 요청 수락 시 트래킹모드 가동, 거부 시 다시 묻기 + if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + naverMap.setLocationTrackingMode(LocationTrackingMode.Follow); + } else { + requestPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION); + } + + updateLocationOverlay(); + + // 위치 변화 업데이트 + naverMap.addOnLocationChangeListener(location -> { + LatLng currentLocation = new LatLng(location.getLatitude(), location.getLongitude()); + locationOverlay.setPosition(currentLocation); + locationOverlay.setBearing(0); + + // 처음 위치 갱신 시에만 카메라 이동 + if (isFirstUpdate[0]) { + CameraUpdate update = CameraUpdate.scrollAndZoomTo(currentLocation, 17.0); // 줌 레벨 17.0 + naverMap.moveCamera(update); + isFirstUpdate[0] = false; // 플래그 업데이트 + } + + + View view = getView(); + TextView tvBuildingName = view.findViewById(R.id.tvBuildingName); + + for (PlaceInfo place : Constants.PLACES) { + float[] results = new float[1]; + Location.distanceBetween( + location.getLatitude(), location.getLongitude(), + place.getLocation().latitude, place.getLocation().longitude, + results + ); + + if (results[0] <= place.getRadius()) { + String buildingName = place.getName(); + tvBuildingName.setText(buildingName + "에 있어요."); + tvBuildingName.setVisibility(View.VISIBLE); + return; // 반경 내 첫 번째 장소를 찾으면 종료 + } + } + tvBuildingName.setVisibility(View.GONE); // 반경 내 장소가 없을 경우 + }); + } + + private void updateLocationOverlay() { + LocationOverlay locationOverlay = naverMap.getLocationOverlay(); + locationOverlay.setVisible(true); + + // 마커 이미지 로드 + Bitmap originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img_marker_red); + + // 이미지 사이즈 조정 + Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, 120, 140, true); + + // 위치 아이콘에 마커 적용 + locationOverlay.setIcon(OverlayImage.fromBitmap(scaledBitmap)); + + // 방향에 따라 마커가 돌아가지 않게 고정 + locationOverlay.setBearing(0); + } + + @Override + public void onResume() { + super.onResume(); + + // naverMap이 초기화된 경우 위치 오버레이를 다시 설정 + if (naverMap != null) { + updateLocationOverlay(); + } + } + } diff --git a/app/src/main/res/layout/fragment_main_home.xml b/app/src/main/res/layout/fragment_main_home.xml index c78c3c9..f23cfeb 100644 --- a/app/src/main/res/layout/fragment_main_home.xml +++ b/app/src/main/res/layout/fragment_main_home.xml @@ -6,11 +6,26 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent" /> + + Date: Wed, 4 Dec 2024 12:23:17 +0900 Subject: [PATCH 04/37] =?UTF-8?q?FEAT/#41=20Realtime=20=EC=97=90=EC=84=9C?= =?UTF-8?q?=20firestore=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/deploymentTargetSelector.xml | 9 - app/build.gradle.kts | 2 + .../Signup1IDAndPWFragment.java | 2 + .../Signup2DetailFragment.java | 196 ++++++++++++++---- .../Signup3ProfileFragment.java | 49 ++++- .../example/mohassu/UserProfileViewModel.java | 47 +++++ 6 files changed, 250 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/example/mohassu/UserProfileViewModel.java diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index a551c90..626a65c 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -13,15 +13,6 @@ - - - - - - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3d8ac8e..2ccabcd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -73,6 +73,8 @@ dependencies { implementation("com.google.firebase:firebase-analytics") implementation("com.google.firebase:firebase-auth") + implementation ("com.google.firebase:firebase-firestore:24.7.0") + implementation ("com.google.firebase:firebase-storage:20.2.0")// 최신 버전 확인 // Naver Map SDK implementation("com.naver.maps:map-sdk:3.19.1") diff --git a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java index 50f4e54..4bfe8e3 100644 --- a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java +++ b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java @@ -11,6 +11,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; @@ -34,6 +35,7 @@ public class Signup1IDAndPWFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_sign_up1, container, false); } diff --git a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup2DetailFragment.java b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup2DetailFragment.java index b19a074..022c158 100644 --- a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup2DetailFragment.java +++ b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup2DetailFragment.java @@ -1,6 +1,131 @@ +//package com.example.mohassu.LoginAndSignUpFragment; +// +//import android.os.Bundle; +//import android.view.LayoutInflater; +//import android.view.View; +//import android.view.ViewGroup; +//import android.widget.Button; +//import android.widget.DatePicker; +//import android.widget.EditText; +//import android.widget.Toast; +// +//import androidx.annotation.NonNull; +//import androidx.fragment.app.Fragment; +//import androidx.navigation.NavController; +//import androidx.navigation.Navigation; +// +//import com.example.mohassu.R; +//import com.google.firebase.auth.FirebaseAuth; +//import com.google.firebase.database.DatabaseReference; +//import com.google.firebase.database.FirebaseDatabase; +// +//public class Signup2DetailFragment extends Fragment { +// +// private EditText etNickname, etName; +// private DatePicker dpBirthdate; +// private Button signupNextButton; +// +// private FirebaseAuth mAuth; +// private DatabaseReference databaseReference; +// +//// @Override +//// protected void onCreate(Bundle savedInstanceState) { +//// super.onCreate(savedInstanceState); +//// Setcon +//// +//// //androidx.fragment.app.FragmentManager +//// //androidx.fragment.app.FragmentTransaction +//// +//// } +// +// @Override +// public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { +// +// return inflater.inflate(R.layout.fragment_sign_up2, container, false); +// } +// +// @Override +// public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { +// super.onViewCreated(view, savedInstanceState); +// +//// getParentFragmentManager() +//// .beginTransaction() +//// .replace(R.id.main,new Signup3ProfileFragment()) +//// .commit(); +// +// // NavController 초기화 +// NavController navController = Navigation.findNavController(view); +// +// // 뒤로가기 버튼에 클릭 리스너 추가 +// view.findViewById(R.id.btnBack).setOnClickListener(v -> { +// navController.navigateUp(); +// }); +// +// // Firebase 초기화 +// mAuth = FirebaseAuth.getInstance(); +// databaseReference = FirebaseDatabase.getInstance().getReference("users"); +// +// // UI 초기화 +// etNickname = view.findViewById(R.id.etNickname); +// etName = view.findViewById(R.id.etName); +// dpBirthdate = view.findViewById(R.id.dpSpinner); +// signupNextButton = view.findViewById(R.id.btnNext); +// +// signupNextButton.setOnClickListener(v -> saveUserProfile(view)); +// } +// +// private void saveUserProfile(View view) { +// String nickname = etNickname.getText().toString().trim(); +// String name = etName.getText().toString().trim(); +// int day = dpBirthdate.getDayOfMonth(); +// int month = dpBirthdate.getMonth() + 1; // Month is 0-based in DatePicker +// int year = dpBirthdate.getYear(); +// String birthdate = year + "-" + month + "-" + day; +// +// // 필드 유효성 검사 +// if (nickname.isEmpty() || name.isEmpty()) { +// Toast.makeText(requireContext(), "모든 필드를 입력해주세요.", Toast.LENGTH_SHORT).show(); +// return; +// } +// +// // Firebase Realtime Database에 저장 +// String uid = mAuth.getCurrentUser().getUid(); +// UserProfile userProfile = new UserProfile(nickname, name, birthdate); +// +// databaseReference.child(uid).setValue(userProfile) +// .addOnCompleteListener(task -> { +// if (task.isSuccessful()) { +// Toast.makeText(requireContext(), "프로필 저장 성공!", Toast.LENGTH_SHORT).show(); +// +// // 다음 Fragment로 이동 +// NavController navController = Navigation.findNavController(requireView()); +// navController.navigate(R.id.actionNextToSignup3); // 적절한 Action ID로 변경 +// } else { +// Toast.makeText(requireContext(), "프로필 저장 실패: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show(); +// } +// }); +// } +// +// public static class UserProfile { +// public String nickname; +// public String name; +// public String birthdate; +// +// public UserProfile() { +// // 기본 생성자 +// } +// +// public UserProfile(String nickname, String name, String birthdate) { +// this.nickname = nickname; +// this.name = name; +// this.birthdate = birthdate; +// } +// } +//} package com.example.mohassu.LoginAndSignUpFragment; import android.os.Bundle; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,17 +141,21 @@ import com.example.mohassu.R; import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.firestore.FirebaseFirestore; + +import java.util.HashMap; +import java.util.Map; public class Signup2DetailFragment extends Fragment { + private static final String TAG = "Signup2DetailFragment"; + private EditText etNickname, etName; private DatePicker dpBirthdate; private Button signupNextButton; private FirebaseAuth mAuth; - private DatabaseReference databaseReference; + private FirebaseFirestore db; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -37,17 +166,9 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - // NavController 초기화 - NavController navController = Navigation.findNavController(view); - - // 뒤로가기 버튼에 클릭 리스너 추가 - view.findViewById(R.id.btnBack).setOnClickListener(v -> { - navController.navigateUp(); - }); - // Firebase 초기화 mAuth = FirebaseAuth.getInstance(); - databaseReference = FirebaseDatabase.getInstance().getReference("users"); + db = FirebaseFirestore.getInstance(); // UI 초기화 etNickname = view.findViewById(R.id.etNickname); @@ -55,6 +176,7 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { dpBirthdate = view.findViewById(R.id.dpSpinner); signupNextButton = view.findViewById(R.id.btnNext); + // 다음 버튼 클릭 리스너 signupNextButton.setOnClickListener(v -> saveUserProfile(view)); } @@ -72,37 +194,27 @@ private void saveUserProfile(View view) { return; } - // Firebase Realtime Database에 저장 + // 현재 사용자 UID 가져오기 String uid = mAuth.getCurrentUser().getUid(); - UserProfile userProfile = new UserProfile(nickname, name, birthdate); - - databaseReference.child(uid).setValue(userProfile) - .addOnCompleteListener(task -> { - if (task.isSuccessful()) { - Toast.makeText(requireContext(), "프로필 저장 성공!", Toast.LENGTH_SHORT).show(); - - // 다음 Fragment로 이동 - NavController navController = Navigation.findNavController(requireView()); - navController.navigate(R.id.actionNextToSignup3); // 적절한 Action ID로 변경 - } else { - Toast.makeText(requireContext(), "프로필 저장 실패: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show(); - } - }); - } - public static class UserProfile { - public String nickname; - public String name; - public String birthdate; - - public UserProfile() { - // 기본 생성자 - } - - public UserProfile(String nickname, String name, String birthdate) { - this.nickname = nickname; - this.name = name; - this.birthdate = birthdate; - } + // Firestore에 사용자 정보 저장 + Map userProfile = new HashMap<>(); + userProfile.put("nickname", nickname); + userProfile.put("name", name); + userProfile.put("birthDate", birthdate); + + db.collection("users").document(uid) + .set(userProfile) + .addOnSuccessListener(aVoid -> { + Toast.makeText(requireContext(), "프로필 저장 성공!", Toast.LENGTH_SHORT).show(); + + // 다음 프래그먼트로 이동 + NavController navController = Navigation.findNavController(view); + navController.navigate(R.id.actionNextToSignup3); // 적절한 Action ID로 변경 + }) + .addOnFailureListener(e -> { + Toast.makeText(requireContext(), "Firestore 저장 실패: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + Log.e(TAG, "Firestore Error", e); + }); } } \ No newline at end of file diff --git a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup3ProfileFragment.java b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup3ProfileFragment.java index 79c0ce4..50e3258 100644 --- a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup3ProfileFragment.java +++ b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup3ProfileFragment.java @@ -20,8 +20,15 @@ import androidx.navigation.Navigation; import com.example.mohassu.R; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.firestore.FirebaseFirestore; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; public class Signup3ProfileFragment extends Fragment { @@ -32,6 +39,9 @@ public class Signup3ProfileFragment extends Fragment { private Button signupNextButton, skipButton; private Uri selectedImageUri; // 선택된 이미지의 URI 저장 + private FirebaseStorage storage; + private FirebaseFirestore db; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_sign_up3, container, false); @@ -41,6 +51,10 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + // Firebase 초기화 + storage = FirebaseStorage.getInstance(); + db = FirebaseFirestore.getInstance(); + // NavController 초기화 NavController navController = Navigation.findNavController(view); @@ -61,13 +75,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat // 다음 버튼 클릭 리스너 signupNextButton.setOnClickListener(v -> { if (selectedImageUri != null) { - // 선택된 이미지 URI를 저장하거나 업로드 로직 추가 - Toast.makeText(requireContext(), "프로필이 저장되었습니다!", Toast.LENGTH_SHORT).show(); + // 프로필 사진 업로드 + uploadProfileImage(navController); } else { Toast.makeText(requireContext(), "프로필을 선택하지 않았습니다.", Toast.LENGTH_SHORT).show(); } - // 다음 Fragment로 이동 - navController.navigate(R.id.actionNextToSignup4); }); // 건너뛰기 버튼 클릭 리스너 @@ -100,4 +112,33 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d } } } + + // Firebase Storage에 프로필 사진 업로드 + private void uploadProfileImage(NavController navController) { + if (selectedImageUri != null) { + String userId = FirebaseAuth.getInstance().getCurrentUser().getUid(); + StorageReference storageRef = storage.getReference().child("profilePictures/" + UUID.randomUUID().toString()); + + storageRef.putFile(selectedImageUri) + .addOnSuccessListener(taskSnapshot -> storageRef.getDownloadUrl().addOnSuccessListener(uri -> { + // Firestore에 사진 URL 저장 + Map updates = new HashMap<>(); + updates.put("photoUrl", uri.toString()); + + db.collection("users").document(userId) + .update(updates) + .addOnSuccessListener(aVoid -> { + Toast.makeText(requireContext(), "프로필 사진 저장 성공!", Toast.LENGTH_SHORT).show(); + // 다음 Fragment로 이동 + navController.navigate(R.id.actionNextToSignup4); + }) + .addOnFailureListener(e -> { + Toast.makeText(requireContext(), "Firestore 저장 실패: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + }); + })) + .addOnFailureListener(e -> { + Toast.makeText(requireContext(), "사진 업로드 실패: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + }); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/mohassu/UserProfileViewModel.java b/app/src/main/java/com/example/mohassu/UserProfileViewModel.java new file mode 100644 index 0000000..5a2111f --- /dev/null +++ b/app/src/main/java/com/example/mohassu/UserProfileViewModel.java @@ -0,0 +1,47 @@ +package com.example.mohassu; + +import android.net.Uri; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class UserProfileViewModel extends ViewModel { + private final MutableLiveData nickname = new MutableLiveData<>(); + private final MutableLiveData name = new MutableLiveData<>(); + private final MutableLiveData birthDate = new MutableLiveData<>(); + private final MutableLiveData photoUri = new MutableLiveData<>(); + + public void setNickname(String nickname) { + this.nickname.setValue(nickname); + } + + public LiveData getNickname() { + return nickname; + } + + public void setName(String name) { + this.name.setValue(name); + } + + public LiveData getName() { + return name; + } + + public void setBirthDate(String birthDate) { + this.birthDate.setValue(birthDate); + } + + public LiveData getBirthDate() { + return birthDate; + } + + public void setPhotoUri(Uri uri) { + this.photoUri.setValue(uri); + } + + public LiveData getPhotoUri() { + return photoUri; + } + +} From 3daf1c3eb51fbf0f5093d3be37780a12c9cb8a29 Mon Sep 17 00:00:00 2001 From: rudeore-098 Date: Wed, 4 Dec 2024 14:11:55 +0900 Subject: [PATCH 05/37] =?UTF-8?q?FEAT/#41=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Signup1IDAndPWFragment.java | 121 +++++++++--------- app/src/main/res/layout/fragment_sign_up1.xml | 20 ++- 2 files changed, 74 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java index 4bfe8e3..f3f5045 100644 --- a/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java +++ b/app/src/main/java/com/example/mohassu/LoginAndSignUpFragment/Signup1IDAndPWFragment.java @@ -2,6 +2,7 @@ import android.os.Bundle; import android.text.TextUtils; +import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,31 +12,25 @@ import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.navigation.NavController; import androidx.navigation.Navigation; import com.example.mohassu.R; import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.database.DataSnapshot; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; public class Signup1IDAndPWFragment extends Fragment { private FirebaseAuth auth; // Firebase Auth 인스턴스 - private DatabaseReference databaseReference; // Firebase Database Reference - private EditText etId, etPassword, etConfirmPassword; - private TextView tvIdError, tvPasswordError; - private Button btnCheckId, btnSignupNext; + private EditText etEmail, etPassword, etConfirmPassword; + private TextView tvEmailError, tvPasswordError; + private Button btnCheckEmail, btnSignupNext; - private boolean isIdChecked = false; // 아이디 중복 검사 여부 + private boolean isEmailChecked = false; // 이메일 중복 검사 여부 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_sign_up1, container, false); } @@ -53,59 +48,73 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { // Firebase 초기화 auth = FirebaseAuth.getInstance(); - databaseReference = FirebaseDatabase.getInstance().getReference("users"); // Firebase Database 경로 // UI 요소 초기화 - etId = view.findViewById(R.id.etId); + etEmail = view.findViewById(R.id.etEmail); // 이메일 입력 필드 etPassword = view.findViewById(R.id.etPassword); etConfirmPassword = view.findViewById(R.id.etConfirmPassword); - tvIdError = view.findViewById(R.id.tvIdError); + tvEmailError = view.findViewById(R.id.tvEmaildError); tvPasswordError = view.findViewById(R.id.tvPasswordError); - btnCheckId = view.findViewById(R.id.btnCheckId); + btnCheckEmail = view.findViewById(R.id.btnCheckEmail); btnSignupNext = view.findViewById(R.id.btnNext); - // 아이디 중복 검사 - btnCheckId.setOnClickListener(v -> checkIdDuplication()); + // 이메일 중복 및 유효성 검사 + btnCheckEmail.setOnClickListener(v -> checkEmailDuplication()); // 회원가입 버튼 클릭 btnSignupNext.setOnClickListener(v -> registerUser()); } - private void checkIdDuplication() { - String userId = etId.getText().toString().trim(); + private void checkEmailDuplication() { + String email = etEmail.getText().toString().trim(); + String dummyPassword = "DummyPassword123"; // 테스트용 비밀번호 (중복 검사에만 사용) - if (TextUtils.isEmpty(userId)) { - tvIdError.setText("아이디를 입력해주세요."); - tvIdError.setVisibility(View.VISIBLE); + if (!isValidEmail(email)) { + tvEmailError.setText("유효한 이메일 주소를 입력해주세요."); + tvEmailError.setVisibility(View.VISIBLE); return; } - databaseReference.child(userId).get().addOnCompleteListener(task -> { - if (task.isSuccessful()) { - DataSnapshot dataSnapshot = task.getResult(); - if (dataSnapshot.exists()) { - tvIdError.setText("이미 사용 중인 아이디입니다."); - tvIdError.setVisibility(View.VISIBLE); - isIdChecked = false; - } else { - tvIdError.setVisibility(View.GONE); - Toast.makeText(requireContext(), "사용 가능한 아이디입니다.", Toast.LENGTH_SHORT).show(); - isIdChecked = true; - } - } else { - Toast.makeText(requireContext(), "아이디 중복 검사 실패: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show(); - } - }); + // Firebase Authentication에서 중복 이메일 검사 + auth.createUserWithEmailAndPassword(email, dummyPassword) + .addOnCompleteListener(task -> { + if (task.isSuccessful()) { + // 중복되지 않은 이메일 -> 테스트 계정 삭제 + auth.getCurrentUser().delete() + .addOnCompleteListener(deleteTask -> { + if (deleteTask.isSuccessful()) { + tvEmailError.setVisibility(View.GONE); + Toast.makeText(requireContext(), "사용 가능한 이메일입니다.", Toast.LENGTH_SHORT).show(); + isEmailChecked = true; + } + }); + } else { + // 중복된 이메일 + if (task.getException() != null && task.getException().getMessage().contains("email address is already in use")) { + tvEmailError.setText("이미 사용 중인 이메일입니다."); + tvEmailError.setVisibility(View.VISIBLE); + isEmailChecked = false; + } else { + Toast.makeText(requireContext(), "중복 검사 실패: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show(); + } + } + }); } private void registerUser() { - String userId = etId.getText().toString().trim(); + String email = etEmail.getText().toString().trim(); String password = etPassword.getText().toString().trim(); String confirmPassword = etConfirmPassword.getText().toString().trim(); // 유효성 검사 - if (!isIdChecked) { - Toast.makeText(requireContext(), "아이디 중복 검사를 진행해주세요.", Toast.LENGTH_SHORT).show(); + if (!isEmailChecked) { + Toast.makeText(requireContext(), "이메일 중복 검사를 진행해주세요.", Toast.LENGTH_SHORT).show(); + return; + } + + if (!isValidEmail(email)) { + tvEmailError.setText("유효한 이메일 주소를 입력해주세요."); + tvEmailError.setVisibility(View.VISIBLE); return; } @@ -124,33 +133,21 @@ private void registerUser() { tvPasswordError.setVisibility(View.GONE); // Firebase Authentication 회원가입 - auth.createUserWithEmailAndPassword(userId + "@gmail.com", password) + auth.createUserWithEmailAndPassword(email, password) .addOnCompleteListener(requireActivity(), task -> { if (task.isSuccessful()) { - // 이메일 인증 메일 전송 - auth.getCurrentUser().sendEmailVerification() - .addOnCompleteListener(emailTask -> { - if (emailTask.isSuccessful()) { - Toast.makeText(requireContext(), "회원가입 성공! 이메일 인증을 완료해주세요.", Toast.LENGTH_SHORT).show(); - - // Firebase Realtime Database에 사용자 데이터 저장 - databaseReference.child(userId).setValue(userId) - .addOnCompleteListener(dbTask -> { - if (dbTask.isSuccessful()) { - // 이메일 인증 안내 화면 또는 다음 Fragment로 이동 - NavController navController = Navigation.findNavController(requireView()); - navController.navigate(R.id.actionNextToSignup2); // 적절한 Action ID로 변경 - } else { - Toast.makeText(requireContext(), "데이터 저장 실패: " + dbTask.getException().getMessage(), Toast.LENGTH_SHORT).show(); - } - }); - } else { - Toast.makeText(requireContext(), "인증 이메일 전송 실패: " + emailTask.getException().getMessage(), Toast.LENGTH_SHORT).show(); - } - }); + Toast.makeText(requireContext(), "회원가입 성공!", Toast.LENGTH_SHORT).show(); + // 다음 Fragment로 이동 + NavController navController = Navigation.findNavController(requireView()); + navController.navigate(R.id.actionNextToSignup2); // 적절한 Action ID로 변경 } else { Toast.makeText(requireContext(), "회원가입 실패: " + task.getException().getMessage(), Toast.LENGTH_SHORT).show(); } }); } + + // 유효한 이메일 형식인지 확인 + private boolean isValidEmail(String email) { + return !TextUtils.isEmpty(email) && Patterns.EMAIL_ADDRESS.matcher(email).matches(); + } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_sign_up1.xml b/app/src/main/res/layout/fragment_sign_up1.xml index cc9dfba..b8b73d8 100644 --- a/app/src/main/res/layout/fragment_sign_up1.xml +++ b/app/src/main/res/layout/fragment_sign_up1.xml @@ -70,17 +70,17 @@ android:gravity="center_vertical">