[TIL] Android 앱 개발 숙련 : 팀 프로젝트 (나만의 맛집 연락처) 4

지혜·2024년 1월 18일

Android_TIL

목록 보기
41/70

✏240118 목요일 TIL(Today I learned) 오늘 배운 것

📖연락처 추가 기능 정리 후 최종 머지

[유효성검사 기능 변경]

  • 팀원들과 상의했을 때, 입력 단계에서 아예 전화번호에 -를 추가하는게 좋을 것 같다고 하셔서, 원래 숫자만 입력가능하게 했던 정규식을 Regex("^([0-9]{2,4})-\\d{3,4}-\\d{4}\$") 으로 고쳤다. 숫자2~4자리수 - 숫자3~4자리수-숫자4자리수 의 형식을 유지하도록했다. 핸드폰 번호 부터 지역번호, 배달의민족 번호, 인터넷전화 번호 등등 여러 가게들을 직접 검색해서 실제 값들이 잘 들어가는지 최종 확인하였다.

[문자열 하드코딩 strings.xml에 넣어두기]

  • 문자열을 strings.xml에서 관리하는건 기본사항이자 기본사항인가보다. 상의하고 말것도 없이 당연히 집어 넣는 분위기라서 물어본게 살짝 뻘쭘했다.ㅎㅎ 기본적인건 아무리 귀찮아도 무조건 지켜야하는데.. 여튼 그래서 xml을 비롯한 유효성 검사에서 사용하던 하드코딩문자열들을 모두 strings.xml에 넣어두었다.

    [AddContactDialogFragment.kt]

    ...
     private fun setMessageValidPhoto(): String = if (profileImage == null) {
           getString(R.string.valid_photo)
       } else getString(R.string.valid_pass)
    
       private fun setMessageValidName(): String = if (binding.addContactEtStoreName.text.toString().isBlank()) {
           getString(R.string.valid_name)
       } else getString(R.string.valid_pass)
    
       private fun setMessageValidPhone(): String {
           val phone = binding.addContactEtStorePhone.text.toString()
           return if (phone.isBlank()) {
               getString(R.string.valid_phone)
           } else if (!validatePhoneNumber(phone)) {
               getString(R.string.valid_phoneFormat)
           } else getString(R.string.valid_pass)
       }
    
       private fun setMessageValidAddress(): String = if (binding.addContactEtStoreAddress.text.toString().isBlank()) {
           getString(R.string.valid_address)
       } else getString(R.string.valid_pass)
    
       private fun setMessageValidInsta(): String {
           val instaID = binding.addContactEtStoreInsta.text.toString()
           return if (instaID.isBlank()) {
               getString(R.string.valid_insta)
           } else if (!validateInstaID(instaID)) {
               getString(R.string.valid_instaFormat)
           } else getString(R.string.valid_pass)
       }
       ...
  • 그래서 getString()을 사용하여 문자열을 가져오도록 수정을 마쳤다.

[충돌확인 후 push & merge]

  • 깃허브 FEAT/addContact 브런치 (내 브런치ㅎ)에 push를 한 후, dev 브런치를 pull로 땡겨와서 충돌 검사를 했다. 나는 다른 팀원들보다 살짝 먼저 merge할 수 있었어서 충돌난게 별로 없었다. 겹치는 부분을 가볍게 확인해서 수정하고, 다시 내 브런치에 push해서 PullRequest를 올려서 상세 설명을 작성한 후 머지를 하였다.

📖선택 기능 : Event Notification 만들기

  • 제일 먼저 머지를 해서, 다른 팀원들의 머지를 기다리는 동안 기능 부분을 좀 더 보충했으면 좋겠다는 의견이 있어서 선택구현기능 중에 Event 시간에 맞춰 Notification 표시 기능을 만들어보기로 했다.
    • 등록된 이벤트(없음/5분/15분/30분 뒤 알림)의 시간에 맞춰 Notification을 띄웁니다.
    • 필수 구성 요소: Notification, Intent, PendingIntent
    • 알림 콘텐츠에는 알림을 클릭했을 때 연락처 앱으로 이동해 보세요.
    • 예시 화면은 5분으로 설정하였으나 편의상 5초로 동작하도록 구현되어 있습니다.
  • 과제 가이드 라인은 연락처 추가 다이얼로그에서 chip버튼을 선택하여 알림이 등장하도록 되어있었기 때문에, 나도 일단 다이얼로그프래그먼트 에서 작동되도록 작성을 시작했다. 아무래도 내가 만든 부분이기도 해서 건드리는데에 더 죄책감도 없었다.

[AddContactDialogFragment.kt]

