[kotlin] 다이얼로그와 알림 이용하기

엔초이·2021년 11월 3일
0

코틀린 개념

목록 보기
2/8

『Do it! 깡샘의 안드로이드 앱 프로그래밍 with 코틀린』 교재를 바탕으로 정리한 내용입니다.

1. API 레벨 호환 고려하기

API 레벨 설정

  • build.gradle 파일에서 확인 및 설정 가능
minSdkVersion 16
targetSdkVersion 31
  • 위와 같이 설정했을 경우, 해당 앱은 16 버전부터 설치할 수 있으며 16 버전 이후에 추가된 함수를 사용하면 오류가 발생할 수 있음 ❗ → 예를 들어, 30 버전에서 추가된 Callback() 함수를 사용하려고 하면 아래 처럼 경고 메시지 발생함

💡 API 레벨 호환 문제 해결 방법

  • '@' 기호로 시작하는 어노테이션을 추가해 안드로이드스튜디오에서 오류를 무시하도록 설정 → @RequiresApi 또는 @TargetApi 사용
// @TargetApi(Build.VERSION_CODES.R)
@RequiresApi(Build.VERSION_CODES.R)
fun showToast() {
	(...생략...)
	toast.Callback(
		(...생략...)
	)
	toat.show()
}
  • 앱 실행 시 API 레벨 호환성 문제를 막기 위해서는 해당 API를 사용할 수 있는 버전인 경우에만 실행되도록 직접 코드로 처리 → Build.VERSION.SDK_INT : 앱이 실행되는 기기의 API 레벨
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
	fun showToast() {
		(...생략...)
		toast.Callback(
			(...생략...)
		)
		toat.show()
	}
}

2. 퍼미션 설정하기

퍼미션이란?

앱의 특정 기능에 부여하는 접근 권한으로, 다른 앱이나 안드로이드 시스템에서 보호하는 특정 기능을 이용하거나 다른앱에서 사용할 수 없도록 기능을 보호하고 싶을 때 퍼미션을 설정한다.

퍼미션 설정과 사용 설정

: 기능을 보호하려는 앱의 매니페스트 파일에 설정

: 퍼미션으로 보호된 기능을 사용하려는 앱의 매니페스트 파일에 설정

👉 A앱의 컴포넌트를 B앱에서 사용하려고 하는 경우

B앱의 입장에서 생각해보면,

  • 퍼미션 적용 안한 상태 → A앱의 컴포넌트 사용 가능

  • A앱 컴포넌트에 설정한 상태 → A앱의 컴포넌트 사용 불가능

  • B앱에서 해당 퍼미션에 대해 태그 설정 → A앱 컴포넌트 사용 가능

  • 태그 속성

    <permission android:name="com.example.permission.TEST_PERMISSION"
    	android:label="Test Permission"
    	android:description="@string/permission_desc"
    	android:protectionLevel="dangerous" />
  • name : 퍼미션 이름 (퍼미션 식별자 역할)

  • label, description : 퍼미션을 이용하는 외부 앱에서 권한 인증 화면에 출력되는 정보

  • protectionLevel : 보호 수준
    - normal : 낮은 수준의 보호. 사용자에게 권한 허용을 요청하지 않아도 됨
    - dangerous : 높은 수준의 보호. 사용자에게 권한 허용을 요청해야 함
    - signature : 같은 키로 인증한 앱만 실행
    - signatureOrSystem : 안드로이드 시스템 앱이거나 같은 키로 인증한 앱만 실행

    👉 protectionLevel 예시
    ACCESS_NETWORK_STATE : normal
    ACCESS_FINE_LOCATION : dangerous

    → protectionLevel이 다른 두 퍼미션을 아래처럼 사용 설정하면, 앱 설치한 후 앱의 권한 화면에는 보호 수준이 dangerous인 퍼미션만 나타남

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  • 앞서 메니페스트 파일에 설정했던 컴포넌트에 적용하는 방법
    • android:permission 속성을 사용해 퍼미션으로 보호하려는 컴포넌트에 적용
    <activity
    	android:name=".MainActivity"
    	android:permission="com.example.TEST_PERMISSION">
    		<intent-filter>
    			<action android:name="android.intent.action.MAIN" />
    		</intent-filter>
    </activity>
  • 해당 컴포넌트는 com.example.TEST_PERMISSION에 의해 보호됨
  • 이 컴포넌트를 이용하는 곳에서 아래처럼 매니페스트 파일에 < uses-permission >으로 선언해 줘야 정상 실행
    <uses-permission android:name="com.example.permission.TEST_PERMISSION" />
  • 안드로이드 시스템에서 보호하는 기능을 사용할 때 사용하는 대표적인 퍼미션들
    • ACCESS_FINE_LOCATION : 위치 정보 접근
    • ACCESS_NETWORK_STATE : 네트워크 정보 접근
    • ACCESS_WIFI_STATE : 와이파이 네트워크 정보 접근
    • BATTERY_STATS : 배터리 정보 접근
    • BLUETOOTH : 블루투스 장치에 연결
    • CAMERA : 카메라 장치에 접근
    • INTERNET : 네트워크 연결
    • READ_EXTERNAL_STORAGE : 외부 저장소에서 파일 읽기
    • READ_PHONE_STATE : 전화기 정보 접근
    • SEND_SMS : 문자 메시지 발신
    • RECEIVE_BOOT_COMPLETED : 부팅 완료 시 실행
    • VIBRATE : 진동 울리기

