Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android搶紅包代碼解析支持微信與QQ

android搶紅包代碼解析支持微信與QQ

編輯:關於Android編程

最近有一段時間沒寫博客了,一方面是工作比較忙,一方面也著實本人水平有限,沒有太多能與大家分享的東西,也就是在最近公司要做一個搶紅包的功能,老板發話了咋們就開干呗,本人就開始在網上收集資料,經過整理和實踐,總算完美實現了功能,這裡拿出本人一點微薄的成就與大家分享。

首先界面是這樣的

開啟自動搶紅包只需點擊相應的選項即可,下面我們進入正題,實現自動搶紅包的原理,其實是借助android下的一個輔助服務AccessibilityService,這個服務是google公司為許多Android使用者因為各種情況導致他們要以不同的方式與手機交互。這包括了有些用戶由於視力上,身體上,年齡上的問題致使他們不能看完整的屏幕或者使用觸屏,也包括了無法很好接收到語音信息和提示的聽力能力比較弱的用戶。Android提供了Accessibility功能和服務幫助這些用戶更加簡單地操作設備,包括文字轉語音(這個不支持中文),觸覺反饋,手勢操作,軌跡球和手柄操作。

而開發者可以利用這些服務使得程序更好用,我們來看代碼:

 


            
                
            

            
        

以上內容配置在AndroidManifest中,qianghongbao_service_config內容如下

 

 

 



android:description 這個是設置服務的描述,在用戶授權的界面可以看到。

 

android:accessibilityEventTypes 這個是配置要監聽的輔助事件,我們只需要用到typeNotificationStateChanged(通知變化事件)、typeWindowStateChanged(界面變化事件)

android:packageNames 這個是要監聽應用的包名,如果要監聽多個應用,則用","去分隔

android:accessibilityFeedbackType 這個是設置反饋方式,方式有如下幾種

\

android:accessibilityFeedbackType="feedbackGeneric"通用的反饋類型

android:notificationTimeout="100"為事件回調的延遲時間

android:accessibilityFlags="flagDefault"默認標記

android:canRetrieveWindowContent="true"如果不設為true,AccessibilityEvent.getSource()獲取的對象即為空

 

AccessibilityService類主要有以下四個方法:

 

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    //接收事件,如觸發了通知欄變化、界面變化等    
}

@Override
protected boolean onKeyEvent(KeyEvent event) {
    //接收按鍵事件
    return super.onKeyEvent(event);
}

@Override
public void onInterrupt() {
  //服務中斷,如授權關閉或者將服務殺死
}

@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    //連接服務後,一般是在授權成功後會接收到
}

當我們監聽的包名發生通知或界面改變時,就會被onAccessibilityEvent方法捕抓到,我們先看看微信的實現:

 

 

 

@Override
	public void onReceiveJob(AccessibilityEvent event) {
		final int eventType = event.getEventType();
		if (eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {// 通知欄事件
			Parcelable data = event.getParcelableData();
			if (data == null || !(data instanceof Notification)) {
				return;
			}
			// 開啟快速模式,不處理
			if (QiangHongBaoService.isNotificationServiceRunning() && getConfig().isEnableNotificationService()) {
				return;
			}
			List texts = event.getText();
			if (!texts.isEmpty()) {
				String text = String.valueOf(texts.get(0));
				notificationEvent(text, (Notification) data);
			}
		} else if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {// 界面改變事件
			openHongBao(event);
		} else if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) {// 界面內容改變事件
			if (mCurrentWindow != WINDOW_LAUNCHER) { // 不在聊天界面或聊天列表,不處理
				return;
			}
			if (isReceivingHongbao) {
				handleChatListHongBao();
			}
		}
	}

這裡我們做了簡單的封裝,onAccessibilityEvent方法觸發時,便會進入AccessibilityEvent傳入onReceiveJob方法中,在該方法中分別處理了通知欄事件,界面改變事件,界面內容改變事件,我們逐個看。

 

 

通知欄事件中,我們進行了判空,並攔截快速模式,重點是event.getText()獲取通知中的文本,進行判斷:

 

 

/** 所有通知欄事件,都在該方法處理 */
	private void notificationEvent(String ticker, Notification nf) {
		String text = ticker;
		int index = text.indexOf(":");
		if (index != -1) {
			text = text.substring(index + 1);
		}
		text = text.trim();
		if (text.contains("[微信紅包]")) { // 紅包消息
			newHongBaoNotification(nf);
		}
	}

 

 

如果有[微信紅包]字眼,便進入下一步處理

 

 

