프로그래밍/안드로이드(코틀린)

안드로이드 3 - 카카오톡 알림 만들기 예제

가카리 2025. 9. 16. 21:54
반응형

 

 

1. 앱 실행화면

 

  - 알림 발생 버튼을 누르면

 

  - 알림이 발생하고

  - 답장을 누르면 아래와 같이 글씨가 써지고 전송 버튼이 활성화됨

 

 

 

2. 프로젝트 구성

  - MainActivity

  - ReplyReceiver(브로드캐스트 리시버)

  - drawable  폴더(사진 및 이미지들)

  - build.gradle.kts 파일 수

 

3. build.gradle.kts

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
}

android {
    namespace = "com.example.ch10_notification"
    compileSdk = 35

    viewBinding.isEnabled = true  //여기 추가됨

    defaultConfig {
        applicationId = "com.example.ch10_notification"
        minSdk = 35
        targetSdk = 35
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }

}

dependencies {

    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.appcompat)
    implementation(libs.material)
    implementation(libs.androidx.activity)
    implementation(libs.androidx.constraintlayout)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso.core)
}

 

4. activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:id="@+id/main">

    <Button
        android:id="@+id/notificationButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="알림 발생"/>

</LinearLayout>

 

 

5. MainActivity.kt

package com.example.ch10_notification
// 패키지 이름 정의. 앱 고유의 네임스페이스 역할을 함.

import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
import androidx.core.content.ContextCompat
import com.example.ch10_notification.databinding.ActivityMainBinding
// 필요한 클래스들을 import. (알림, 권한 체크, 바인딩 등)

class MainActivity : AppCompatActivity() {
    // MainActivity: 앱이 실행되면 가장 먼저 보여지는 화면(Activity)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 액티비티 생성 시 최초 실행되는 메서드

        val binding = ActivityMainBinding.inflate(layoutInflater)
        // activity_main.xml을 기반으로 자동 생성된 바인딩 클래스 인스턴스 생성
        // layoutInflater가 XML을 실제 View 객체로 변환해줌

        setContentView(binding.root)
        // 변환된 뷰를 현재 액티비티 화면에 표시

        // 권한 요청 런처: POST_NOTIFICATIONS 권한을 동적으로 요청할 때 사용
        val permissionLauncher = registerForActivityResult(
            ActivityResultContracts.RequestMultiplePermissions()
        ) {
            // 요청 결과 콜백 (it은 Map<String, Boolean>)
            if (it.all { permission -> permission.value == true }) {
                // 모든 권한이 승인되면 알림 실행
                noti()
            } else {
                // 거부되면 Toast로 메시지 표시
                Toast.makeText(this, "permission denied...", Toast.LENGTH_SHORT).show()
            }
        }

        // 버튼 클릭 이벤트
        binding.notificationButton.setOnClickListener {
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                // Android 13 (API 33) 이상에서는 POST_NOTIFICATIONS 권한이 필요
                if (ContextCompat.checkSelfPermission(
                        this,
                        "android.permission.POST_NOTIFICATIONS"
                    ) == PackageManager.PERMISSION_GRANTED
                ) {
                    // 권한 이미 허용됨 → 알림 실행
                    noti()
                } else {
                    // 권한 요청 실행
                    permissionLauncher.launch(
                        arrayOf("android.permission.POST_NOTIFICATIONS")
                    )
                }
            } else {
                // Android 12 이하 → 권한 필요 없음
                noti()
            }
        }
    }

    fun noti(){
        // 실제 알림(Notification)을 만드는 함수

        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        // NotificationManager: 알림을 관리하는 시스템 서비스

        val builder: NotificationCompat.Builder
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            // Android 8.0 (API 26) 이상 → NotificationChannel 필요
            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)
            // 채널을 NotificationManager에 등록

            builder = NotificationCompat.Builder(this, channelId)
            // 채널 ID를 연결해서 빌더 생성
        } else {
            // Android 7.1 이하 → 채널 필요 없음
            builder = NotificationCompat.Builder(this)
        }

        // 알림의 기본 정보 설정
        builder.run {
            setSmallIcon(R.drawable.small) // 작은 아이콘
            setWhen(System.currentTimeMillis()) // 알림 발생 시간
            setContentTitle("홍길동") // 제목
            setContentText("안녕하세요.") // 내용
            setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.big)) // 큰 아이콘
        }

        // ---- 인라인 답장(RemoteInput) 설정 ----
        val KEY_TEXT_REPLY = "key_text_reply" // 입력값 키
        var replyLabel = "답장" // 답장 라벨
        var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
            setLabel(replyLabel) // 입력창에 표시될 힌트
            build()
        }

        // 답장 동작을 받을 BroadcastReceiver 지정
        val replyIntent = Intent(this, ReplyReceiver::class.java)

        // PendingIntent: 나중에 실행할 Intent를 감싸는 객체
        val replyPendingIntent = PendingIntent.getBroadcast(
            this, 30, replyIntent, PendingIntent.FLAG_MUTABLE
        )

        // 알림에 "답장" 액션 추가
        builder.addAction(
            NotificationCompat.Action.Builder(
                R.drawable.send, // 액션 아이콘
                "답장", // 버튼 텍스트
                replyPendingIntent // 실행할 인텐트
            ).addRemoteInput(remoteInput).build() // RemoteInput 연결
        )

        // 알림 표시 (id = 11)
        manager.notify(11, builder.build())
    }
}

 

 

 

 

6. ReplyReceiver.kt

 

package com.example.ch10_notification

import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.RemoteInput

class ReplyReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // 알림에서 사용자가 입력한 글자 획득
        val replyTxt = RemoteInput.getResultsFromIntent(intent)
            ?.getCharSequence("key_text_reply")

        // 입력된 답장을 로그로 출력
        Log.d("kkang", "replyTxt : $replyTxt")

        // 알림 취소 (id = 11인 알림 제거)
        val manager = context.getSystemService(
            AppCompatActivity.NOTIFICATION_SERVICE
        ) as NotificationManager
        manager.cancel(11)
    }
}

 

 

 

출처 : https://kkangsnote.tistory.com/