...
fun eventNotification() {
        val manager = activity?.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        val builder: NotificationCompat.Builder

        val channelId = "one-channel"
        val channelName = "My Channel One"
        val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT).apply {
            description = "My Channel One Description"
            setShowBadge(true)
            val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
            val audioAttributes = AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .setUsage(AudioAttributes.USAGE_ALARM)
                .build()
            setSound(uri, audioAttributes)
            enableVibration(true)
        }

        manager.createNotificationChannel(channel)
        builder = NotificationCompat.Builder(requireContext(), channelId)

        val intent = Intent(requireContext(), MainActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val pendingIntent = PendingIntent.getActivity(requireContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE)

        builder.run {
            setSmallIcon(R.mipmap.ic_launcher)
            setWhen(System.currentTimeMillis())
            setContentTitle("연락처 알림")
            setContentText("설정한 알림이 도착했습니다!!")
            setContentIntent(pendingIntent)
            setAutoCancel(true)
        }
        manager.notify(11, builder.build())
    }
 ...
  • 사실은.. 이번에도.. 개인과제 애플마켓에서.. 했던 코드를 많이 재활용했다. 근데 어짜피 배운게 이것 뿐이라 배운 코드가 이 코드이긴 하다.ㅎㅎ 여튼, 대신에 이번에는 푸시 알람을 클릭했을 때, 다시 어플로 이동하게 만들어야 해서 intent와 pendingIntent를 추가로 붙여주었다.

    • intent 변수를 선언하여 메인액티비티로 이동하도록 코드를 작성했다.
    • intent.flags에 notification에서 액티비티를 시작하기 위해 FLAG_ACTIVITY_NEW_TASK를 작성한다. FLAG_ACTIVITY_CLEAR_TASK는 실행 액티비티 외의 것들을 모두 제거한다.
    • pendingIntent 를 통해 다른 앱이 프로세스를 점유하고 있을 때 Intent를 수행하게 해준다. 이때, FLAG_CANCEL_CURRENT는 이전에 생성된 PendingIntent를 취소하고 새로만들고, FLAG_IMMUTABLE로 생성된 펜딩 인텐트가 변경되지 않도록 한다. (특별한 경우가 아니면 보통 FLAG_IMMUTABLE를 사용해야한다.)
    • 마지막으로, builder.run 안에 setContentIntent(pendingIntent)으로 pendingIntent를 사용한다.
    • setAutoCancel(true)를 주어서 알람을 클릭하면 푸시메시지가 자동으로 사라지도록 하면 완성이다!

  • 이렇게 만든 알람을, chip을 통해서 몇 분 알람을 줄 것인지 조정을 하려고 했는데, 무슨 일인지 밑의 코드에서 chekedId가 혼자서 따로 놀았다. 사실 밑의 코드를 사용하는 것이 맞는지도 긴가민가했는데, 여튼 checkedId는 말그대로 Id를 참조하는 것이라 뷰바인딩 값이랑 비교가 안되서 작동이 일체 안되는 것이였다. (로그를 일일이 찍어보며 알아냈다..)

val handler = Handler(Looper.getMainLooper())
                addContactChipGroup.setOnCheckedStateChangeListener { group, checkedId ->
                    when (checkedId) {
                        addContactChip5m -> {
                            handler.postDelayed({ eventNotification() }, 1000)
                        }

                        addContactChip30m -> {
                            handler.postDelayed({ eventNotification() }, 3000)
                        }

                        addContactChip1h -> {
                            handler.postDelayed({ eventNotification() }, 5000)
                        }
                    }

                }
  • 그래서 ID값을 참조하도록 R.id.addContact_chip_5m도 써보고 addContactChip5m.id도 써보고 따로 변수에 값을 담는 방법도 써봤는데 전부 다 빨간 밑 줄이 뜨면서 Inspection 'Constant conditions' has no quick-fixes for this problem. Click to edit inspection options, WILL suppress the warning, or disable the inspection completely. 이런 문구가 떴다. 이건 뭐 고쳐주는 것도 아니고.. 그냥 경고문이라서 어떻게 할 수 가 없어서 결국 이 방법을 포기했다.
  • 혼자서는 도저히 칩으로 조정 하기가 버거워서 팀원들과 상담했는데, 어짜피 알람 기능인데 연락처 추가할 때 알람을 설정하는 건 사용자 경험상 좀 다르지 않냐는 의견과 함께 상세페이지에서 그냥 버튼으로 알람 설정할 수 있게 바꾸자고 건의 드렸더니 긍정적인 답변이 돌아왔다. 버튼에서는 정상적으로 활성화 되는 것을 확인 했기 때문에 나머지 기본 필수 구현 기능 들에 대해 기본적인 머지가 끝나면 코드를 합치자고 해주셨다. 따로 노는 별개의 코드이기 때문에 쉽게 통합 가능할 것 같다.

📖오늘의 반성과 내일 할 일

  • 결국 chip을 생각한대로 사용하지 못한 점이 조금 아쉽다. 만약 기간적으로 여유가 더 있었더라면 다른 방법으로 검색을 더 해봤을텐데.. 그래도 시간을 맞추는 것이 더 중요한 가치이니까 안되면 되는 방법으로 최대한 해봤다.
  • 오늘은 생각보다 팀원들의 코드를 합치는데 시간이 꽤 걸렸다. 그리고 디자인이 아니고 기능적으로 세부사항을 조정하는 것도 은근히 있었다. 다들 관성적으로 뒤로가기 버튼 아이콘을 넣으셨는데 실제로 기능까지 넣지 않아서 살릴지 죽일지 조정한다던지, 다들 자기 코드에 먼저 pull하셔서 충돌 해결을 하실 때 다른 팀원들의 코드를 이해해가면서 버그도 찾는다던지.. 그리고 의외로 추가적인 선택 기능 구현에 대해서도 다들 진행도가 있으셔서 그거에 대한 마무리도 시간이 좀 있었다.
  • 그래서 오늘은 코드를 최종적으로 합쳐가는데에 진행도가 끝났다. 디자인은 기본 디자인 방향을 잡아주시긴 했지만 디테일까지는 손을 못댔어서 내일 오전 중으로 디자인 마무리를 하고 오후에 발표준비를 하기로 했다. 드디어 내일이 금요일인데 과연 모든 팀원들의 바람대로 주말을 여유있게 보낼 수 있을지!! 내일도 파이팅해야겠다.ㅎㅎ
profile
파이팅!

0개의 댓글