/** 打開通知欄消息 */
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private void newHongBaoNotification(Notification notification) {
		isReceivingHongbao = true;
		// 以下是精華,將微信的通知欄消息打開
		PendingIntent pendingIntent = notification.contentIntent;
		boolean lock = NotifyHelper.isLockScreen(getContext());

		if (!lock) {// 未鎖,自動點開通知
			NotifyHelper.send(pendingIntent);
		} else {// 鎖屏,顯示通知,微信自帶,不作任何處理
			NotifyHelper.showNotify(getContext(), String.valueOf(notification.tickerText), pendingIntent);
		}

		// 鎖屏 || 模式非自動搶
		if (lock || getConfig().getWechatMode() != Config.WX_MODE_0) {
			// 開啟聲音,震動提示
			NotifyHelper.playEffect(getContext(), getConfig());
		}
	}

獲取到PendingIntent,並判斷當前是否鎖屏,這裡主要用到了

 

 

 

/** 執行PendingIntent事件 */
	public static void send(PendingIntent pendingIntent) {
		try {
			pendingIntent.send();
		} catch (PendingIntent.CanceledException e) {
			e.printStackTrace();
		}
	}

該方法可以執行模擬點擊,如同點擊了紅包通知一般,這樣就會觸發下一個內容改變事件

 

 

 

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private void openHongBao(AccessibilityEvent event) {
		if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI".equals(event.getClassName())) {
			mCurrentWindow = WINDOW_LUCKYMONEY_RECEIVEUI;
			// 點中了紅包,下一步就是去拆紅包
			handleLuckyMoneyReceive();
		} else if ("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(event.getClassName())) {
			mCurrentWindow = WINDOW_LUCKYMONEY_DETAIL;
			// 拆完紅包後看詳細的紀錄界面
			if (getConfig().getWechatAfterGetHongBaoEvent() == Config.WX_AFTER_GET_GOHOME) { // 返回主界面,以便收到下一次的紅包通知
				AccessibilityHelper.performHome(getService());
			}
		} else if ("com.tencent.mm.ui.LauncherUI".equals(event.getClassName())) {
			mCurrentWindow = WINDOW_LAUNCHER;
			// 在聊天界面,去點中紅包
			handleChatListHongBao();
		} else {
			mCurrentWindow = WINDOW_OTHER;
		}
	}

我們根據當前的activity判斷下一步處理

 

 

 

/**
	 * 收到聊天裡的紅包
	 * */
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
	private void handleChatListHongBao() {
		int mode = getConfig().getWechatMode();
		if (mode == Config.WX_MODE_3) { // 只通知模式
			return;
		}

		AccessibilityNodeInfo nodeInfo = getService().getRootInActiveWindow();
		if (nodeInfo == null) {
			return;
		}

		if (mode != Config.WX_MODE_0) {// 非自動搶
			boolean isMember = isMemberChatUi(nodeInfo);
			if (mode == Config.WX_MODE_1 && isMember) {// 過濾群聊
				return;
			} else if (mode == Config.WX_MODE_2 && !isMember) { // 過濾單聊
				return;
			}
		}

		// 下面就是,激動人心的搶紅包代碼
		List list = nodeInfo.findAccessibilityNodeInfosByText("領取紅包");

		if (list != null && list.isEmpty()) {// "領取紅包"關鍵字節點獲取不到
			// 從消息列表查找紅包
			AccessibilityNodeInfo node = AccessibilityHelper.findNodeInfosByText(nodeInfo, HONGBAO_TEXT_KEY);
			if (node != null) {
				isReceivingHongbao = true;
				AccessibilityHelper.performClick(node);
			}
		} else if (list != null) {
			if (isReceivingHongbao) {
				// 最新的紅包領起
				AccessibilityNodeInfo node = list.get(list.size() - 1);
				AccessibilityHelper.performClick(node);
				isReceivingHongbao = false;
			}
		}
	}

同樣根據判斷關鍵文本進入模擬點擊,然後再次觸發界面改變事件

 

 

 