퍼미션 허용 확인

  • API 레벨 23버전 이후로는 개발자가 선언한 과 상관없이 사용자가 권한 화면에서 권한을 거부할 수 있게 됨
  • 앱 실행 시 사용자가 퍼미션을 거부했는지 확인하고 거부했으면 다시 퍼미션 허용 요청
    • 사용자의 퍼미션 허용 여부를 확인하기 위해 checkSelfPermission() 함수 이용
    • 사용자에게 퍼미션 허용을 요청할 때에는 requestPermissions() 함수 이용
    • 퍼미션 요청 결과는 onRequestPermissionsResult() 함수 이용
    // 퍼미션 허용 확인 함수
    open static fun checkSelfPermission(
    	@NonNULL context: Context,
    	@NonNull permission: String
    ) : Int
    
    // 권한 허용한 경우 PackageManager.PERMISSION_GRANTED 상수 반환
    // 권한 거부한 경우 PackageManager.PERMISSION_DENIED 상수 반환
    // 퍼미션 허용 요청 함수
    // 함수가 실행 시 퍼미션을 요청하는 다이얼로그가 화면에 나타남
    open static fun requestPermissions(
    	@NonNULL activity: Activity,
    	@NonNULL permissions: Array<String!>,
    	@IntRange(0) requestCode: Int
    ): Unit
    // 퍼미션 요청 결과 확인 함수
    abstract fun onRequestPermissionResult(
    	requestCode: Int,
    	@NonNULL permissions: Array<String!>,
    	@NonNULL grantResults: IntArray
    ): Unit

3. 다양한 다이얼로그

토스트 메시지 띄우기

  • makeText() 함수를 이용하는 방법
    • open static fun makeText(contextL Context!, text: CharSequence!, duration: Int): Toast!
    • open static fun makeText(contextL Context!, resId: Int, duration: Int): Toast!
      • 두 번째 매개변수에는 출력할 문자열을, 세 번째 매개변수에는 토스트가 출력되는 시간을 전달 → 출력 시간은 Toast.LENGTH_SHORT(3초) 또는 Toast.LENGTH_LONG(5초) 상수 사용
    Toast.makeText(this, "종료하려면 한 번 더 누르세요", Toast.LENGTH_SHORT).show()
  • setter 함수를 이용하는 방법 문자열이나 화면에 보이는 시간을 설정할 수 있는 세터 함수
    • open fun setDuration(duration: Int): Unit

    • open fun setText(resId: Int): Unit

      토스트가 뜨는 위치를 정할 수 있는 세터 함수

    • open fun setGravity(gravity: Int, sOffset: Int, yOffset: Int): Unit

    • open fun setMargin(horizontalMargin: Float, verticalMargin: Float): Unit

  • 콜백 기능을 이용하는 방법
    • 토스트가 화면에 보이는 순간이나 토스트가 사라지는 순간을 콜백으로 감지해 특정 로직 수행
    @RequiresApi(Build.VERSION_CODES.R)
    	fun showToast() {
    	val toast = Toast.makeText(this, "종료하려면 한 번 더 누르세요", Toast.LENGTH_SHORT)
    		toast.addCallback(
    			object : Toast.Callback() {
    				// 토스트가 뜨는 순간 호출
    				override fun onToastShown() {
    					super.onToastShown()
    				}
    
    				// 토스트가 사라지는 순간 호출
    				override fun onToastHidden() {
    					super.onToastHidden()
    				}
    			})
    		toast.show()
    }

