diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 25dd527f..635c6e01 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ - @@ -16,7 +15,6 @@ - - = IMAGE_PICK_MAX) { return@setOnClickListener } - pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + requestPermission() } } @@ -387,19 +388,112 @@ class CertificateFragment : Fragment() { //알림 권한 교육용 팝업 @RequiresApi(Build.VERSION_CODES.TIRAMISU) - fun showPermissionDialog() { + fun showNotificationPermissionDialog() { MaterialAlertDialogBuilder(requireContext()) .setTitle(getString(R.string.certificate_scr_dialog_title)) .setMessage(getString(R.string.certificate_scr_dialog_message)) .setPositiveButton(getString(R.string.certificate_scr_dialog_positive_button)) { dialogInterface: DialogInterface, i: Int -> - permissionResult.launch( - Manifest.permission.POST_NOTIFICATIONS - ) + navigateToAppSetting() } .setNegativeButton(getString(R.string.certificate_scr_dialog_negative_button)) { dialogInterface: DialogInterface, i: Int -> viewModel.insertCertificateInitInfo() }.show() } + private val multiplePermissionsLauncher = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + permissions.entries.forEach { (permission, isGranted) -> + when(permission){ + Manifest.permission.READ_EXTERNAL_STORAGE ->{ + if (isGranted){ + pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + }else{ + if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)){ + showPermissionSettiongDialog() + }else{ + showStoragePermissionDialog() + } + } + } + Manifest.permission.READ_MEDIA_IMAGES -> { + if (isGranted){ + pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + }else{ + if (!shouldShowRequestPermissionRationale(Manifest.permission.READ_MEDIA_IMAGES)){ + showPermissionSettiongDialog() + }else{ + showPermissionSettiongDialog() + } + } + } + } + } + } + + fun showStoragePermissionDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.permission_dialog_scr_guide)) + .setMessage(getString(R.string.permission_dialog_scr_guide_message)) + .setPositiveButton(getString(R.string.permission_dialog_scr_guide_select)) { dialogInterface: DialogInterface, i: Int -> + //권한 물어보기 + requestPermission() + } + .setNegativeButton(getString(R.string.permission_dialog_scr_guide_cancel)) { dialogInterface: DialogInterface, i: Int -> + dialogInterface.cancel() + }.show() + } + + + //권한 설정 화면을 위한 다이얼로그 띄우는 메서드 + fun showPermissionSettiongDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(getString(R.string.permission_dialog_scr_guide_setting)) + .setPositiveButton(getString(R.string.permission_dialog_scr_guide_setting_select)) { dialogInterface: DialogInterface, i: Int -> + navigateToAppSetting() + } + .setNegativeButton(getString(R.string.permission_dialog_scr_guide_setting_cancel)) { dialogInterface: DialogInterface, i: Int -> + dialogInterface.cancel() + }.show() + } + + //앱 권한 세팅 화면으로 이동키시는 메서드 + fun navigateToAppSetting() { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", requireContext().packageName, null) + } + startActivity(intent) + } + //권한 확인 및 요청 메서드 + fun requestPermission(){ + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + (ContextCompat.checkSelfPermission(requireContext(), + Manifest.permission.READ_MEDIA_IMAGES + ) == PermissionChecker.PERMISSION_GRANTED) + ) { + pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } else if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE + && ContextCompat.checkSelfPermission(requireContext(), + Manifest.permission.READ_MEDIA_IMAGES + ) == PermissionChecker.PERMISSION_DENIED + ) { + // 34이상이고 READ_MEDIA_VISUAL_USER_SELECTED만 허용되어있다면 권한 물어보는 다이얼로그를 띄워야함. + /*showPermissionDialog()*/ + multiplePermissionsLauncher.launch(arrayOf(Manifest.permission.READ_MEDIA_IMAGES)) + } else if (ContextCompat.checkSelfPermission(requireContext(), + Manifest.permission.READ_EXTERNAL_STORAGE + ) == PermissionChecker.PERMISSION_GRANTED + ) { + pickMultipleMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly)) + } else { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2){ + //READ_EXTERNAL_STORAGE 권한 요청 + multiplePermissionsLauncher.launch(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)) + }else{ + multiplePermissionsLauncher.launch(arrayOf(Manifest.permission.READ_MEDIA_IMAGES)) + } + } + } fun loadingTaskSettingStart() { binding.run { diff --git a/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingPermissionFragment.kt b/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingPermissionFragment.kt index 8a647899..34228414 100644 --- a/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingPermissionFragment.kt +++ b/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingPermissionFragment.kt @@ -1,43 +1,61 @@ package com.fitmate.fitmate.presentation.ui.onboarding +import android.Manifest import android.content.Context +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import com.fitmate.fitmate.R import com.fitmate.fitmate.databinding.FragmentOnboardingPermissionBinding +import com.fitmate.fitmate.presentation.viewmodel.OnBoardingViewModel +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class OnBoardingPermissionFragment: Fragment() { + val permissions = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){ + arrayOf(Manifest.permission.POST_NOTIFICATIONS,Manifest.permission.READ_MEDIA_IMAGES) + }else{ + arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) + } + private lateinit var binding: FragmentOnboardingPermissionBinding + private val viewModel: OnBoardingViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = FragmentOnboardingPermissionBinding.inflate(layoutInflater) + binding.view = this return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - binding.buttonFragmentOnboardingPermissionToLogin.setOnClickListener { - //권한 물어보고 로그인 화면으로 이동 - //TODO 권한 리스트들 물어보기 - //TODO 온보딩 완료 값 로컬에 저장 - /*findNavController().navigate(R.id.action_onBoardingPermissionFragment_to_loginFragment)*/ + viewModel.onboardingInquiryStatus.observe(viewLifecycleOwner){ + if (it){ + findNavController().navigate(R.id.action_onBoardingPermissionFragment_to_homeFragment) + } } } + fun setButtonClick() { + multiplePermissionsLauncher.launch(permissions) + } - private fun onBoardingFinished() { - val sharedPref = activity?.getSharedPreferences("onBoarding", Context.MODE_PRIVATE) - val editor = sharedPref?.edit() - editor?.putBoolean("Finished", true) - editor?.apply() + val multiplePermissionsLauncher = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + permissions.entries.forEach { (permission, isGranted) -> } + viewModel.saveOnBoardingStateInPref() } + } diff --git a/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingThirdFragment.kt b/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingThirdFragment.kt index ec10606f..9cebcb35 100644 --- a/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingThirdFragment.kt +++ b/app/src/main/java/com/fitmate/fitmate/presentation/ui/onboarding/OnBoardingThirdFragment.kt @@ -12,10 +12,9 @@ import com.fitmate.fitmate.databinding.FragmentOnboardingThirdBinding import com.fitmate.fitmate.presentation.viewmodel.OnBoardingViewModel import dagger.hilt.android.AndroidEntryPoint -@AndroidEntryPoint -class OnBoardingThirdFragment: Fragment(R.layout.fragment_onboarding_third) { + +class OnBoardingThirdFragment: Fragment() { private lateinit var binding: FragmentOnboardingThirdBinding - private val viewModel: OnBoardingViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -27,22 +26,9 @@ class OnBoardingThirdFragment: Fragment(R.layout.fragment_onboarding_third) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - observeSavePref() - initViews() - } - - private fun observeSavePref() { - viewModel.onboardingInquiryStatus.observe(viewLifecycleOwner) { - if (it) { - findNavController().navigate(R.id.action_onboardingContainerFragment_to_homeFragment) - } - } - } - - private fun initViews() { binding.buttonFinishFragment.setOnClickListener { - viewModel.saveOnBoardingStateInPref() + findNavController().navigate(R.id.action_onboardingContainerFragment_to_onBoardingPermissionFragment) } } + } \ No newline at end of file diff --git a/app/src/main/java/com/fitmate/fitmate/presentation/ui/userinfo/UserInfoFragment.kt b/app/src/main/java/com/fitmate/fitmate/presentation/ui/userinfo/UserInfoFragment.kt index 7a244a9d..65ec781b 100644 --- a/app/src/main/java/com/fitmate/fitmate/presentation/ui/userinfo/UserInfoFragment.kt +++ b/app/src/main/java/com/fitmate/fitmate/presentation/ui/userinfo/UserInfoFragment.kt @@ -1,16 +1,24 @@ package com.fitmate.fitmate.presentation.ui.userinfo +import android.Manifest.permission.READ_EXTERNAL_STORAGE +import android.Manifest.permission.READ_MEDIA_IMAGES import android.app.AlertDialog +import android.content.DialogInterface import android.content.Intent import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.MediaStore +import android.provider.Settings import android.util.Log import android.view.View import android.widget.ImageView import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.core.content.PermissionChecker.PERMISSION_DENIED +import androidx.core.content.PermissionChecker.PERMISSION_GRANTED import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import com.bumptech.glide.Glide @@ -18,24 +26,56 @@ import com.bumptech.glide.request.RequestOptions import com.fitmate.fitmate.R import com.fitmate.fitmate.databinding.FragmentUserInfoBinding import com.fitmate.fitmate.util.ControlActivityInterface +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.firebase.ktx.Firebase import com.google.firebase.storage.ktx.storage -class UserInfoFragment: Fragment(R.layout.fragment_user_info) { +class UserInfoFragment : Fragment(R.layout.fragment_user_info) { private lateinit var binding: FragmentUserInfoBinding private lateinit var uri: Uri - private val registerForActivityResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - when (result.resultCode) { - AppCompatActivity.RESULT_OK -> { - uri = result.data?.data!! - Log.d("testt",uri.toString()) - showImagePreview() + private val multiplePermissionsLauncher = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions()) { permissions -> + permissions.entries.forEach { (permission, isGranted) -> + when(permission){ + READ_EXTERNAL_STORAGE ->{ + if (isGranted){ + accessGallery() + }else{ + if (!shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)){ + showPermissionSettiongDialog() + }else{ + showPermissionDialog() + } + } + } + READ_MEDIA_IMAGES -> { + if (isGranted){ + accessGallery() + }else{ + if (!shouldShowRequestPermissionRationale(READ_MEDIA_IMAGES)){ + showPermissionSettiongDialog() + }else{ + showPermissionSettiongDialog() + } + } + } } } } + private val registerForActivityResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + when (result.resultCode) { + AppCompatActivity.RESULT_OK -> { + uri = result.data?.data!! + Log.d("testt", uri.toString()) + showImagePreview() + } + } + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentUserInfoBinding.bind(view) @@ -47,9 +87,12 @@ class UserInfoFragment: Fragment(R.layout.fragment_user_info) { private fun setClickListener() { listOf( - binding.textViewUserInfoContent1, binding.textViewUserInfoContent2, - binding.textViewUserInfoContent3, binding.textViewUserInfoContent4, - binding.textViewUserInfoContent5, binding.textViewUserInfoContent6 + binding.textViewUserInfoContent1, + binding.textViewUserInfoContent2, + binding.textViewUserInfoContent3, + binding.textViewUserInfoContent4, + binding.textViewUserInfoContent5, + binding.textViewUserInfoContent6 ).forEach { textView -> textView.setOnClickListener { handleOnClick(textView.id) } } @@ -57,7 +100,7 @@ class UserInfoFragment: Fragment(R.layout.fragment_user_info) { private fun handleOnClick(viewId: Int) { when (viewId) { - R.id.textViewUserInfoContent1 -> accessGallery() + R.id.textViewUserInfoContent1 -> requestPermission()//TODO 권한 체크로 변경하고 권한 체크하는 곳에서 이동하도록 변경 R.id.textViewUserInfoContent2 -> navigateFitOff() R.id.textViewUserInfoContent3 -> announcement() R.id.textViewUserInfoContent4 -> navigateLicense() @@ -75,20 +118,15 @@ class UserInfoFragment: Fragment(R.layout.fragment_user_info) { val preview = imagePreviewView.findViewById(R.id.imageViewPreview) Glide.with(this).load(uri).into(preview) - AlertDialog.Builder(requireContext()) - .setView(imagePreviewView) + AlertDialog.Builder(requireContext()).setView(imagePreviewView) .setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() - Glide.with(this) - .load(uri) - .apply(RequestOptions().circleCrop()) + Glide.with(this).load(uri).apply(RequestOptions().circleCrop()) .into(binding.imageViewUserInfoIcon) imageUpload("test_user_id") - } - .setNegativeButton(android.R.string.cancel) { dialog, _ -> + }.setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.dismiss() - } - .show() + }.show() } private fun imageUpload(userId: String) { @@ -102,10 +140,11 @@ class UserInfoFragment: Fragment(R.layout.fragment_user_info) { userRef.putFile(uri).addOnSuccessListener { userRef.downloadUrl.addOnSuccessListener { downloadUri -> val downloadURL = downloadUri.toString() - activity.getSharedPreferences("UserInfo", AppCompatActivity.MODE_PRIVATE).edit().apply { - putString("profileImageUri", uri.toString()) - apply() - } + activity.getSharedPreferences("UserInfo", AppCompatActivity.MODE_PRIVATE).edit() + .apply { + putString("profileImageUri", uri.toString()) + apply() + } Toast.makeText(activity, "성공적으로 프로필 사진을 변경하였습니다.", Toast.LENGTH_SHORT).show() } @@ -115,18 +154,21 @@ class UserInfoFragment: Fragment(R.layout.fragment_user_info) { } private fun loadProfileImage() { - val sharedPreferences = requireActivity().getSharedPreferences("UserInfo", AppCompatActivity.MODE_PRIVATE) + val sharedPreferences = + requireActivity().getSharedPreferences("UserInfo", AppCompatActivity.MODE_PRIVATE) val profileImageUri = sharedPreferences.getString("profileImageUri", null) profileImageUri?.let { - Glide.with(this) - .load(Uri.parse(it)) - .apply(RequestOptions().circleCrop()) + Glide.with(this).load(Uri.parse(it)).apply(RequestOptions().circleCrop()) .into(binding.imageViewUserInfoIcon) } } private fun accessGallery() { - registerForActivityResult.launch(Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)) + registerForActivityResult.launch( + Intent( + Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI + ) + ) } private fun announcement() { @@ -148,4 +190,63 @@ class UserInfoFragment: Fragment(R.layout.fragment_user_info) { private fun navigateFitOff() { navigateTo(R.id.action_userInfoFragment_to_myFitOffFragment) } + + //교육용 팝업 띄우는 메서드 + fun showPermissionDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.permission_dialog_scr_guide)) + .setMessage(getString(R.string.permission_dialog_scr_guide_message)) + .setPositiveButton(getString(R.string.permission_dialog_scr_guide_select)) { dialogInterface: DialogInterface, i: Int -> + //권한 물어보기 + requestPermission() + } + .setNegativeButton(getString(R.string.permission_dialog_scr_guide_cancel)) { dialogInterface: DialogInterface, i: Int -> + dialogInterface.cancel() + }.show() + } + + //권한 설정 화면을 위한 다이얼로그 띄우는 메서드 + fun showPermissionSettiongDialog() { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(getString(R.string.permission_dialog_scr_guide_setting)) + .setPositiveButton(getString(R.string.permission_dialog_scr_guide_setting_select)) { dialogInterface: DialogInterface, i: Int -> + navigateToAppSetting() + } + .setNegativeButton(getString(R.string.permission_dialog_scr_guide_setting_cancel)) { dialogInterface: DialogInterface, i: Int -> + dialogInterface.cancel() + }.show() + } + + //앱 권한 세팅 화면으로 이동키시는 메서드 + fun navigateToAppSetting() { + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { + data = Uri.fromParts("package", requireContext().packageName, null) + } + startActivity(intent) + } + //권한 확인 및 요청 메서드 + fun requestPermission(){ + if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + (ContextCompat.checkSelfPermission(requireContext(), READ_MEDIA_IMAGES) == PERMISSION_GRANTED) + ) { + accessGallery() + } else if ( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE + && ContextCompat.checkSelfPermission(requireContext(), READ_MEDIA_IMAGES) == PERMISSION_DENIED + ) { + // 34이상이고 READ_MEDIA_VISUAL_USER_SELECTED만 허용되어있다면 권한 물어보는 다이얼로그를 띄워야함. + /*showPermissionDialog()*/ + multiplePermissionsLauncher.launch(arrayOf(READ_MEDIA_IMAGES)) + } else if (ContextCompat.checkSelfPermission(requireContext(), READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) { + accessGallery() + } else { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2){ + //READ_EXTERNAL_STORAGE 권한 요청 + multiplePermissionsLauncher.launch(arrayOf(READ_EXTERNAL_STORAGE)) + }else{ + multiplePermissionsLauncher.launch(arrayOf(READ_MEDIA_IMAGES)) + } + } + } } diff --git a/app/src/main/res/layout/fragment_onboarding_permission.xml b/app/src/main/res/layout/fragment_onboarding_permission.xml index ee5bf9e5..7f402b8d 100644 --- a/app/src/main/res/layout/fragment_onboarding_permission.xml +++ b/app/src/main/res/layout/fragment_onboarding_permission.xml @@ -3,7 +3,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> - + diff --git a/app/src/main/res/navigation/nav_main_graph.xml b/app/src/main/res/navigation/nav_main_graph.xml index 1fc14422..8e7a5fc9 100644 --- a/app/src/main/res/navigation/nav_main_graph.xml +++ b/app/src/main/res/navigation/nav_main_graph.xml @@ -31,10 +31,8 @@ android:name="com.fitmate.fitmate.presentation.ui.onboarding.OnboardingContainerFragment" android:label="OnboardingContainerFragment" > + android:id="@+id/action_onboardingContainerFragment_to_onBoardingPermissionFragment" + app:destination="@id/onBoardingPermissionFragment" /> + android:label="OnBoardingPermissionFragment" > + + 사진은 최대 5장까지 첨부할 수 있습니다! 종료 사진을 하나 이상 첨부해주세요! 인증 완료하기 - 정말 거부하시겠습니까? + 알림 권한이 없습니다! 알림 권한을 허용하지 않으면 인증 과정에서 운동 시간과 알림을 확인할 수 없습니다! 권한 허용하러가기 알림 없이 진행하기 @@ -161,5 +161,14 @@ 그룹 투표 현황 + 저장소 권한을 항상 모두 허용해야만 갤러리에 접근할 수 있습니다! + 권한 설정하러 가기 + 취소 + + 정말 거부하시겠습니까? + 저장소 권한을 허용하지 않으면 사진 첨부를 할 수 없습니다! + 권한 허용하러가기 + 취소 + \ No newline at end of file