/**
	 * 點擊聊天裡的紅包後,顯示的界面
	 * */
	@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
	private void handleLuckyMoneyReceive() {
		AccessibilityNodeInfo nodeInfo = getService().getRootInActiveWindow();

		AccessibilityNodeInfo targetNode = null;
		int event = getConfig().getWechatAfterOpenHongBaoEvent();
		int wechatVersion = getWechatVersion();
		if (event == Config.WX_AFTER_OPEN_HONGBAO) { // 拆紅包
			if (wechatVersion < USE_ID_MIN_VERSION) {
				targetNode = AccessibilityHelper.findNodeInfosByText(nodeInfo, "拆紅包");
			} else {
				String buttonId = "com.tencent.mm:id/b43";

				if (wechatVersion == 700) {
					buttonId = "com.tencent.mm:id/b2c";
				}

				if (buttonId != null) {
					targetNode = AccessibilityHelper.findNodeInfosById(nodeInfo, buttonId);
				}

				if (targetNode == null) {
					// 分別對應固定金額的紅包 拼手氣紅包
					AccessibilityNodeInfo textNode = AccessibilityHelper.findNodeInfosByTexts(nodeInfo, "發了一個紅包", "給你發了一個紅包", "發了一個紅包,金額隨機");

					if (textNode != null) {
						for (int i = 0; i < textNode.getChildCount(); i++) {
							AccessibilityNodeInfo node = textNode.getChild(i);
							if ("android.widget.Button".equals(node.getClassName())) {
								targetNode = node;
								break;
							}
						}
					}
				}

				if (targetNode == null) { // 通過組件查找
					targetNode = AccessibilityHelper.findNodeInfosByClassName(nodeInfo, "android.widget.Button");
				}
			}
		} else if (event == Config.WX_AFTER_OPEN_SEE) { // 看一看
			if (getWechatVersion() < USE_ID_MIN_VERSION) { // 低版本才有 看大家手氣的功能
				targetNode = AccessibilityHelper.findNodeInfosByText(nodeInfo, "看看大家的手氣");
			}
		} else if (event == Config.WX_AFTER_OPEN_NONE) {// 靜靜地看著
			return;
		}

		if (targetNode != null) {
			final AccessibilityNodeInfo n = targetNode;
			long sDelayTime = getConfig().getWechatOpenDelayTime();
			if (sDelayTime != 0) {
				getHandler().postDelayed(new Runnable() {
					@Override
					public void run() {
						AccessibilityHelper.performClick(n);
					}
				}, sDelayTime);
			} else {
				AccessibilityHelper.performClick(n);
			}
		}
	}

微信中有兩種紅包,一種是固定金額的紅包,還有一種是拼手氣紅包,兩個的關鍵字不同,我們分開判斷。

 

 

QQ的實現也基本相同,我們直接上代碼

 

 

@Override
	public void onReceiveJob(AccessibilityEvent event) {
		openRed(event);
	}

private void openRed(AccessibilityEvent event) {
		this.rootNodeInfo = event.getSource();
		if (rootNodeInfo == null) {
			return;
		}
		mReceiveNode = null;
		checkNodeInfo();
		/* 如果已經接收到紅包並且還沒有戳開 */
		if (mLuckyMoneyReceived && (mReceiveNode != null)) {
			int size = mReceiveNode.size();
			if (size > 0) {
				String id = getHongbaoText(mReceiveNode.get(size - 1));
				long now = System.currentTimeMillis();
				if (this.shouldReturn(id, now - lastFetchedTime))
					return;
				lastFetchedHongbaoId = id;
				lastFetchedTime = now;
				AccessibilityNodeInfo cellNode = mReceiveNode.get(size - 1);
				if (cellNode.getText().toString().equals("口令紅包已拆開")) {
					return;
				}
				cellNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
				if (cellNode.getText().toString().equals(QQ_HONG_BAO_PASSWORD)) {
					AccessibilityNodeInfo rowNode = getService().getRootInActiveWindow();
					if (rowNode == null) {
						return;
					} else {
						recycle(rowNode);
					}
				}
				mLuckyMoneyReceived = false;
			}
		}
	}

@TargetApi(Build.VERSION_CODES.KITKAT)
	public void recycle(AccessibilityNodeInfo info) {
		if (info.getChildCount() == 0) {
			/* 這個if代碼的作用是:匹配“點擊輸入口令的節點,並點擊這個節點” */
			if (info.getText() != null && info.getText().toString().equals(QQ_CLICK_TO_PASTE_PASSWORD)) {
				info.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
			}
			/* 這個if代碼的作用是:匹配文本編輯框後面的發送按鈕,並點擊發送口令 */
			if (info.getClassName().toString().equals("android.widget.Button") && info.getText().toString().equals("發送")) {
				info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
			}
		} else {
			for (int i = 0; i < info.getChildCount(); i++) {
				if (info.getChild(i) != null) {
					recycle(info.getChild(i));
				}
			}
		}
	}

可見這是一個遞歸方法,反復獲取節點並判斷節點文本,模擬點擊紅包按鈕。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved