0. 알림을 받아오는 App 만들기
- 알림을 보내는 어플과 알림을 받아 저장하는 어플 분리
- 실제 앱 개발 시 분리해서 작성될 거니까...분리해서 실습해봤다..!
1. NotificationListener
- Notification Listener Service를 상속받아 Notification Listener 만들기
- 만들어서 Toast로 잘 받아와지는 지 확인
class MyNotificationListener : NotificationListenerService(){
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
val notification = sbn?.notification
val title = notification?.extras?.getString(Notification.EXTRA_TITLE)
val content = notification?.extras?.getString(Notification.EXTRA_TEXT)
val text = title + " " + content
val duration = Toast.LENGTH_LONG
val toast = Toast.makeText(applicationContext, text, duration)
toast.show()
}
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
super.onNotificationRemoved(sbn)
}
}
2. Listener Service 등록
- 1에서 만든 Listener Service를 AndroidManifest.xml에 등록
- <application> 안에 넣어야한다.
<!--<service android:name=".내가만든NotificationListenerService클래스이름"-->
<service android:name=".MyNotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
3. 권한 받기
- 사용자에게 Notification 권한 받기
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
if(!permissionGranted()){
val intent = Intent(
"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")
startActivity(intent)
}
}
private fun permissionGranted() : Boolean {
val sets = NotificationManagerCompat.getEnabledListenerPackages(this)
return sets != null && sets.contains(packageName)
}
}
4. Room - 라이브러리
- 불러오는 게 잘 되길래 Room으로 SQLite 저장해서 불러오는 것까지 해보려고 함
- SQLite를 직접 쓰려고 했는데..? 아래 문구를 읽고, Room 라이브러리를 써보기로 함...
Room 지속성 라이브러리는 SQLite를 완벽히 활용하면서 원활한 데이터베이스 액세스가 가능하도록 SQLite에 추상화 계층을 제공
plugins{
...
id 'kotlin-kapt'
}
dependencies {
...
//Room for SQLite
def roomVersion = "2.4.1"
implementation("androidx.room:room-runtime:$roomVersion")
annotationProcessor("androidx.room:room-compiler:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")
}
5. Room - Entity, Dao, Database
- 임시 테스트이기 때문에... 한 개의 파일에 전부 작성
- 실제로 개발할 때는 각 파일 다 나눠서 작성할 것...!!!
- Room Database는 리소스를 많이 소비하기 때문에, 싱글톤 디자인 패턴을 따르라는 Tip이 있다.
- Entity
@Entity
data class NotiData(
@PrimaryKey(autoGenerate = true) val nid:Int?,
@ColumnInfo(name = "title") val title : String?,
@ColumnInfo(name = "content") val content : String?
){
constructor(title: String?, content: String?): this( null, title, content)
}
- Dao
@Dao
interface NotiDataDao{
@Query("SELECT * FROM NotiData")
fun getAll() : List<NotiData>
@Insert
fun insert(noti : NotiData)
}
- Database
@Database(entities = [NotiData::class], version = 1)
abstract class NotiDatabase : RoomDatabase(){
abstract fun notiDataDao() : NotiDataDao
//Singletone
companion object{
private var notiInstance: NotiDatabase? = null
@Synchronized
fun getNotiInstance(context: Context): NotiDatabase?{
if(notiInstance == null){
synchronized(NotiDatabase::class){
notiInstance = Room.databaseBuilder(context.applicationContext, NotiDatabase::class.java, "noti.db")
.allowMainThreadQueries()
.build()
}
}
return notiInstance
}
fun destroyNotiInstance(){
notiInstance = null
}
}
}
6. Room - 사용
- Notification Listener가 새 알림을 잡으면 notiDB에 저장
class MyNotificationListener : NotificationListenerService(){
private lateinit var notiDb : NotiDatabase
override fun onNotificationPosted(sbn: StatusBarNotification?) {
...
notiDb = NotiDatabase.getNotiInstance(applicationContext)!!
notiDb.notiDataDao().insert(NotiData(title, content))
}
...
}
- Notification App에서 불러오기 버튼을 누르면 notiDB에 저장된 값을 출력
class MainActivity : AppCompatActivity() {
private lateinit var notiDb : NotiDatabase
override fun onCreate(savedInstanceState: Bundle?) {
...
notiDb = NotiDatabase.getNotiInstance(applicationContext)!!
btn_refresh.setOnClickListener{
all_noti.setText(notiDb.notiDataDao().getAll().toString())
}
}
...
}
7. 전체 코드
- MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var notiDb : NotiDatabase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if(!permissionGranted()){
val intent = Intent(
"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS")
startActivity(intent)
}
notiDb = NotiDatabase.getNotiInstance(applicationContext)!!
btn_refresh.setOnClickListener{
all_noti.setText(notiDb.notiDataDao().getAll().toString())
}
}
private fun permissionGranted() : Boolean {
val sets = NotificationManagerCompat.getEnabledListenerPackages(this)
return sets != null && sets.contains(packageName)
}
}
- MyNotificationListener.kt
class MyNotificationListener : NotificationListenerService(){
private lateinit var notiDb : NotiDatabase
override fun onNotificationPosted(sbn: StatusBarNotification?) {
super.onNotificationPosted(sbn)
val notification = sbn?.notification
val title = notification?.extras?.getString(Notification.EXTRA_TITLE)
val content = notification?.extras?.getString(Notification.EXTRA_TEXT)
val text = title + " " + content
val duration = Toast.LENGTH_LONG
val toast = Toast.makeText(applicationContext, text, duration)
toast.show()
notiDb = NotiDatabase.getNotiInstance(applicationContext)!!
notiDb.notiDataDao().insert(NotiData(title, content))
}
override fun onNotificationRemoved(sbn: StatusBarNotification?) {
super.onNotificationRemoved(sbn)
}
}
- NotiData.kt
//NotiEntity.kt, NotiDao.kt, NotiDatabase.kt 등 파일 나눠서 쓰기...!
@Entity
data class NotiData(
@PrimaryKey(autoGenerate = true) val nid:Int?,
@ColumnInfo(name = "title") val title : String?,
@ColumnInfo(name = "content") val content : String?
){
constructor(title: String?, content: String?): this( null, title, content)
}
@Dao
interface NotiDataDao{
@Query("SELECT * FROM NotiData")
fun getAll() : List<NotiData>
@Insert
fun insert(noti : NotiData)
}
@Database(entities = [NotiData::class], version = 1)
abstract class NotiDatabase : RoomDatabase(){
abstract fun notiDataDao() : NotiDataDao
companion object{
private var notiInstance: NotiDatabase? = null
@Synchronized
fun getNotiInstance(context: Context): NotiDatabase?{
if(notiInstance == null){
synchronized(NotiDatabase::class){
notiInstance = Room.databaseBuilder(context.applicationContext, NotiDatabase::class.java, "noti.db")
.allowMainThreadQueries() //main에서 써주려고 추가
.build()
}
}
return notiInstance
}
fun destroyNotiInstance(){
notiInstance = null
}
}
}
8. 결과
9. 참조
- Android Developers - NotificationListenerService
- [Android] NotificationListenerService(카카오톡 대화 반응하기) - Notification 텍스트 받아오기
- NotificationListenerService - 사용자 권한받기
- 에러(Cannot access database on the main thread since it...)
- Android Kotlin | Room DB의 사용법과 예제 - Room DB 참고
- [Android][Kotlin] Room으로 DB 저장하기 - Room DB 참고
- Android Developers - 로컬 데이터베이스에 데이터 저장
'개발 > Android' 카테고리의 다른 글
[Kotlin] 안드로이드 설치된 어플 목록 가져오기(+Notification) with Package Manager (0) | 2022.03.10 |
---|---|
[Kotlin] 실제 휴대폰 Push 알림(Notification) 확인하기 (0) | 2022.03.10 |
[Kotlin] Android Push 알림(Notification) 바로 답장하기 with Remote Input (0) | 2022.03.02 |
[Kotlin] Android Push 알림(Notification) 생성 with NotificationCompat API (0) | 2022.02.28 |
댓글