Bài viết này triển khai cùng kỹ thuật với bài trước: sử dụng NotificationListenerService để theo dõi, ghi log tất cả notification xuất hiện trên thiết bị. Phù hợp cho app backup tin nhắn, tự động điền OTP, hoặc giám sát thông báo.
Triển khai nhanh (tóm tắt từ bài chi tiết)
1. Interface Callback (MyListener.java)
package app.tutorialspoint.com.notifyme;
public interface MyListener {
void setValue(String packageName);
}
2. Service lắng nghe (NotificationService.java)
package app.tutorialspoint.com.notifyme;
import android.content.Context;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
public class NotificationService extends NotificationListenerService {
private static final String TAG = "NotifListener";
private static MyListener listener;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
String pkg = sbn.getPackageName();
String text = sbn.getNotification().tickerText != null ? sbn.getNotification().tickerText.toString() : "";
Log.d(TAG, "Posted: " + pkg + " - " + text);
if (listener != null) listener.setValue("POST: " + pkg + " - " + text);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
String pkg = sbn.getPackageName();
Log.d(TAG, "Removed: " + pkg);
if (listener != null) listener.setValue("REMOVE: " + pkg);
}
public static void setListener(MyListener l) { listener = l; }
}
3. MainActivity implement Listener
package app.tutorialspoint.com.notifyme;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Intent;
import android.os.Bundle;
import androidx.core.app.NotificationCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements MyListener {
private TextView logView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NotificationService.setListener(this);
logView = findViewById(R.id.textView);
findViewById(R.id.btnCreateNotification).setOnClickListener(v -> createTestNotif());
}
private void createTestNotif() {
NotificationManager m = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
NotificationCompat.Builder b = new NotificationCompat.Builder(this, "default")
.setContentTitle("Test")
.setContentText("Listener demo")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setAutoCancel(true);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
m.createNotificationChannel(new NotificationChannel("10001", "Test", NotificationManager.IMPORTANCE_HIGH));
b.setChannelId("10001");
}
m.notify((int)System.currentTimeMillis(), b.build());
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_settings) {
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void setValue(String s) {
runOnUiThread(() -> logView.append("\n" + s));
}
}
4. Manifest (bắt buộc permission & intent-filter)
<service
android:name=".NotificationService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
Mở rộng: Lấy nội dung chi tiết notification (Android 7+)
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Notification notif = sbn.getNotification();
Bundle extras = notif.extras;
// Lấy tiêu đề, nội dung văn bản
CharSequence title = extras.getCharSequence(Notification.EXTRA_TITLE);
CharSequence text = extras.getCharSequence(Notification.EXTRA_TEXT);
CharSequence bigText = extras.getCharSequence(Notification.EXTRA_BIG_TEXT);
// Lấy ảnh lớn (BigPictureStyle)
Bitmap picture = (Bitmap) extras.getParcelable(Notification.EXTRA_LARGE_ICON);
// Lấy RemoteInput (nếu có quick reply)
// Xử lý actions, categories, v.v.
}
Các trường EXTRAS phổ biến
| Key (Notification.EXTRA_*) | Kiểu | Mô tả |
|---|---|---|
| TITLE | CharSequence | Tiêu đề notification |
| TEXT | CharSequence | Nội dung ngắn |
| BIG_TEXT | CharSequence | Nội dung mở rộng (BigTextStyle) |
| SUB_TEXT | CharSequence | Phụ đề |
| LARGE_ICON | Bitmap | Large icon bitmap |
| PICTURE | Bitmap | BigPictureStyle ảnh |
| MESSAGES | Parcelable[] | MessagingStyle tin nhắn |
| PEOPLE | String[] | Danh sách người liên quan |
Lưu ý triển khai thực tế
- Service chạy nền, cần xử lý tốt vòng đời tránh bị kill.
- Đăng ký listener trong
Application.onCreate()hoặcMainActivitynhưng phải đảm bảo service đã bind. - Xử lý null safety cho
extrasvà các trường bên trong. - Tuân thủ chính sách Google Play về quyền riêng tư khi xin quyền Notification Access.