날짜 또는 시간 입력받기

  • 피커(Picker) 다이얼로그 사용
    • 날짜 입력받을 시 DatePickerDialog 사용
    date.setOnClickListener {
    	val cal = Calendar.getInstance(
    	val dateSetListener = 
    		DatePickerDialog.OnDateSetListener { view, year, month, dayOfMonth ->
    			Log.d("kkang", "${year}${month + 1}${dayOfMonth}일") }
                
    	DatePickerDialog(
    		this, 
    		dateSetListener, 
    		cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)
    	).show()
    }
  • 시간 입력받을 시 TimePickerDialog 사용
    time.setOnClickListener {
    	val cal = Calendar.getInstance()    
    	val timeSetListener = 
    		TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute ->
    			Log.d("kkang", "${hourOfDay}${minute}분") }
    
    	TimePickerDialog(
    		this, 
    		timeSetListener, 
    		cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true
    	 ).show()
    }

알림 창 띄우기

  • AlertDialog 사용

  • 알림창은 위부터 차례대로 제목, 내용, 버튼 영역으로 구분 → 원하는 영역만 지정해서 사용 가능

  • 알림창은 생성자가 protected로 선언되어 객체 직접 생성이 불가능하므로 AlertDialog.Builder 사용

  • 알림 창의 아이콘, 제목, 내용을 지정하는 함수

    • open fun setIcon(iconId: Int): AlertDialog.Builder!
    • open fun setTitle(title: CharSequence!): AlertDialog.Builder!
    • open fun setMessage(message: CharSequence!): AlertDialog.Builder!
  • 알림 창의 버튼을 지정하는 함수

    • open fun setPositiveButton(text: CharSequence!, listener: DialogInterface.OnClickListener!): AlertDialog.Builder!

    • open fun setNegativeButton(text: CharSequence!, listener: DialogInterface.OnClickListener!): AlertDialog.Builder!

    • open fun setNeutralButton(text: CharSequence!, listener: DialogInterface.OnClickListener!): AlertDialog.Builder!

      → 첫 번째 매개변수는 버튼의 문자열, 두 번째 매개변수는 버튼 클릭 시 이벤트 핸들러 전달

      ❗버튼 클릭 시 처리할 내용 없으면 두 번째 매개변수에 null 대입

      ❗버튼은 최대 3개까지 추가할 수 있으므로, 같은 함수 여러번 사용 시 하나만 나타나게

    val eventHandler = object: DialogInterface.OnClickListener {
        override fun onClick(p0: DialogInterface?, p1: Int) {
            if(p1 == DialogInterface.BUTTON_POSITIVE) {
                Log.d("kkang", "positive button click")
            } else if(p1 == DialogInterface.BUTTON_NEGATIVE) {
                Log.d("kkang", "negative button click")
            }
        }
    }
    
    (...생략...)
    setPositiveButton("OK", eventHandler)
    setNegativeButton("Cancel", eventHandler)

dialog.setOnClickListener {
	AlertDialog.Builder(this).run {
		setTitle("test dialog")
		setIcon(android.R.drawable.ic_dialog_info)
		setMessage("정말 종료하시겠습니까?")
		setPositiveButton("OK", null)
		setNegativeButton("Cancel", null)
		setNeutralButton("More", null)
		setPositiveButton("Yes", null)
		setNegativeButton("No", null)
		show()
	}
}
  • 목록을 출력하기 위한 함수
    • open fun setItems(items: Array<Char Sequence!>!, listener: DialogInterface.OnClickListener!): AlertDialog.Builder!

    • open fun setMultiChoiceItems(items: Array<Char Sequence!>!, checkedItems: BooleanArray!, listener: DialogInterface.OnMultiChoiceClickListener!): AlertDialog.Builder!

    • open fun setSingleChoiceItems(items: Array<Char Sequence!>!, checkedItem: Int, listener: DialogInterface.OnClickListener!): AlertDialog.Builder!

    // 목록 출력
    val items = arrayOf<String>("사과", "복숭아", "수박", "딸기")
    AlertDialog.Builder(this).run {
    	setTitle("items test")
    	setIcon(android.R.drawable.ic_dialog_info)
    	setItems(items, object: DialogInterface.OnClickListener {
    		override fun onClick(p0: DialogInterface?, p1: Int) {
    		Log.d("kkang", "선택한 과일 : ${items[p1]}")
    		}
    	})
    	setPositiveButton("닫기", null)
    	show()
    }

    // 체크박스
    setMultiChoiceItems(items, booleanArrayOf(true, false, true, false),
    object : DialogInterface.OnMultiChoiceClickListener {
    	override fun onClick(p0: DialogInterface?, p1: Int, p2: Boolean) {
    		Log.d("kkang", "${items[p1]}이 ${if (p2) "선택되었습니다." else "선택 해제되었습니다."}")
    	}
    })

    // 라디오 버튼
    setSingleChoiceItems(items, 1, object : DialogInterface.OnClickListener {
    	override fun onClick(p0: DialogInterface?, p1: Int) {
    		Log.d("kkang", "${items[p1]}이 선택되었습니다.")
    	}
    })
  • 알림창 닫기 속성을 설정하는 함수 매개변수 true로 설정 시 사용자가 기기의 뒤로가기 버튼을 눌렀을 때 알림 창 닫힘
    • open fun setCancelable(cancelable: Boolean): AlertDialog.Builder!

      매개변수 true로 설정 시 알림 창의 바깥 영역을 터치했을 때 닫힘

    • open fun setCancelabledOnTouchOutside(cancel: Boolean): Unit

    AlertDialog.Builder(this).run {
    	(...생략...)
    	})
    	setCancelable(false)
    	setPositiveButton("닫기", null)
    	show()
    }.setCanceledOnTouchOutside(false)

커스텀 다이얼로그 만들기

  • 마찬가지로 AlertDialog 사용
  • LayoutInflater 클래스 이용

    LayoutInflater 클래스란?

    → 레이아웃 XML 파일을 코드에서 초기화하는 기능 제공

    즉, XML에 선언한 뷰를 코드에서 이용하기 위해 생성해주는 역할

  • 커스텀 다이얼로그 구현 방법
    1. LayoutInflater로 레이아웃 XML 파일 초기화
    // XML파일 초기화 
    val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    val rootView = inflater.inflate(R.layout.activity_one, null)
    
    // 뷰 바인딩 기법 적용한 XML파일 초기화
    val binding = ActivityOneBinding.inflate(layoutInflater)
    val rootView = binding.root
1. 다이얼로그를 구성하는 레이아웃 XML파일 작성
2. 작성한 XML파일을 LayoutInflater로 초기화해 다이얼로그에 적용
    val dialogBinding = DialogInputBinding.inflate(layoutInflater)
    AfterDialog.Builder(this).run {
    	setTitle("Input")
    	setView(dialogBinding.root)
    	setPositiveButton("닫기", null)
    	show()
    }

4. 소리와 진동 알림

소리 알림

  • 시스템에 등록된 소리 이용하는 방법
    • RingtonManager : 안드로이드 시스템에서 제공하는 알림, 알람, 벨소리 등의 소리를 가져오는 역할
      1. RingtonManger.getDefaultUri() 함수를 통해 소리의 식별값(Uri 객체) 받아옴
      2. RingtoneManager.getRingtone() 함수의 두 번째 매개변수로 전달
      3. 소리를 재생하는 Ringtone 객체를 받아 play() 함수를 통해 소리 재생
val notification: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val ringtone = RingtoneManager.getRingtone(applicationContext, notification)
ringtone.play()
  • 자체 음원을 준비해 재생하는 방법
    1. res/raw 디렉터리에 음원파일을 리소스로 등록
    2. MediaPlayer 클래스에 리소스 정보 지정한 후 start() 함수 호출해 음원 재생
        val player: MediaPlayer = MediaPlayer.create(this, R.raw.whoosh)
        player.start()

진동 알림

  • 진동 퍼미션 얻기
    <uses-permission android:name="android.permission.VIBRATE" />
  • 진동 객체 얻기
    • Vibrator 클래스를 이용해 getSystemService() 함수로 객체 받음
    val vibrator = getSystemService(VIBRATE_SERVICE) as Vibrator
  • 진동 시간과 패턴 지정 함수API 레벨 1부터 제공하는 함수

    26버전(Android 8)에서 deprecated 되었지만, 이전 버전 기기 사용자를 위해 계속 사용해야 함

    • open fun vibrate(millisecond: Long) : Unit
      • 진동이 울리는 시간 지정하는 함수 (500으로 설정 시 0.5초간 진동 울림)
    • open fun vibrate(pattern: LongArray!, repeat: Int): Unit
      • 첫 번째 매개변수는 진동 패턴 지정 (500, 1000, 500, 2000 의 배열값 전달 시 0.5초 쉬고 1초간 울리고 0.5초 쉬고 2초간 울림)
      • 두 번째 매개변수에는 패턴 반복 횟수 지정 (-1 지정 시 한 번만 울리고, 0으로 지정 시 cancel() 함수로 진동 알림을 끄기 전까지 계속 울림)
  • 진동의 세기까지 지정 가능한 함수API 레벨 26부터 제공하는 함수

    API 레벨 26부터는 진동 정보를 VibrationEffect 객체로 지정할 수 있는 함수 제공

    • open fun vibrate(vibe: VibrationEffect!): Unit
    • open static fun createOneShot(milliseconds: Long, amplitude: Int): VibrationEffect
      • 두 번째 함수로 만든 VibrationEffect 객체를 vibrate() 함수에 대입해 사용
      • 첫 번째 매개변수의 시간 동안 진동이 울리며, 두 번째 매개변수에는 진동의 세기 지정(0~255 숫자)

    👉 기기가 정의한 기본 세기로 진동 사용하려면?

    val vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.0) {
    	vibrator.vibrate(VibrationEffect.createOneShot(
    		500, VibrationEffect.DEFAULT_AMPLITUDE))
    } else {
    		vibrator.vibrate(500)
    }
  • 반복해서 진동을 울리는 함수
    • open static fun createWaveform(timing: LongArray!, amplitudes: IntArray!, repeat: Int): VibrationEffect!
      • 매개변수로는 순서대로 진동이 울리는 패턴, 진동 세기 패턴, 패턴의 반복 횟수 지정
      • 아래 코드 실행 시 처음 0.5초간 진동이 울리지 않다가 1초간 50의 세기로 진동이 울리고, 다시 0.5초간 울리지 않다가 마지막 2초간 200의 세기로 진동이 울리게 됨
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.0) {
    	vibrator.vibrate(VibrationEffect.createWaveform(
    		longArrayOf((500, 1000, 500, 2000),
    		intArrayOf(0, 50, 0, 200), -1))
    } else {
    	vibrator.vibrate(longArrayOf(500, 1000, 500, 2000), -1)
    }

5. 알림 띄우기

  • 알림을 사용하는 상태 바는 앱이 직접 제어할 수 없기 때문에 앱의 화면을 구성하거나 사용자 이벤트를 처리하는 프로그래밍과는 구조가 다르며, 알림을 위해 제공하는 API를 이용해야 함
  • 알림은 NotificationManager의 notify() 함수를 통해 발생 전체 과정
    1. NotificationManager로 알림 채널을 생성
    2. 생성한 채널 정보를 NotificationCompat.Builder()함수에 대입해 NotificationCompat.Builder생성
    3. 생성한 빌더로 Notification 객체 생성
    4. 생성한 객체를 NotificationManager의 notify() 함수에 대입

알림 채널

  • API 레벨 26(Android 8) 버전 이전까지는 NotificationChannel 정보가 필요 없었지만, 26 버전에서 생성자가 deprecated됨
  • 현재는 앱의 알림을 채널로 구분하기 위해 채널의 식별값을 빌더 생성 시 지정
    • Builder(context: Context, channelId: String!)
  • 알림 채널 생성자
    • NotificationChannel(id: String!, name: CharSequence!, importance: Int) → 채널의 식별값과 설정 화면에 표시할 채널 이름 매개변수로 전달 → 세번째 매개변수로는 알림의 중요도를 의미하는 상수를 전달
    • 중요도 상수 NotificationManager.IMPORTANCE_HIGH : 긴급 상황으로 알림음 울리며 헤드업으로 표시 NotificationManager.IMPORTANCE_DEFAULT : 높은 중요도이며 알림음이 울림 NotificationManager.IMPORTANCE_LOW : 중간 중요도이며 알림음이 울리지 않음 NotificationManager.IMPORTANCE_MIN : 낮은 중요도이며 알림음 없고 상태 바에도 표시 안함
    • 채널 각종 정보를 설정하는 함수 fun setDescription(description: String!): Unit : 채널 설명 문자열 fun setShowBadge(showBadge: Boolean): Unit : 홈 화면 아이콘에 배지 아이콘 출력 여부 fun setSound(sound: Uri!, audioAttributes: AudioAttributes!): Unit : 알림음 재생 fun enableLights(lights: Boolean): Unit : 불빛 표시 여부 fun setLightColor(argb: Int): Unit : 불빛이 표시된다면 불빛의 색상 fun enableVibration(vibration: Boolean): Unit : 진동 울릴 지 여부 fun setVibrationPattern(vibrationPattern: LongArray!): Unit : 진동을 울린다면 진동의 패턴
    //알림 빌더
    val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
    val builder: NotificationCompat.Builder
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    	val channelId = "one-channel"
    	val channelName = "My Channel One"
    	val channel =
    	NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)
    
    	// 채널 정보 설정
    	channel.description = "My Channel One Description"
    	channel.setShowBadge(true)
    	val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
    	val audioAttributes = AudioAttributes.Builder()
    		.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
    		.setUsage(AudioAttributes.USAGE_ALARM)
    		.build()
    
    	channel.setSound(uri, audioAttributes)
    	channel.enableLights(true)
    	channel.lightColor = Color.RED
    	channel.enableVibration(true)
    	channel.vibrationPattern = longArrayOf(100, 200, 100, 200)
    
    	// NotificationManager에 채널 등록
    	manager.createNotificationChannel(channel)
    
    	// 채널 이용해 빌더 생성
    	builder = NotificationCompat.Builder(this, channelId)
    
    	} else {
    		builder = NotificationCompat.Builder(this)
    }

알림 객체

  • 앞서 생성된 빌더를 이용해 알림 객체 생성
  • 객체에는 출력할 이미지, 문자열 등의 정보를 담아 상태바에서 내용 출력
  • 알림 객체 설정
builder.setSmallIcon(android.R.drawable.ic_notification_overlay)  //스몰 아이콘
builder.setWhen(System.currentTimeMillis()) //알림 시각
builder.setContentTitle("Content Title")    //제목
builder.setContentText("Content Message")   //내용
  • 알림 발생
    • notify() 함수 이용
    • 첫번째 매개변수는 식별하기 위한 숫자
manager.notify(11, builder.build())
  • 알림 취소
    • cancel() 함수 이용
    • 기본적으로는 알림을 밀어서(스와이프) 취소할 수 있음
manager.cancel(11)
  • 알림 취소 막기
    • 스와이프해도 알림이 사라지지 않게 하려면 함수로 지정해야 함
builder.setAutoCancel(false)    // 알림 터치 시 이벤트는 발생하지만 알림이 사라지지는 않음  
builder.setOngoing(true)      // 스와이프해도 알림이 사라지지 않음

알림 구성

  • 알림 터치 이벤트
    • 알림은 앱이 아닌 시스템에서 관리하는 상태 바에 출력되는 정보이므로 앱의 터치 이벤트로 처리 불가
    • 인텐트에 Notification 객체를 담아 이벤트 발생 시 인텐트가 실행되도록 시스템에 의뢰→ PendingIntent 클래스 이용

      pendingIntent

      컴포넌트별로 실행을 의뢰하는 함수를 제공하는 클래스

      • static fun getActivity(context: Context!, requestCode: Int, intent: Intent!, flags: Int): PendingIntent!
      • static fun getBroadcast(context: Context!, requestCode: Int, intent: Intent!, flags: Int): PendingIntent!
      • static fun getService(context: Context!, requestCode: Int, intent: Intent!, flags: Int): PendingIntent!
    val intent = Intent(this, SubActivity::class.java)
    val pendingIntent =
    	PendingIntent.getActivity(this, 10, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    builder.setContentIntent(pendingIntent)
  • 액션
    • 알림에 액션 최대 3개까지 추가 가능
    • 터치 이벤트와 마찬가지로 액션을 터치했을 때 실행할 인텐트 정보를 PendingIntent로 구성해 등록
      - 액션 등록 시 addAction() 함수 이용
      open fun addAction(action: Notification.Action!): Notification.Builder - 액션 빌더 생성자
      Builder(icon: Int, title: CharSequence!, intent: PendingIntent!)
    val actionIntent = Intent(this, SubActivity::class.java)
    val actionPendingIntent = PendingIntent.getBroadcast(this, 20, 
    	actionIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    
    builder.addAction(
    	NotificationCompat.Action.Builder(
    		android.R.drawable.stat_notify_more,
    		"Action",
    		actionPendingIntent
    	).build()
    )
  • 원격 입력
    • 액션의 한 종류로, 알림에서 사용자 입력을 직접 받는 기법
    // 원격 입력
    val KEY_TEXT_REPLY = "key_text_reply"
    var replyLabel: String = "답장"
    var remoteInput: RemoteInput = RemoteInput.Builder(KEY_TEXT_REPLY).run {
    	setLabel(replyLabel)
    	build()
    }
    
    // 인텐트 준비
    val replyIntent = Intent(this, SubActivity::class.java)
    val replyPendingIntent = PendingIntent.getBroadcast(
    	this, 30, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT
    )
    
    // 원격 입력 액션 등록
    builder.addAction(
    	NotificationCompat.Action.Builder(
    		android.R.drawable.ic_menu_send,
    		"답장",
    		replyPendingIntent
    	).addRemoteInput(remoteInput).build()
    )
    
    // 브로드캐스트 리시버로부터 사용자가 입력한 글 받아오기
    val replyText = RemoteInput.getResultsFromIntent(intent)?.getCharSequence("key_text_reply").toString()
    
    // 알림 갱신
    manager.notify(11, builder.build())
  • 프로그레스
    • 앱에서 작업 시간을 시각적으로 보여주는 역할
    • 화면 필요없이 빌더에 setProgress() 함수만 추가하면 자동으로 생성
      - open fun setProgress(max: Int, progress: Int, indeterminate: Boolean): Notification.Builder
    builder.setProgress(100, 0, false)
    manager.notify(11, builder.build())
    
    // 10초 동안 프로그래스 바 진행값 증가
    thread {
    	for (i in 1..100) {
    		builder.setProgress(100, i, false)
    		manager.notify(11, builder.build())
    		SystemClock.sleep(100)
    	}
    }

알림 스타일

  • 큰 이미지 스타일
    • BigPicturesStyle 이용
    • BigPicturesStyle 객체의 bigPicture 프로퍼티에 출력할 이미지를 비트맵 형식으로 정하고, 이 객체를 빌더의 setStyle() 함수에 지정
        val bigPicture = BitmapFactory.decodeResource(resources, R.drawable.test)
        val bigStyle = NotificationCompat.BigPictureStyle()
        bigStyle.bigPicture(bigPicture)
        builder.setStyle(bigStyle)
  • 긴 텍스트 스타일
    • 긴 문자열을 출력하는 BigTextStyle 이용
    • 이메일 수신 알림으로 주로 사용
        val bigTextStyle = NotificationCompat.BigTextStyle()
        bigTextStyle.bigText(resources.getString(R.string.long_text))
        builder.setStyle(bigTextStyle)
  • 상자 스타일
    • 문자열을 목록으로 출력하는 InboxStyle 이용
        val style = NotificationCompat.InboxStyle()
        style.addLine("first")
        style.addLine("second")
        style.addLine("third")
        style.addLine("fourth")
        builder.setStyle(style)
  • 메시지 스타일
    • 여러 사람이 주고받은 메시지를 구분해서 출력하는 경우에 사용
    • 각각의 메시지는 Message 객체로 표현
      • 메시지 객체 Message(text: CharSequence, timestamp: Long, sender: Person?)
        // Person 객체 생성
        val sender1: Person = Person.Builder()
        	.setName("kkang")
        	.setIcon(IconCompat.createWithResource(this, R.drawable.person1))
        	.build()
        
        val sender2: Person = Person.Builder()
        	.setName("choi")
        	.setIcon(IconCompat.createWithResource(this, R.drawable.person2))
        	.build()
        
        // 메시지 객체 생성
        val message1 = NotificationCompat.MessagingStyle.Message(
        	"hello",
        	System.currentTimeMillis(),
        	sender1
        )
        
        val message2 = NotificationCompat.MessagingStyle.Message(
        	"world",
        	System.currentTimeMillis(),
        	sender2
        )	
        
        // MessageStyle에 메시지 객체 대입
        val messageStyle = NotificationCompat.MessagingStyle(sender1)
        	.addMessage(message1)
        	.addMessage(message2)
        builder.setStyle(messageStyle)
profile
기록

0개의 댓글