NotificationListenerService
NotificationListenerService는 Notification에 대한 정보를 받을 수 있는 객체입니다. 안드로이드 SDK는 NotificationManager에 직접 listener를 등록하는 코드는 제공하고 있지 않습니다. 정보를 받고 싶다면 NotificationListenerService를 상속받은 새로운 서비스를 구현하여야 합니다.
서비스 구현
아래처럼 NotificationListenerService를 상속받고 다음 두개의 메소드를 구현해야 합니다.
- onNotificationPosted: 새로운 노티피케이션이 추가될 때 호출됩니다.
- onNotificationRemoved: 노티피케이션이 제거될 때 호출됩니다.
아래 코드처럼 두개의 메소드가 호출될 때 로그가 출력되도록 하였습니다.
public class MyNotificationListener extends NotificationListenerService {
public final static String TAG = "MyNotificationListener";
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
super.onNotificationRemoved(sbn);
Log.d(TAG, "onNotificationRemoved ~ " +
" packageName: " + sbn.getPackageName() +
" id: " + sbn.getId());
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
Notification notification = sbn.getNotification();
Bundle extras = sbn.getNotification().extras;
String title = extras.getString(Notification.EXTRA_TITLE);
CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
CharSequence subText = extras.getCharSequence(Notification.EXTRA_SUB_TEXT);
Icon smallIcon = notification.getSmallIcon();
Icon largeIcon = notification.getLargeIcon();
Log.d(TAG, "onNotificationPosted ~ " +
" packageName: " + sbn.getPackageName() +
" id: " + sbn.getId() +
" postTime: " + sbn.getPostTime() +
" title: " + title +
" text : " + text +
" subText: " + subText);
}
}
AndroidManifest 설정
AndroidManifest.xml
에는 위에서 구현한 서비스를 등록해야 합니다. 안드로이드 프레임워크만 접근할 수 있도록 서비스의 퍼미션에 BIND_NOTIFICATION_LISTENER_SERVICE
를 설정하고,
아래와 같은 인텐트필터를 설정해야 합니다.
<service android:name=".MyNotificationListener"
android:label="My Notification Listener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
퍼미션을 설정하는 이유는... 저 퍼미션은 SystemServer(system)만 갖을 수 있습니다. 따라서 3rd party가 저 서비스를 실행할 수 없고 오직 NotificationManager에서만 접근할 수 있습니다.
인텐트필터를 설정하는 이유는 저 action으로 NotificationManager가 단말에 등록된 서비스를 찾는데 사용하기 때문입니다. 만약 인텐트필터를 저렇게 등록하지 않으면 노티를 알림받지 못합니다.
사용자로부터 권한 받기
지금까지 서비스를 구현하였고 AndroidManifest에 등록하였습니다. 마지막으로 노티피케이션에 대한 정보를 받으려면 사용자 동의를 받아야 합니다.
간혹 3rd party app들이 노티피케이션에 id와 pw를 보여주기도 합니다. 이처럼 노티피케이션은 개인정보와 관련된 부분이기 때문에 노티피케이션 정보를 받으려면 사용자로부터 권한을 부여받아야 합니다.
구현방법은... 먼저 권한이 있는지 체크하고, 권한을 받는 UI를 실행하여 사용자가 내 앱에 권한을 주도록 해야 합니다.
아래 코드는 현재 이 앱이 권한을 갖고 있는지 체크하는 코드입니다.
private boolean permissionGrantred() {
Set<String> sets = NotificationManagerCompat.getEnabledListenerPackages(this);
if (sets != null && sets.contains(getPackageName())) {
return true;
} else {
return false;
}
}
getEnabledListenerPackages
는 내부적으로 Settings에 저장된 string을 가져옵니다.
그 string은 권한을 갖고 있는 packageName을 나열한 것이고, Set객체로 return됩니다. 우리는 리턴받은 Set에서 내 앱의 packageName이 있는지만 체크하면 됩니다.
구현한 앱 실행해보기
앱을 실행해보면 처음에는 권한이 없기 때문에 권한을 받을 수 있는 Activity가 실행됩니다.
UI에서 내 앱에 권한을 주면 NotificationManager는 내가 구현한 서비스를 실행하고, 노티피케이션에 대한 정보를 서비스로 전달해 줍니다.
노티피케이션이 등록되거나 삭제될 때 아래처럼 로그가 출력됩니다.
11-10 14:23:51.579 3100-3100/com.codechacha.notificationlistener D/MyNotificationListener: onNotificationRemoved ~ packageName: com.google.android.setupwizard id: 5
11-10 14:23:51.692 3100-3100/com.codechacha.notificationlistener D/MyNotificationListener: onNotificationPosted ~ packageName: android id: 40 postTime: 1541827431588 title: Android Setup is using battery text : Tap for details on battery and data usage subText: null
11-10 14:23:51.703 3100-3100/com.codechacha.notificationlistener D/MyNotificationListener: onNotificationPosted ~ packageName: com.google.android.setupwizard id: 6 postTime: 1541827431588 title: Preparing for setup… text : subText: null
정리
예제로 NotificationListenerService를 구현한 앱을 만들어 보았습니다. 두개의 메소드만 구현해보았는데요, NotificationListenerService에는 더 많은 정보를 callback 받는 함수들이 정의되어 있습니다. 필요한 내용이 있으면 추가로 구현해서 사용하시면 됩니다.
메소드에 대한 자세한 설명은 Android developers에서 확인할 수 있습니다.
참고
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 명령어로 로그 출력