안드로이드의 노티피케이션 종류와 구현 방법에 대해서 정리하였습니다. Notification을 구현하려면 먼저 Notification Channel을 생성해야 합니다. 그리고 BigText, BigPicture, Inbox, Messaging, Media Style 등으로 Notification을 등록할 수 있습니다. 또한 Head Up으로 화면에 띄우거나, Reply 버튼을 추가할 수도 있습니다.
안드로이드 Q를 기준으로 Notificaiton을 어떻게 구현하는지, 화면에 어떻게 보이는지 알아보겠습니다.
이 글에서 소개하는 코드는 모두 코틀린으로 작성되었습니다. 모든 코드는 GitHub에서 확인할 수 있습니다.
Notification Channel 생성
Android O(API 26)에서 Notification Channel 개념이 추가되었습니다. 그래서 Android O이상의 디바이스에 Notification을 띄우려먼 먼저 Channel을 생성해야 합니다.
다음 코드는 Channel을 생성하는 코드입니다.
private fun createNotificationChannel(context: Context, importance: Int, showBadge: Boolean,
name: String, description: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "${context.packageName}-$name"
val channel = NotificationChannel(channelId, name, importance)
channel.description = description
channel.setShowBadge(showBadge)
val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
}
}
NotificationManager.createNotificationChannel() 로 채널을 생성할 수 있으며, 채널 아이디, 채널 이름, 채널의 중요도로 생성한 NotificationChannel 객체를 인자로 전달합니다.
- Channel Id : 앱마다 unique한 ID를 생성해야 하며, 길면 잘릴 수 있습니다.
- Channel Name : 사용자에게 보여지는 채널의 이름입니다.
- Channel Importance: 채널이 중요도를 의미하며, IMPORTANCE_DEFAULT, IMPORTANCE_HIGH 등으로 설정할 수 있습니다.
채널이 생성되면, 노티피케이션을 등록할 수 있습니다. 노티피케이션을 등록할 때 채널을 만들 때 사용한 Channel Id를 사용해야 합니다.
Notification 등록
채널을 만들었다면, 등록한 채널 ID로 노티피케이션을 등록할 수 있습니다. 간단한 노티는 아래와 같이 아이콘, 제목, 컨텐츠를 보여줍니다.
아래 코드는 기본적인 노티피케이션을 등록하는 코드입니다.
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT,
false, getString(R.string.app_name), "App notification channel") // 1
val channelId = "$packageName-${getString(R.string.app_name)}" // 2
val title = "Android Developer"
val content = "Notifications in Android P"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT) // 3
val builder = NotificationCompat.Builder(this, channelId) // 4
builder.setSmallIcon(R.drawable.ic_codechacha) // 5
builder.setContentTitle(title) // 6
builder.setContentText(content) // 7
builder.priority = NotificationCompat.PRIORITY_DEFAULT // 8
builder.setAutoCancel(true) // 9
builder.setContentIntent(pendingIntent) // 10
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build()) // 11
주석에 '// 1'
처럼 중요한 부분에 번호를 붙였고, 아래에 자세히 설명하였습니다.
- 노티피케이션 채널을 만들지 않았으면 만듭니다.
- 채널을 만들 때 사용한 채널 ID입니다. 노티 등록할 때 필요합니다.
- PendingIntent 입니다. 노티를 터치했을 때 액티비티를 실행하기 위해 필요합니다.
- Notification을 만드는 Builder입니다. 여기에 channelId를 인자로 넣어야 합니다.
- 노티에 보이는 아이콘입니다.
- 노티에 보이는 타이틀입니다.
- 노티의 타이틀 아래에 보이는 텍스트 입니다.
- 노티의 중요도를 나타냅니다. 중요도에 따라서 노티가 보여지지 않을 수 있습니다.
- AutoCancel이 true이면 사용자가 노티를 터치했을 때 사라지게 합니다. false면 눌러도 사라지지 않습니다.
- 위에서 만든 PendingIntent를 노티에 등록합니다.
- NotificationManager.notify()으로 노티를 등록합니다.
ID가 같으면 1개의 노티만 생성되고, 다르면 여러개의 노티가 생성됩니다. ID를 알면 그 ID로 등록된 노티를 코드로 취소할 수도 있습니다.
정리하면, 노티피케이션은 Notification.Builder를 이용하여 생성하며, 이 과정에서 PendingIntent 및 리소스들을 설정할 수 있습니다. 그리고 NotificationManager.notify()를 통해 생성한 노티를 등록할 수 있습니다.
BigText Style
기본적인 노티 말고, 다른 커스텀 노티를 만들고 싶을 때가 있습니다. 안드로이드는 자주 사용되는 노티피케이션들을 스타일이라는 개념으로 사용할 수 있도록 만들었습니다. 적용하는 방법은 먼저 스타일 객체를 생성하고, Builder에 추가하여 노티를 생성하면 됩니다.
가장 먼저 BigText 스타일에 대해서 알아보겠습니다. BigTextStyle은 많은 양의 텍스트를 보여줄 수 있는 노티입니다. 기본 노티와 다르게, BigTextStyle 노티는 접고(collapse) 펼 수(expand) 있습니다.
BigTextStyle이 적용된 코드는 다음과 같습니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT, false,
getString(R.string.app_name), "App notification channel")
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "Android Developer"
val content = "Notifications in Android P"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val bigText = "Android 9 introduces several enhancements to notifications," +
" all of which are available to developers targeting API level 28 and above."
val style = NotificationCompat.BigTextStyle() // 1
style.bigText(bigText) // 2
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title)
builder.setContentText(content)
builder.setStyle(style) // 3
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
builder.setContentIntent(pendingIntent)
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
위와 동일한 방식으로 설명을 합니다. Basic 노티와 동일한 설정에 대한 설명은 생략하였습니다.
- BigTextStyle 객체를 생성합니다.
- 스타일에 bigText(긴 텍스트)를 설정합니다.
- Builder에 BigTextStyle을 설정합니다.
기본적인 노티 생성 코드와 대부분 동일하고, 스타일 객체가 추가되는 것이 다른 부분입니다.
BigPicture Style
BigPictureStyle은 큰 이미지를 보여주는 노티피케이션입니다.
다음은 BigPictureStyle이 적용된 노티피케이션 코드입니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT, false,
getString(R.string.app_name), "App notification channel")
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "Castle"
val content = "Welcome to my catle"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val style = NotificationCompat.BigPictureStyle() // 1
style.bigPicture(
BitmapFactory.decodeResource(resources, R.drawable.castle)) // 2
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title)
builder.setContentText(content)
builder.setStyle(style) // 3
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
builder.setContentIntent(pendingIntent)
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
- BigPictureStyle 객체를 생성합니다.
- 스타일에 Big Picture를 등록합니다. 인자는 Bitmap 이미지를 받습니다.
BitmapFactory로 리소스를 Bitmap으로 변환하였습니다. 3. Builder에 스타일을 설정하였습니다.
기본적인 노티피케이션 코드에 BigPictureStyle이 적용되었습니다.
Inbox Style
InboxStyle은 많은 양의 아이템들을 표현하는데 좋은 노티피케이션 스타일입니다. 예로, 이메일 앱 등에서 쓰일 수 있습니다. 이 스타일의 노티도 접었다 펼 수 있습니다.
다음은 InboxStyle이 적용된 노티피케이션 코드입니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT, false,
getString(R.string.app_name), "App notification channel")
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "3 Mails" // 1
val content = "+5 Mails" // 2
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val style = NotificationCompat.InboxStyle() // 3
style.addLine("Mail1 ...") // 4
style.addLine("Mail2 ...")
style.addLine("Mail3 ...")
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title)
builder.setContentText(content)
builder.setStyle(style) // 5
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
builder.setContentIntent(pendingIntent)
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
- 노티의 타이틀입니다.
- 노티가 접혔을 때 보이는 텍스트입니다. 펼쳐졌을 때는 보이지 않습니다.
- InboxStyle 객체를 생성합니다.
- addLine()으로 리스트의 아이템들을 등록하며, 노티가 펼쳐졌을 때 보이는 텍스트입니다.
- Builder에 스타일을 설정합니다.
Messaging Style
MessagingStyle은 사용자 간의 대화 내용을 표현할 때 쓰일 수 있는 스타일입니다. 아래 그림처럼 메시지 앱에서 볼 수 있을 것 같은 노티피케이션입니다.
다음은 MessagingStyle이 적용된 노티피케이션의 코드입니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT, false,
getString(R.string.app_name), "App notification channel")
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "Messaging style title"
val content = "Messaging style content"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val userIcon1 = IconCompat.createWithResource(this, R.drawable.codechacha) // 1
val userIcon2 = IconCompat.createWithResource(this, R.drawable.android)
val userIcon3 = IconCompat.createWithResource(this, R.drawable.android)
val userName1 = "Chacha" // 2
val userName2 = "Android"
val userName3 = "JS"
val timestamp = System.currentTimeMillis() // 3
val user1 = Person.Builder().setIcon(userIcon1).setName(userName1).build() // 4
val user2 = Person.Builder().setIcon(userIcon2).setName(userName2).build()
val user3 = Person.Builder().setIcon(userIcon3).setName(userName3).build()
val style = NotificationCompat.MessagingStyle(user3) // 5
style.addMessage("You can get great deals there", timestamp, user1) // 6
style.addMessage("I know what to get", timestamp, user2) // 7
val builder = NotificationCompat.Builder(this, channelId) // 8
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title)
builder.setContentText(content)
builder.setStyle(style) // 9
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
builder.setContentIntent(pendingIntent)
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
- Person 객체 생성에 쓰이는 Icon 객체를 만듭니다.
- Person 객체의 이름입니다.
- Message 등록할 때 쓰이는 시간입니다. 샘플이기 때문에 현재시간을 넣어주었습니다.
- Person.Builder를 이용하여 Person객체를 만들었습니다. 여기에 아이콘과 이름이 들어갑니다.
- MessagingStyle 객체를 생성하며, 인자로 Person 객체를 전달합니다. Person은 메시지를 받는 사람입니다.
Inline Reply 기능을 추가했을 때 바로 답장이 가능하며, 이 인자의 Person객체가 답장한 것처럼 됩니다. Inline Reply는 아래에서 소개됩니다. 6. User1이 보낸 메시지를 추가하였씁니다. 7. User2가 보낸 메시지를 추가하였습니다. 8. Builder를 생성합니다. 9. Builder에 스타일을 설정합니다.
MessagingStyle의 인자로 들어가는 User는 메시지를 받는 User입니다. addMessage()
에 들어가는 Person 인자는 메시지를 보낸 User입니다.
Media Style
MediaStyle은 음악 재생 관련 노티에 사용될 수 있는 스타일입니다. 아래 그림처럼 Play, Skip Prev 등의 버튼이 있고 버튼이 눌렸을 때 Media Session을 제어 할 수 있습니다. 여기서는 노티를 띄우는 것에 대해서만 설명합니다.
다음은 MediaStyle이 적용된 코드입니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT, false,
getString(R.string.app_name), "App notification channel")
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "Don't Say a Word"
val content = "Ellie Goulding"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title) // 1
builder.setContentText(content) // 2
builder.setLargeIcon(
BitmapFactory.decodeResource(resources, R.drawable.castle)) // 3
builder.addAction(NotificationCompat.Action(
R.drawable.ic_thumb_down,"skip prev", pendingIntent)) // 4
builder.addAction(NotificationCompat.Action(
R.drawable.ic_skip_prev,"skip prev", pendingIntent))
builder.addAction(NotificationCompat.Action(
R.drawable.ic_pause,"pause", pendingIntent))
builder.addAction(NotificationCompat.Action(
R.drawable.ic_skip_next,"skip next", pendingIntent))
builder.addAction(NotificationCompat.Action(
R.drawable.ic_thumb_up,"skip prev", pendingIntent))
builder.setStyle(MediaStyle().setShowActionsInCompactView(1, 2, 3)) // 5
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
builder.setContentIntent(pendingIntent)
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
- 노티에 보여지는 타이틀입니다.
- 타이틀 아래에 보여지는 컨텐츠입니다.
- 노티의 오른쪽에 보여지는 이미지 입니다.
- 노티에 보여지는 아이콘입니다. addAction()으로 추가하며, 아이콘, 타이틀, PendingIntent를 인자로 넣습니다.
아이콘이 눌리면 PendingIntent가 실행됩니다. 이 예제에서는 5개의 아이콘을 addAction()으로 추가했습니다. 5. Builder에 MediaStyle 스타일을 만들고 추가합니다. MediaStyle을 생성하고 setShowActionsInCompactView(1, 2, 3)를 설정하였습니다. 이 코드는 노티가 접혔을 때 Index 1, 2, 3번 아이콘만 보이도록 만드는 것입니다.
Builder.addAction()
에 아이콘과 아이콘이 눌렸을 때 실행할 PendingIntent를 추가합니다.
이 PendingIntent를 사용하면 Media를 제어할 수 있습니다.
MediaStyle.setShowActionsInCompactView()는 접혔을 때 보여질 아이콘의 Index를 설정합니다. 위의 그림처럼 펼쳤을 때는 모든 아이콘이 보이지만 접혔을 때는 3개의 아이콘만 보이도록 만들었습니다.
Inline Reply
Inline Reply는 노티에서 바로 응답을 할 수 있는 기능입니다. MessagingStyle의 코드에서 Inline Reply를 추가해보겠습니다.
다음은 위의 MessagingStyle 예제에 Inline Reply가 적용된 코드입니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_DEFAULT, false,
getString(R.string.app_name), "App notification channel")
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "3 Messages"
val content = "+5 Messages"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT)
val replyLabel = "Enter your reply here"
val remoteInput = RemoteInput.Builder("key_reply")
.setLabel(replyLabel)
.build()
val replyAction = NotificationCompat.Action.Builder(
android.R.drawable.sym_action_chat, "REPLY", pendingIntent) // 1
.addRemoteInput(remoteInput) // 2
.setAllowGeneratedReplies(true) // 3
.build()
val userIcon1 = IconCompat.createWithResource(this, R.drawable.codechacha)
val userIcon2 = IconCompat.createWithResource(this, R.drawable.android)
val userIcon3 = IconCompat.createWithResource(this, R.drawable.person)
val userName1 = "Chacha"
val userName2 = "Android"
val userName3 = "JS"
val timestamp = System.currentTimeMillis()
val user1 = Person.Builder().setIcon(userIcon1).setName(userName1).build()
val user2 = Person.Builder().setIcon(userIcon2).setName(userName2).build()
val user3 = Person.Builder().setIcon(userIcon3).setName(userName3).build()
val style = NotificationCompat.MessagingStyle(user3)
style.addMessage("You can get great deals there", timestamp, user1)
style.addMessage("I know what to get", timestamp, user2)
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title)
builder.setContentText(content)
builder.setStyle(style)
builder.priority = NotificationCompat.PRIORITY_DEFAULT
builder.setAutoCancel(true)
builder.setContentIntent(pendingIntent)
builder.addAction(replyAction) // 4
builder.addAction(android.R.drawable.ic_menu_close_clear_cancel,
"DISMISS", pendingIntent); // 5
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
대부분의 코드는 MessagingStyle 예제와 동일합니다. 다른 부분은 아래 3개입니다.
- Reply Action을 만듭니다.
- 이 기능은 잘 몰라서, 추후 업데이트 하겠습니다.
- 이 기능은 잘 몰라서, 추후 업데이트 하겠습니다.
- Builder에 Replay Action을 추가합니다.
- Dismiss Action을 추가합니다. 버튼의 아이콘과 버튼이 눌렸을 때 실행될 PendingIntent를 인자로 넣습니다.
Builder.addAction()
을 통해서 노티에 Inline Reply 버튼을 만들 수 있습니다.
RemoteInput의 인자가 노티에 어떤 영향을 미치는지 자세히 알지 못해 추후 업데이트 하겠습니다.
Head Up Notifiation
Head up Notification은 전면에 노티가 뜨는 것을 말합니다. 사용자가 Statusbar를 내려서 확인하지 않아도 바로 화면에 뜨기 때문에 쉽게 눈에 띕니다. 전화와 같은 중요한 작업을 알릴 때 이런 노티를 적용하면 좋을 것 같습니다. 중요하지 않은데 사용된다면 사용자가 이 앱이 거슬리다고 생각할 수 있습니다.
아래 화면은 Head up notification입니다. 예제는 기본 노티에 적용하였지만 모든 스타일의 노티에 적용할 수 있습니다.
다음은 Head Up Notifiation이 적용된 코드입니다.
val NOTIFICATION_ID = 1001;
createNotificationChannel(this, NotificationManagerCompat.IMPORTANCE_HIGH, false,
getString(R.string.app_name), "App notification channel") // 1
val channelId = "$packageName-${getString(R.string.app_name)}"
val title = "Android Developer"
val content = "Notifications in Android P"
val intent = Intent(baseContext, NewActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val fullScreenPendingIntent = PendingIntent.getActivity(baseContext, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT) // 2
val builder = NotificationCompat.Builder(this, channelId)
builder.setSmallIcon(R.drawable.ic_codechacha)
builder.setContentTitle(title)
builder.setContentText(content)
builder.priority = NotificationCompat.PRIORITY_HIGH // 3
builder.setAutoCancel(true)
builder.setFullScreenIntent(fullScreenPendingIntent, true) // 4
val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(NOTIFICATION_ID, builder.build())
- 채널을 만들 때 중요도를 IMPORTANCE_HIGH로 설정해야 합니다.
- PendingIntent를 만듭니다. Builder에 fullScreenPendingIntent를 등록하기 때문에 변수 이름을 fullScreenPendingIntent라고 했습니다.
- Builder의 priority를 PRIORITY_HIGH로 설정해야 합니다.
- fullScreenPendingIntent를 등록합니다.
그리고 가장 중요한 것은 앱의 AndroidManifest.xml에 다음 퍼미션을 꼭 추가해야 합니다. Install 퍼미션이기 때문에 설치만 하면 바로 권한을 받습니다.
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
Head up notification은 채널의 중요도를 IMPORTANCE_HIGH, 노티의 중요도를 PRIORITY_HIGH로 설정해야 합니다.
그리고 fullScreenIntent를 추가해야 합니다.
마지막으로 가장 중요한 것은 Q부터 USE_FULL_SCREEN_INTENT
권한이 없으면 Head up 노티를 띄울 수 없기 때문에 꼭 추가해줘야 합니다.
정리
다양한 종류의 노티피케이션에 대해서 알아보았습니다. 위에서 소개한 스타일을 자신의 앱에 적용할 수 없다면 Custom해서 구현해야 합니다.
이 글에서 사용된 코드는 GitHub에 있습니다.
참고
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력