1. 程式人生 > >讓你監控Android手機的通知欄

讓你監控Android手機的通知欄

2016.6.12更新

最最主要的原始碼其實就只有下面我提到的這些了,最核心的功能都在上面了,以及可能碰到的坑。

程式碼已經開源到github,地址:

原文:

有段時間沒寫博了,想著還是要寫點東西,免得生疏了,正好前段時間做了一個抓取通知欄的功能,期間也走了一些彎路,

通過網上查資料,看Android原始碼,最終總算解決了監控通知欄的功能。實現的效果如下:


不過在使用通知欄監控之前,首先要進行設定,要讓手機允許你監控通知欄,畢竟這是很隱私的操作,不然如果沒有這個允許動作,那麼Android手機也太危險了,

包括聊天資訊,短訊息都有可能被監控。設定如下:


在輔助功能這裡,開啟NotificationMonitor服務。

接下來來講一下實現方法:

首先寫一個類繼承AccessibilityService,AccessibilityService輔助服務,可以看官方介紹:

http://developer.android.com/reference/android/accessibilityservice/AccessibilityService.html

這個服務執行在後臺,當有定義的的AccessibilityEvent被觸發時,則會進行相應的回撥函式,通知欄需要使用到的事件是:

AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED
也就是通知欄狀態變化事件。

程式碼如下:

public class NeNotificationService extends AccessibilityService {

	
	@Override
	public void onAccessibilityEvent(AccessibilityEvent event) {
		
		//判斷輔助服務觸發的事件是否是通知欄改變事件
	    if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) 
	    {
	    	
	    		//獲取Parcelable物件
	    		Parcelable data = event.getParcelableData();
	    		
	    		//判斷是否是Notification物件
				if (data instanceof Notification) {
				
					Notification notification = (Notification) data;
					
					Intent intent = new Intent();
					intent.putExtra("NotifyData", notification);
					intent.putExtra("packageName", event.getPackageName());
					
					intent.setAction(".NeNotificationService");
				
					//進行處理解析通知欄內容的函式
					MainActivity.notifyReceive((String)event.getPackageName(), notification);
				
	    }else
	    {
	    	
	    }
	    
	}
	
	
	/**
	*Service被啟動的時候會呼叫這個API
	*/
	@Override
	protected void onServiceConnected() {

		//設定關心的事件型別
	    AccessibilityServiceInfo info = new AccessibilityServiceInfo();
	    info.eventTypes = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED |
				AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | 
				AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
	    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
	    info.notificationTimeout = 100;//兩個相同事件的超時時間間隔
	    setServiceInfo(info);
	}
}

這個服務類在使用之前肯定是要先被啟動的,啟動可以放在MainActivity的某個按鈕點選時:
//在MainActivity.onCreate裡初始化
Intent upservice = new Intent(this, NeNotificationService.class); 

//按鈕按下時更新或啟動服務
accesscStartNo.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				updateServiceStatus(true);
			}
		});

//這裡防止多次啟動服務,所以先判斷服務是否在執行中
private void updateServiceStatus(boolean start)
	{
		boolean bRunning = util.isServiceRunning(this, "com.nis.bcreceiver.NeNotificationService");
		
		//沒有Running則啟動
		if (start && !bRunning) {
			this.startService(upservice);
		} else if(!start && bRunning) {
			this.stopService(upservice);
		}		
		
	}

//另外需要在AndroidManifest.xml檔案中配置
<service android:name="com.nis.bcreceiver.NeNotificationService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:label="NotificationMonitor"
        android:enabled="true"
        android:exported="false">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
         <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibilityservice" />
    </service>


這樣我們基本完成了Notification的監控和抓取,至於如果你想解析出什麼資料,就可以處理這個
notification變數。通過多個程式的測試,發現notification裡比較有用的就是contentView,這是一個直接顯示在
</pre></p><p>介面上的View,我們只要解析它就可以獲取展示的View的標題及文字內容,處理程式碼如下:</p><p><pre name="code" class="java">  private void AnalyzeView(RemoteViews remoteView, String packName) {

		try {
			//把RemoteView apply後變成當前可以處理的View
			View v1 = remoteView.apply(this, rootLayout);
		
			//然後就是列舉處理這個View的內容
			EnumGroupViews(v1);
			
			//展示出來
			rootLayout.addView(v1);
			
			
		} catch (Exception e) {
			AppLog.e("addToUi excep",e);
		}
		
	}

	private void EnumGroupViews(View v1)
	{
		if(v1 instanceof ViewGroup)
		{
			
			ViewGroup lav = (ViewGroup)v1;
			int lcCnt = lav.getChildCount();
			for(int i = 0; i < lcCnt; i++)
			{
				View c1 = lav.getChildAt(i);
				if(c1 instanceof ViewGroup)
					EnumGroupViews(c1);//遞迴處理GroupView
				else if(c1 instanceof TextView)
				{
					//TestView則解析裡面文字內容
					TextView txt = (TextView)c1;
					String str = txt.getText().toString().trim();
					if(str.length() > 0)
					{
						//這裡列印文字內容
					}
					
					AppLog.i( "TextView id:"+ txt.getId() + ".text:" + str);
				}else
				{
					AppLog.w("2 other layout:" + c1.toString());
					
				}
			}
		}
		else {
			AppLog.w("1 other layout:" + v1.toString());
		}
	}

通過這個解析,我們基本上完成了從監控、抓取、解析內容的過程。網上也還有其它一些解析處理的方法,大家也可以參考,以上解析remoteView是本人

在檢視其相應原始碼情況下自己琢磨出來的,而且測試了十幾款有推送訊息的應用,都可以正常解析其所有內容。