>알고리즘 문제
1. 문제
길이가 같은 두 개의 큐가 주어집니다. 하나의 큐를 골라 원소를 추출(pop)하고, 추출된 원소를 다른 큐에 집어넣는(insert) 작업을 통해 각 큐의 원소 합이 같도록 만들려고 합니다. 이때 필요한 작업의 최소 횟수를 구하고자 합니다. 한 번의 pop과 한 번의 insert를 합쳐서 작업을 1회 수행한 것으로 간주합니다.
큐는 먼저 집어넣은 원소가 먼저 나오는 구조입니다. 이 문제에서는 큐를 배열로 표현하며, 원소가 배열 앞쪽에 있을수록 먼저 집어넣은 원소임을 의미합니다. 즉, pop을 하면 배열의 첫 번째 원소가 추출되며, insert를 하면 배열의 끝에 원소가 추가됩니다. 예를 들어 큐 [1, 2, 3, 4]가 주어졌을 때, pop을 하면 맨 앞에 있는 원소 1이 추출되어 [2, 3, 4]가 되며, 이어서 5를 insert하면 [2, 3, 4, 5]가 됩니다.
다음은 두 큐를 나타내는 예시입니다.
queue1 = [3, 2, 7, 2]
queue2 = [4, 6, 5, 1]
두 큐에 담긴 모든 원소의 합은 30입니다. 따라서, 각 큐의 합을 15로 만들어야 합니다. 예를 들어, 다음과 같이 2가지 방법이 있습니다.
- queue2의 4, 6, 5를 순서대로 추출하여 queue1에 추가한 뒤, queue1의 3, 2, 7, 2를 순서대로 추출하여 queue2에 추가합니다. 그 결과 queue1은 [4, 6, 5], queue2는 [1, 3, 2, 7, 2]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 7번 수행합니다.
- queue1에서 3을 추출하여 queue2에 추가합니다. 그리고 queue2에서 4를 추출하여 queue1에 추가합니다. 그 결과 queue1은 [2, 7, 2, 4], queue2는 [6, 5, 1, 3]가 되며, 각 큐의 원소 합은 15로 같습니다. 이 방법은 작업을 2번만 수행하며, 이보다 적은 횟수로 목표를 달성할 수 없습니다.
따라서 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수는 2입니다.
길이가 같은 두 개의 큐를 나타내는 정수 배열 queue1, queue2가 매개변수로 주어집니다. 각 큐의 원소 합을 같게 만들기 위해 필요한 작업의 최소 횟수를 return 하도록 solution 함수를 완성해주세요. 단, 어떤 방법으로도 각 큐의 원소 합을 같게 만들 수 없는 경우, -1을 return 해주세요.
제한사항
- 1 ≤ queue1의 길이 = queue2의 길이 ≤ 300,000
- 1 ≤ queue1의 원소, queue2의 원소 ≤ 109
- 주의: 언어에 따라 합 계산 과정 중 산술 오버플로우 발생 가능성이 있으므로 long type 고려가 필요합니다.
2. 솔루션
class Solution {
fun solution(queue1: IntArray, queue2: IntArray): Int {
var answer=0
var q1sum=queue1.map{it.toLong()}.sum()
var target=(q1sum+queue2.map{it.toLong()}.sum())
if(target%2!=0L) return -1
target/=2
var left=0
var right=queue1.size
val q=queue1+queue2
while(left<q.size&&right<q.size){
if(q1sum==target) return answer
if(q1sum<target){
q1sum+=q[right++]
answer++
}else{
q1sum-=q[left++]
answer++
}
}
return -1
}
}
-두 큐의 합을 각각 구했을때 둘이 같아진다는것은 한 큐의 합이 두 큐 모든 원소의 합을 구해 반으로 나눈것과 같아진다는 말이므로 queue1의 합만 살펴봐도 된다. =>한 큐의 합이 타겟값과 같아지는지 체크
-그리디 알고리즘을 이용: queue1이 될수 있는 원소의 나열은 queue1과 queue2를 이어붙인것의 부분수열이므로 새로운 q=queue1+queue2의 부분수열의 합을 체크하면 된다.
-시작은 아무것도 poll하지 않은 상태이므로 queue1의 합에서부터 시작한다.
-이후 while문을 돌면서 타겟보다 크면 left를 올려주고, 타겟보다 작으면 right를 올려주며 부분수열의 합을 갱신한다.
@위 방법을 사용하는 이유
-시작 부분수열의 합을 s1, 타겟을 t라 하자
-s1>t일때 최적으로 가기위해 처음에 바로 left를 올려주는 것 말고 다른 방법이 있다고 하자
-그렇다고 하더라도 left를 언젠가는 올려줘야 함.
-순서가 바뀌는 것을 아무 의미 없으므로 바로 left를 올려주는 것이 최적의 방법이 된다. s<t인 경우도 마찬가지
-이 후에 생성된 부분수열에 대해 합이 si라 할때 그 역시도 마찬가지이기 때문에 위 방법을 사용한다.
>숙련 1주차 강의
1. 프레그먼트의 데이터 전달
(1)Activity -> Fragment :companion object를 이용
-프래그먼트의 newInstance 메소드로 데이터를 번들로 전달하며 프래그먼트 생성
binding.run {
fragment1Btn.setOnClickListener{
// [1] Activity -> FirstFragment
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragment(fragment)
}
fragment2Btn.setOnClickListener {
// [1] Activity -> SecondFragment
val dataToSend = "Hello Second Fragment!\n From Activity"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
-받은 정보를 프래그먼트에서 argument에 번들 저장 후 onCreate에서 번들값을 프로퍼티에 할당한 onViewCreated()에서 view에 적용시킴
private var param1: String? = null
override fun onCreate(saveInstanceState: Bundle?){
super.onCreate(saveInstanceState)
argument?.let{
param1 = it.getString(ARG_PARAM1)
}
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
// [1] Activity -> FirstFragment
FirstFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// [1] Activity -> FirstFragment
binding.tvFrag1Text.text = param1
}
-ARG_PARAM1은 클래스 바깥에 전역으로 설정한 private const val인 상수
private const val ARG_PARAM1= "param1"
-arguments: 프래그먼트의 번들 프로퍼티라 보면 된다.
=>Bundle()을 파라미터로 받고 번들은 put(key:String, value)로 데이터를 넣을 수 있다.
=>기본 타입뿐만 아니라 parcelable까지 포함 가능
-@JvmStatic: 변수나 메소드를 static처럼 사용하기 위함
<1>static: 클래스 변수에 쓰이는 키워드로 따로 메모리에서 저장해서 프로그램 종료시까지 유지된다. 객체 생성없이 접근이 가능하다.
<2>companion object와 static의 차이: 자바로 변환시 차이점이 생김
-class A안에 companion object가 있으면 자바에선 class A안에 companion이라는 클래스가 생김
-어노테이션을 사용하지 않은 변수에 대해서는 companion에만 getter,setter가 생성됨. 이때 변수는 class A의 멤버로 존재
=>이러면 static멤버에 대한 접근을 companion의 getter로만 접근할 수있게됨.
-어노테이션을 사용하면 companion뿐만 아니라 class A에서도 해당 멤버에 대한 getter,setter가 생성됨
-결과: A의 static 멤버에 접근하기위해서 A.companion.getter()로 사용해야하는 함수를 A.getter()로 접근할 수 있게해줌.
=>기존 자바에서 private static 멤버에 대한 접근을 A.getter로 하던대로 할 수 있게 해줌.
=>완전히 static처럼 사용하게 해줌
=>즉, java와 혼용되는 경우 호환성을 위해서 이렇게 해주는 것.
(2)Fragment ->Fragment
// [2] Fragment -> Fragment
binding.btnGofrag2.setOnClickListener{
val dataToSend = "Hello Fragment2! \n From Fragment1"
val fragment2 = SecondFragment.newInstance(dataToSend)
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, fragment2)
.addToBackStack(null)
.commit()
}
- requireActivity().supportFragmentManager 부분은 parentFragmentManager로 써도 됨
-beginTransaction().~.commit()은 commit{ ~ }으로 대체 가능
(3)Fragment -> Activity
-클래스 외부 전역에 FragmentDataListener 인터페이스를 정의하고 액티비티가 이 인터페이스를 정의했는지 onAttach에서 체크
=>액티비티가 인터페이스를 상속받고 있어야 context is FragmentDataListener 부분을 통과할 수 있음
-상속받아서 구현중이면 해당 context를 프래그먼트의 프로퍼티인 listener에 저장
-onViewCreated에서 버튼을 눌렀을시 listener에 저장된 activity의 내의 오버라이드된 onDataReceived를 호출해서 데이터를 보냄
-마지막으로 onDestroyView에서 listener객체 해제
private const val ARG_PARAM1 = "param1"
interface FragmentDataListener {
fun onDataReceived(data: String)
}
class SecondFragment : Fragment() {
// [3] SecondFragment -> Activity
private var listener: FragmentDataListener? = null
private var param1: String? = null
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
// [3] SecondFragment -> Activity
if (context is FragmentDataListener) {
listener = context
} else {
throw RuntimeException("$context must implement FragmentDataListener")
}
}
...
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// [2] Fragment -> Fragment
binding.tvFrag2Text.text = param1
// [3] SecondFragment -> Activity
binding.btnSendActivity.setOnClickListener{
val dataToSend = "Hello from SecondFragment!"
listener?.onDataReceived(dataToSend)
}
}
...
override fun onDestroyView() {
super.onDestroyView()
// Binding 객체 해제
_binding = null
listener = null
}
}
-액티비티에서는 인터페이스를 상속받고 데이터를 받을 메소드를 오버라이드해줌
class MainActivity : AppCompatActivity(), FragmentDataListener {
.
.
.
// [3] SecondFragment -> Activity
override fun onDataReceived(data: String) {
// Fragment에서 받은 데이터를 처리
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
}
2. 다이얼로그
(1)다이얼로그란
-사용자에게 결정을 내리거나 추가정보를 입력하라는 메시지를 표시하는 작은 창
-다음으로 진행전 조치를 취해야하는 모달 이벤트에 사용
@모달: 기존 화면위에 플로팅 되는 화면
(2)다이얼로그 구조
-AlertDialog클래스를 사용하면 여러 대화 상자 디자인 빌드 가능
-영역
<1>제목: 선택사항, 컨텐츠 영역이 채워져 있는 경우에만 사용
<2>컨텐츠 영역: 메시지, 목록, 혹은 맞춤 레이아웃 표시
<3>작업버튼: 버튼은 3개까지 가능
(3)사용법
<1>기본 다이얼로그
var builder = AlertDialog.Builder(this)
builder.setTitle("기본 다이얼로그 타이틀")
builder.setMessage("기본 다이얼로그 메세지")
builder.setIcon(R.mipmap.ic_launcher)
-빌더 생성 후 타이틀, 타이틀 앞에 붙는 아이콘, 컨텐츠영역의 메시지를 설정
val listener = object : DialogInterface.OnClickListener {
override fun onClick(p0: DialogInterface?, p1: Int) {
when (p1) {
DialogInterface.BUTTON_POSITIVE ->
binding.tvTitle.text = "BUTTON_POSITIVE"
DialogInterface.BUTTON_NEUTRAL ->
binding.tvTitle.text = "BUTTON_NEUTRAL"
DialogInterface.BUTTON_NEGATIVE ->
binding.tvTitle.text = "BUTTON_NEGATIVE"
}
}
}
-각 버튼 클릭별 작업을 설정
builder.setPositiveButton("Positive", listener)
builder.setNegativeButton("Negative", listener)
builder.setNeutralButton("Neutral", listener)
builder.show()
-각 버튼별 이름을 설정하고 listener를 연결시켜줌
-show()로 보여줌
<2>커스텀 다이얼로그
// 2. 커스텀 다이얼로그
binding.btn2Custom.setOnClickListener {
val builder = AlertDialog.Builder(this)
builder.setTitle("커스텀 다이얼로그")
builder.setIcon(R.mipmap.ic_launcher)
val v1 = layoutInflater.inflate(R.layout.dialog, null)
builder.setView(v1)
// p0에 해당 AlertDialog가 들어온다. findViewById를 통해 view를 가져와서 사용
val listener = DialogInterface.OnClickListener { p0, p1 ->
val alert = p0 as AlertDialog
val edit1: EditText? = alert.findViewById<EditText>(R.id.editText)
val edit2: EditText? = alert.findViewById<EditText>(R.id.editText2)
binding.tvTitle.text = "이름 : ${edit1?.text}"
binding.tvTitle.append(" / 나이 : ${edit2?.text}")
}
builder.setPositiveButton("확인", listener)
builder.setNegativeButton("취소", null)
builder.show()
}
-setView() 부분에 아래 코드로 대체 가능
val v1=DialogBinding.inflate(LayoutInflater.from(this),null,false)
builder.setView(v1.root)
-listner부분은 아래 코드로 대체 가능
val listener = DialogInterface.OnClickListener {_,int->
when (int){
DialogInterface.BUTTON_POSITIVE ->run {
binding.tvTitle.text = "이름 : ${v1.editText.text}"
binding.tvTitle.append(" / 나이 : ${v1.editText2.text}")
}
else -> return@OnClickListener
}
}
=>지금 경우는 negative버튼에 리스너를 설정 안해서 그냥 나가지게 하여서 리스너 이벤트가 고려할 경우의 수가 한가지 뿐이므로 when없이 그냥 해도 괜찮음
val listener = DialogInterface.OnClickListener {_,_->
binding.tvTitle.text = "이름 : ${v1.editText.text}"
binding.tvTitle.append(" / 나이 : ${v1.editText2.text}")
}
@append(): 텍스트뷰의 기존 텍스트에서 새 텍스트를 이어붙여주는 역할을 한다.
<3>날짜 다이얼로그
binding.btn3Date.setOnClickListener {
val calendar = Calendar.getInstance()
val year = calendar.get(Calendar.YEAR)
val month = calendar.get(Calendar.MONTH)
val day = calendar.get(Calendar.DAY_OF_MONTH)
val listener = DatePickerDialog.OnDateSetListener { datePicker, i, i2, i3 ->
// i년 i2월 i3일
binding.tvTitle.text = "${i}년 ${i2 + 1}월 ${i3}일"
}
var picker = DatePickerDialog(this, listener, year, month, day)
picker.show()
}
-calendar을 이용해 오늘 날짜를 받아 DatePickerDialog에 설정함
-listener도 한번에 설정
@버튼 커스터마이징 하는 방법
-show()아래에서 버튼을 가져와 색이나 글을 바꿀 수 있다.
picker.show()
picker.getButton(DatePickerDialog.BUTTON_POSITIVE).setText("설정")
picker.getButton(DatePickerDialog.BUTTON_NEGATIVE).setText("취소")
<4>시간 다이얼로그
// 4. 시간 다이얼로그
binding.btn4Time.setOnClickListener {
val calendar = Calendar.getInstance()
val hour = calendar.get(Calendar.HOUR)
val minute = calendar.get(Calendar.MINUTE)
val listener = TimePickerDialog.OnTimeSetListener { timePicker, i, i2 ->
binding.tvTitle.text = "${i}시 ${i2}분"
}
val picker = TimePickerDialog(this, listener, hour, minute, false) // true하면 24시간 제
picker.show()
}
-calendar을 이용해 시간을 얻어서 TimePickerDialog에 설정
=>매개변수중 마지막은 24시간으로 보여줄지 12시간으로 보여줄지 여부: true가 24시간
-날짜 다이얼로그와 마찬가지로 버튼 변경가능
-TimePickerDialog()에서 두번째 인자가 생략되어있는데 이를 이용해서 다른 테마 적용가능
=>예시
val picker = TimePickerDialog(this, android.R.style.Theme_DeviceDefault_Dialog_Alert,listener, hour, minute, false)
<5>프로그래스 다이얼로그
binding.btn5Porgress.setOnClickListener {
val builder = AlertDialog.Builder(this)
builder.setTitle("프로그래스바")
builder.setIcon(R.mipmap.ic_launcher)
val v1 = layoutInflater.inflate(R.layout.progressbar, null)
builder.setView(v1)
builder.show()
}
<?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:gravity="center"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="match_parent"
android:layout_height="76dp"
android:layout_gravity="center"
android:layout_marginTop="48dp"
android:indeterminate="false"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="loading..."
android:textSize="16sp"
android:layout_marginTop="16dp"/>
</LinearLayout>
-커스텀 다이얼로그로 만들 수 있음
-배경색 바꾸는 방법
val alertDialog=builder.create()
alertDialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
alertDialog.show()
-주의해야할것은 builder가 아니라 alertDialog로 show()해야 한다는 것이다.
@다이얼로그 배경색 설정
-일반 다이얼 로그에서는 위에서 바꾸는 배경색이 그 배경색이 맞음
-커스텀 다이얼로그의 경우 다른 레이아웃을 인플레이트 시키기 때문에 배경색은 배경 그림자 같은 느낌
-예시:여기서 하얀색이 배경
=>이 때문에 완전히 커스텀 하기 위해서는 버튼과 제목을 없애고 배경색까지 transparent로 바꿔야 한다.
(4)DialogFragment
class MyDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(it)
builder.setMessage("Do you like this app?")
.setPositiveButton("Yes") { dialog, id ->
// Send the positive button event back to the host activity
}
.setNegativeButton("No") { dialog, id ->
// Send the negative button event back to the host activity
}
// Create the AlertDialog object and return it
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}
MyDialogFragment().show(supportFragmentManager,null)
-아래 코드에서 태그는 아무 문자열 넣으면 됨
-activity는 getActicity()와 같음
-액티비티를 context로 받아서 builder.create()를 반환함
-프래그먼트를 show()하면 다이얼로그가 뜸
3. 알림(Notification)
(1)알림이란: 단말기 상단에 표시되는 것으로 간단한 작업이 가능하고 알림을 눌러 앱 열기 가능
(2)알림 채널(안드로이드 8.0이상)
-알림 만들기 전에 알림 채널을 만들어야함
-알림을 그룹화해 알림 활성화나 방식 변경 가능
-코드
private val myNotificationID = 1
private val channelID = "default"
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0
val channel = NotificationChannel(channelID, "default channel",
NotificationManager.IMPORTANCE_DEFAULT)
channel.description = "description text of this channel."
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
(3)알림 생성
private val myNotificationID = 1
private fun showNotification() {
val builder = NotificationCompat.Builder(this, channelID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("title")
.setContentText("notification text")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
NotificationManagerCompat.from(this).notify(myNotificationID, builder.build())
}
-NotificationCompat.Builder로 객체 알림에 대한 UI정보와 작업 지정
-NotificationCompat.Builder.build() 호출:Notification 객체 반환
- NotificationManagerCompat.notify() 호출로 시스템에 Notification객체 전달 =>알림
-builer에 들어갈 수 있는 내용
<1>setSmallIcon: 아이콘
<2>setContentTitle: 제목
<3>setContentText(): 세부내용
<4>setPriority: 중요도
[1]채널 중요도(안드로이드 8.0이상): 채널에서 중요도 설정
val channel = NotificationChannel(channelID, "default channel",
NotificationManager.IMPORTANCE_DEFAULT)
[2]알림 우선순위(안드로이드 7.1이하): 위에 코드에서처럼 NotificationCompat.Builder(this,channelID)에서 setPriority로 설정한다.
NotificationCompat.Builder(this,channelID).setPriority(NotificationCompat.PRIORITY_DEFAULT)
[3]중요도 순위
<5>긴 텍스트: 긴 텍스트를 추가한 확장뷰로 알림에 넣음
builder.setStyle(NotificationCompat.BigTextStyle().bigText(resources.getString(R.String.아이디)))
<6>그림: 우측에 작은 그림이다가 확장뷰에서 큰 그림으로 보여짐
val bitmap=BitmapFactory.decodeResource(resources, R.drawable.아이디)
builder.setStyle(NotificationCompat.BigPictureStyle().
bigPicture(bitmap).
bigLargeIcon(null)) /큰 아이콘을 확장뷰동안 숨기기
<7>버튼: 버튼을 누르면 activity나 broadcast시작
val intent=Intent(this, TestActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this,0,intent,0)
builder.addAction(R.drawable.아이디, 버튼텍스트:String(),pendingIntent)
-addAction에서 첫 매개변수인 drawable리소스의 경우 안드로이드 7.0부터는 안 보이게 변함.
-PendingIntent.getActivity()
[1]첫번째 매개변수: context
[2]두번째 매개변수: Request Code=>pendingIntent를 고유하게 식별하기 위해 사용.
[3]세번째 매개변수:Intent
[4]네번째 매개변수:Flags =>PendingIntent의 동작방식을 의미하며 0은 기본동작을 의미
@Flags 종류
- FLAG_UPDATE_CURRENT : 이미 생성된 PendingIntent가 있다면, 덮어씌움
- FLAG_CANCLE_CURRENT : 이미 생성된 PendingIntent가 있다면, 취소 후 새로 생성
- FLAG_NO_CREATE : 이미 생성된 PendingIntent가 있다면, 그걸 사용
- FLAG_ONE_SHOT : 해당 PendingIntent를 일회성으로 사용
@PendingIntent
=>보류 인텐트: Intent를 바로 시작하지않고 특정 시점에 시작하도록 해주는 클래스
<8>프로그래스바
//스레드로 프로그래스바 업데이트
Thread{
for(i in (1..100).step(10)){
Thread.sleep(1000)
builder.setProgress(100,i,false)
NotificationManagerCompat.from(this).notify(myNotificationID, builder.build())
}
builder.setContentText("Completed").setProgress(0,0,false) //max=0이면 프로그래스바 사라짐
NotificationManagerCompat.from(this).notify(myNotificationID, builder.build())
}.start()
<9>알림에 액티비티 연결 예
class MainActivity : AppCompatActivity() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
binding.notificationButton.setOnClickListener{
notification()
}
}
fun notification(){
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val builder: NotificationCompat.Builder
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
// 26 버전 이상
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)
}
// 채널을 NotificationManager에 등록
manager.createNotificationChannel(channel)
// 채널을 이용하여 builder 생성
builder = NotificationCompat.Builder(this, channelId)
}else {
// 26 버전 이하
builder = NotificationCompat.Builder(this)
}
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.flower)
val intent = Intent(this, SecondActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
// 알림의 기본 정보
builder.run {
setSmallIcon(R.mipmap.ic_launcher)
setWhen(System.currentTimeMillis())
setContentTitle("새로운 알림입니다.")
setContentText("알림이 잘 보이시나요.")
setStyle(NotificationCompat.BigTextStyle()
.bigText("이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다.이것은 긴텍스트 샘플입니다.
아주 긴 텍스트를 쓸때는 여기다 하면 됩니다.이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다."))
setLargeIcon(bitmap)
// setStyle(NotificationCompat.BigPictureStyle()
// .bigPicture(bitmap)
// .bigLargeIcon(null)) // hide largeIcon while expanding
addAction(R.mipmap.ic_launcher, "Action", pendingIntent)
}
manager.notify(11, builder.build())
}
}
@권한추가
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<!-- API 33 이상을 위한 알림 권한 추가 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
...
</manifest>
@권한추가 알림
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
// 알림 권한이 없다면, 사용자에게 권한 요청
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
}
startActivity(intent)
}
}
'코틀린-안드로이드' 카테고리의 다른 글
52일차)알고리즘 문제(행렬 테두리 회전하기, 전력망을 둘로 나누기), AppleMarket 과제, MVVM과제 , 특강 정리(View Rendering, ViewBinding, Fragment 데이터 전달, ViewPager2와 TabLayout) (1) | 2024.07.15 |
---|---|
51일차)알고리즘 문제(억억단을 외우자, 무인도 여행) (0) | 2024.07.13 |
49일차)알고리즘 문제, 숙련 1주차 강의(프레그먼트) (0) | 2024.07.11 |
48일차)알고리즘 문제(삼각 달팽이, 블록 게임), 숙련 1주차 강의(뷰바인딩, 어댑터뷰) (0) | 2024.07.10 |
47일차)알고리즘 문제(큰 수 만들기), 챌린지반 3주차 강의(디자인 패턴, MVVM), 챌린지반 과제 (1) | 2024.07.09 |