編輯:關於Android編程
寫在前面:
最近接到老大的一個需求,要求在手機端攔截微信的通知(Notification),從而獲得聯系人和內容。之後將聯系人和內容發送到我們的硬件產品上,展示出來之後,再將我們想回復內容傳給微信,並且發送給相應聯系人。
老大還提示我需要用AccessibilityService去實現它,當然在此之前我並不知道AccessibilityService是什麼鬼,不過沒關系, just do IT !
AccessibilityService官方文檔(需翻牆)
上面這個鏈接是AccessibilityService的官方文檔,可以翻牆點進去了解下,我再給大家總結一下:
AccessibilityService是Android系統框架提供給安裝在設備上應用的一個可選的導航反饋特性。AccessibilityService 可以替代應用與用戶交流反饋,比如將文本轉化為語音提示,或是用戶的手指懸停在屏幕上一個較重要的區域時的觸摸反饋等。
如果感覺上面的描述比較抽象,沒關系,也許你見過下面這張圖:
打開你手機的設置–輔助功能中,有很多APP提供的服務,他們都是基於AccessibilityService編寫的,AccessibilityService可以偵聽你的點擊,長按,手勢,通知欄的變化等。並且你可以通過很多種方式找到窗體中的EditText,Button等組件,去填充他們,去點擊他們來幫你實現自動化的功能。
像360助手的自動安裝功能,它就是偵聽著系統安裝的APP,然後找到“安裝”按鈕,實現了自動點擊。微信自動搶紅包功能,實現方式都是如此。
首先我們在res文件夾下創建xml文件夾,然後創建一個名為auto_reply_service_config的文件,一會我們會在清單文件中引用它。
代碼:
這個文件表示我們對AccessibilityService服務未來偵聽的行為做了一些配置,比如 typeNotificationStateChanged 和 typeWindowStateChanged 表示我們需要偵聽通知欄的狀態變化和窗體狀態改變。
android:packageNames=”com.tencent.mm” 這是微信的包名,表示我們只關心微信這一個應用。
代碼不打算帶著大家一行一行看了,如果有不明白的,去看看文檔,或者下面回復我,我給大家解答~
下面貼出AccessibilityService類的全部代碼,注釋還算詳盡,如有疑問,下方回復。
package com.ileja.autoreply;
import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import java.io.IOException;
import java.util.List;
public class AutoReplyService extends AccessibilityService {
private final static String MM_PNAME = "com.tencent.mm";
boolean hasAction = false;
boolean locked = false;
boolean background = false;
private String name;
private String scontent;
AccessibilityNodeInfo itemNodeinfo;
private KeyguardManager.KeyguardLock kl;
private Handler handler = new Handler();
/**
* 必須重寫的方法,響應各種事件。
* @param event
*/
@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
int eventType = event.getEventType();
android.util.Log.d("maptrix", "get event = " + eventType);
switch (eventType) {
case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:// 通知欄事件
android.util.Log.d("maptrix", "get notification event");
List texts = event.getText();
if (!texts.isEmpty()) {
for (CharSequence text : texts) {
String content = text.toString();
if (!TextUtils.isEmpty(content)) {
if (isScreenLocked()) {
locked = true;
wakeAndUnlock();
android.util.Log.d("maptrix", "the screen is locked");
if (isAppForeground(MM_PNAME)) {
background = false;
android.util.Log.d("maptrix", "is mm in foreground");
sendNotifacationReply(event);
handler.postDelayed(new Runnable() {
@Override
public void run() {
sendNotifacationReply(event);
if (fill()) {
send();
}
}
}, 1000);
} else {
background = true;
android.util.Log.d("maptrix", "is mm in background");
sendNotifacationReply(event);
}
} else {
locked = false;
android.util.Log.d("maptrix", "the screen is unlocked");
// 監聽到微信紅包的notification,打開通知
if (isAppForeground(MM_PNAME)) {
background = false;
android.util.Log.d("maptrix", "is mm in foreground");
sendNotifacationReply(event);
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (fill()) {
send();
}
}
}, 1000);
} else {
background = true;
android.util.Log.d("maptrix", "is mm in background");
sendNotifacationReply(event);
}
}
}
}
}
break;
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
android.util.Log.d("maptrix", "get type window down event");
if (!hasAction) break;
itemNodeinfo = null;
String className = event.getClassName().toString();
if (className.equals("com.tencent.mm.ui.LauncherUI")) {
if (fill()) {
send();
}else {
if(itemNodeinfo != null){
itemNodeinfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (fill()) {
send();
}
back2Home();
release();
hasAction = false;
}
}, 1000);
break;
}
}
}
//bring2Front();
back2Home();
release();
hasAction = false;
break;
}
}
/**
* 尋找窗體中的“發送”按鈕,並且點擊。
*/
@SuppressLint("NewApi")
private void send() {
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
if (nodeInfo != null) {
List list = nodeInfo
.findAccessibilityNodeInfosByText("發送");
if (list != null && list.size() > 0) {
for (AccessibilityNodeInfo n : list) {
n.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
} else {
List liste = nodeInfo
.findAccessibilityNodeInfosByText("Send");
if (liste != null && liste.size() > 0) {
for (AccessibilityNodeInfo n : liste) {
n.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
pressBackButton();
}
}
/**
* 模擬back按鍵
*/
private void pressBackButton(){
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec("input keyevent " + KeyEvent.KEYCODE_BACK);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param event
*/
private void sendNotifacationReply(AccessibilityEvent event) {
hasAction = true;
if (event.getParcelableData() != null
&& event.getParcelableData() instanceof Notification) {
Notification notification = (Notification) event
.getParcelableData();
String content = notification.tickerText.toString();
String[] cc = content.split(":");
name = cc[0].trim();
scontent = cc[1].trim();
android.util.Log.i("maptrix", "sender name =" + name);
android.util.Log.i("maptrix", "sender content =" + scontent);
PendingIntent pendingIntent = notification.contentIntent;
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}
}
}
@SuppressLint("NewApi")
private boolean fill() {
AccessibilityNodeInfo rootNode = getRootInActiveWindow();
if (rootNode != null) {
return findEditText(rootNode, "正在忙,稍後回復你");
}
return false;
}
private boolean findEditText(AccessibilityNodeInfo rootNode, String content) {
int count = rootNode.getChildCount();
android.util.Log.d("maptrix", "root class=" + rootNode.getClassName() + ","+ rootNode.getText()+","+count);
for (int i = 0; i < count; i++) {
AccessibilityNodeInfo nodeInfo = rootNode.getChild(i);
if (nodeInfo == null) {
android.util.Log.d("maptrix", "nodeinfo = null");
continue;
}
android.util.Log.d("maptrix", "class=" + nodeInfo.getClassName());
android.util.Log.e("maptrix", "ds=" + nodeInfo.getContentDescription());
if(nodeInfo.getContentDescription() != null){
int nindex = nodeInfo.getContentDescription().toString().indexOf(name);
int cindex = nodeInfo.getContentDescription().toString().indexOf(scontent);
android.util.Log.e("maptrix", "nindex=" + nindex + " cindex=" +cindex);
if(nindex != -1){
itemNodeinfo = nodeInfo;
android.util.Log.i("maptrix", "find node info");
}
}
if ("android.widget.EditText".equals(nodeInfo.getClassName())) {
android.util.Log.i("maptrix", "==================");
Bundle arguments = new Bundle();
arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
true);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
arguments);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
ClipData clip = ClipData.newPlainText("label", content);
ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
clipboardManager.setPrimaryClip(clip);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
return true;
}
if (findEditText(nodeInfo, content)) {
return true;
}
}
return false;
}
@Override
public void onInterrupt() {
}
/**
* 判斷指定的應用是否在前台運行
*
* @param packageName
* @return
*/
private boolean isAppForeground(String packageName) {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
String currentPackageName = cn.getPackageName();
if (!TextUtils.isEmpty(currentPackageName) && currentPackageName.equals(packageName)) {
return true;
}
return false;
}
/**
* 將當前應用運行到前台
*/
private void bring2Front() {
ActivityManager activtyManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List runningTaskInfos = activtyManager.getRunningTasks(3);
for (ActivityManager.RunningTaskInfo runningTaskInfo : runningTaskInfos) {
if (this.getPackageName().equals(runningTaskInfo.topActivity.getPackageName())) {
activtyManager.moveTaskToFront(runningTaskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
return;
}
}
}
/**
* 回到系統桌面
*/
private void back2Home() {
Intent home = new Intent(Intent.ACTION_MAIN);
home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
home.addCategory(Intent.CATEGORY_HOME);
startActivity(home);
}
/**
* 系統是否在鎖屏狀態
*
* @return
*/
private boolean isScreenLocked() {
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
return keyguardManager.inKeyguardRestrictedInputMode();
}
private void wakeAndUnlock() {
//獲取電源管理器對象
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
//獲取PowerManager.WakeLock對象,後面的參數|表示同時傳入兩個值,最後的是調試用的Tag
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
//點亮屏幕
wl.acquire(1000);
//得到鍵盤鎖管理器對象
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
kl = km.newKeyguardLock("unLock");
//解鎖
kl.disableKeyguard();
}
private void release() {
if (locked && kl != null) {
android.util.Log.d("maptrix", "release the lock");
//得到鍵盤鎖管理器對象
kl.reenableKeyguard();
locked = false;
}
}
}
接著配置清單文件,權限和service的配置比較重要。
為了使用某些必要的API,最低API level應該是18
運行程序,打開服務,看看效果如何把~
接著用其他手機試著發送給我幾條微信
可以看到,自動回復功能就實現了。
寫在後面:
代碼沒有給大家詳細講解,不過看注釋應該可以看懂個大概。當微信程序切換到後台,或者鎖屏(無鎖屏密碼)時,只要有通知出現,都可以實現自動回復。
關於AccessibilityService可以監控的行為非常多,所以我覺得可以實現各種各樣炫酷的功能,不過我並不建議你打開某些流氓軟件的AccessibilityService服務,因為很有可能造成一些安全問題,所以,自己動手寫就安全多了嘛。
C中比較難的這一塊,大概就是指針了,所以大家還是多翻閱一下資料,當然,如果只是想了解一下,看本篇也就夠了,不過我也盡量陳述的很詳細一.指針 要說指針,其實通俗易懂的話來
一、UI 概述Android應用程序的用戶界面是一切,用戶可以看到並與之交互。UI 是用戶能看見並可交互的組件。– 系統 UI– 自定義 UI&n
該篇文章是一個ListFragment的一個實例,通過了解該實例,更能了解比較常用的ListFragment的用法,以及各Fragment之間的數據傳遞。 實現效果圖:
有很多人喜歡在手機上下東西,但是有手機在休眠的時候WIFI會斷網,這樣想下載的東西也下載不了了,那麼怎麼避免這種情況出現呢,下面小編就給大家介紹下防止手機休