安卓輔助功能 滴滴自動刷單
阿新 • • 發佈:2019-02-07
由於一個親戚上班比較遠,開車上班拉個順風車也可以省個油錢是吧,但每天要花費大量的時間去搶單子,於是我想著做一個能自動刷單的輔助功能軟體;
首先說一下思路,就是在搶單的Activity1通過adb input命令模擬下拉重新整理的操作,然後監聽頁面資訊的,但出現時間順路程度在開始設定的時間內就點選當前條目,然後進去Activity2在點選確認同行按鈕,彈出Dialog再點選確定同行;由於滴滴出行每次更新的空間id會變化,通話id獲得空間會取不到,分享一下思路及程式碼,我這裡自測沒問題,並且還搶單成功過。
我這裡還做了一個懸浮穿可以看到每次重新整理的搶單資訊可以拖拽移動
下面介紹一下AccessibityService類的實現:
初始化配置資訊
/** * 初始化配置資訊 */ @Override protected void onServiceConnected() { super.onServiceConnected(); Log.e(TAG, "onServiceConnected"); myApp = MyApp.app; //例項化工具類 utils = new AccessibityServiceUtils(this); //獲得手機所有應用的包名 這裡監聽所有的包名,通過這種方法獲得當前應用的包名 List<PackageInfo> allAppNames = PackUtils.getAllAppNames(this); String[] strings = new String[allAppNames.size() + 1]; for (int i = 0; i < allAppNames.size(); i++) { strings[i] = allAppNames.get(i).packageName; } strings[allAppNames.size()] = launcher; AccessibilityServiceInfo info = new AccessibilityServiceInfo(); info.flags= AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS; info.eventTypes = TYPE_WINDOW_CONTENT_CHANGED | TYPE_WINDOW_STATE_CHANGED | TYPE_VIEW_SCROLLED; info.packageNames = strings; info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN; info.notificationTimeout = 100; this.setServiceInfo(info); }
對事件的處理:只有EventType為32的時候getClassName返回的才是當前的activity的類名,並且32的事件總是第一個,所以我在這裡可以率先獲得當前的Activity名字
@TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { //是否搶單成功 if (utils.isSuccess()) { return; } //只處理滴滴出行的事件 String s = accessibilityEvent.getPackageName().toString(); if (!s.equals(utils.getPack())) { return; } AccessibilityNodeInfo rootInActiveWindow = getRootInActiveWindow(); int eventType = accessibilityEvent.getEventType(); Log.e("event", " " + eventType); //開啟自動重新整理 if (myApp.isRefresh()) { utils.startAutoFresh(); } switch (eventType) { case 4096: if (utils.getCurrentAcivity().equals(utils.getActivity1()) && !utils.isStart()) { //由於一次下拉重新整理會有兩次4096事件,這裡消費一件,只做一次處理 if (utils.isRefresh()) { utils.setRefresh(false); } else { utils.setRefresh(true); utils.onRefresh(rootInActiveWindow); } } break; case 2048: if (utils.getCurrentAcivity().equals(utils.getActivity1()) && utils.isStart()) { utils.onRefresh(rootInActiveWindow); } if (utils.isToActivity02()) { if (utils.getCurrentAcivity().equals(utils.getActivity2()) || utils.getCurrentAcivity().equals(utils.getDialog())) { utils.qiangdan(rootInActiveWindow); } } break; case 32: utils.setCurrentAcivity(accessibilityEvent.getClassName().toString()); if (utils.isToActivity02()) { if (accessibilityEvent.getClassName().equals(utils.getActivity2())) { utils.qiangdan(rootInActiveWindow); } } break; case 1: /* if (currentAcivity.equals(activity1)) { if (!isStart) { success = true; activity1 = ""; timer.cancel(); } }*/ break; } } 下面介紹一下工具的主要方法; 獲得ListView中的item資訊 由於對於一個AccessibityNodeInfo,Item中的ChildCount總是遠大於其他AccessibityNodeInfo中的ChildCount所以找寫了一個遞迴 判斷所有AccessibityNodeInfo的childCount當大於10的時候就是Item的資訊,然後加入到集合中;
public void getItem(AccessibilityNodeInfo rootInActiveWindow) { if (rootInActiveWindow == null) { return; } if (rootInActiveWindow.getChildCount() == 0) { /* if (rootInActiveWindow.getText() != null) { Log.e("ssmm", " id = " + rootInActiveWindow.getText().toString()); }*/ } else { Log.e("count", rootInActiveWindow.getChildCount() + ""); if (rootInActiveWindow.getChildCount() > 10) { mList.add(rootInActiveWindow); } for (int i = 0; i < rootInActiveWindow.getChildCount(); i++) { getItem(rootInActiveWindow.getChild(i)); } } }第一個Activity的邏輯處理
/** * 下拉重新整理 * * @param rootInActiveWindow */ public void onRefresh(AccessibilityNodeInfo rootInActiveWindow) { mList.clear(); getItem(rootInActiveWindow); Log.e("mms", "" + mList.size()); if (mList.size() == 0) { return; } StringBuffer stringBuffer = new StringBuffer(); for (int i = 0; i < mList.size(); i++) { int hh = 0; int like = 0; AccessibilityNodeInfo accessibilityNodeInfo = mList.get(i); List<AccessibilityNodeInfo> acce = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId("com.sdu.didi.psnger:id/xo"); List<AccessibilityNodeInfo> accessibilityt = accessibilityNodeInfo.findAccessibilityNodeInfosByText("順路程度"); if (acce.size() > 0) { String s = acce.get(0).getText().toString(); String hour = s.substring(s.length() - 5, s.length() - 3); String m = s.substring(s.length() - 2, s.length()); hh = Integer.valueOf(hour) * 60 + Integer.valueOf(m); Log.e("mmmm", "hour == " + hour + " m= " + m); stringBuffer.append(" " + hour + ":" + m); } if (accessibilityt.size() > 0) { String s = accessibilityt.get(0).getText().toString(); String liketext = s.substring(s.length() - 3, s.length() - 1); like = Integer.valueOf(liketext); Log.e("mmmm", "like == " + like); stringBuffer.append(" " + like + "%\n"); } if (hh >= myApp.getStart() && hh <= myApp.getStop() && like >= myApp.getLike()) { List<AccessibilityNodeInfo> sure = accessibilityNodeInfo.findAccessibilityNodeInfosByText("確認同行"); if (sure != null && sure.size() > 0) { isToActivity02 = true; timer.cancel(); activity1 = ""; accessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK); return; } } } stringBuffer.append(" " + myApp.getStart() / 60 + ":" + myApp.getStop() % 60 + " " + myApp.getLike() + "% " + ++count); if (myApp.getTextView() != null){ myApp.getTextView().setText(stringBuffer.toString()); } }
第二個Activity的邏輯處理
public void qiangdan(AccessibilityNodeInfo rootInActiveWindow) { if (rootInActiveWindow == null) { return; } Log.e("aaa", rootInActiveWindow.getChildCount() + ""); if (rootInActiveWindow.getChildCount() <= 4) { List<AccessibilityNodeInfo> sue = rootInActiveWindow.findAccessibilityNodeInfosByText(myApp.getQiang()); if (sue != null && sue.size() > 0) { sue.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK); success = true; if (myApp.isVoccicer()) mTts.startSpeaking("搶單成功 搶單成功 搶單成功", null); } } else { List<AccessibilityNodeInfo> su = rootInActiveWindow.findAccessibilityNodeInfosByText("確認同行"); if (su != null && su.size() > 0) { AccessibilityNodeInfo parent = su.get(0).getParent(); parent.performAction(AccessibilityNodeInfo.ACTION_CLICK); } } }關於自動重新整理我這裡用的一個定時器,每隔固定的時間執行input命令
public void startAutoFresh() { if (isStart) { isStart = false; timer.schedule(new TimerTask() { @Override public void run() { if (currentAcivity.equals(activity1) && myApp.isStart()) { RootCmd.execRootCmdSilent(swip); } } }, 1000, myApp.getSpace()); } }
語音提示的初始化的類,我這裡整合的是訊飛的線上語音合成,離線的要錢啊
private void vioce() { //1.建立SpeechSynthesizer物件, 第二個引數:本地合成時傳InitListener mTts = SpeechSynthesizer.createSynthesizer(context.getApplicationContext(), null); //2.合成引數設定,詳見《科大訊飛MSC API手冊(Android)》SpeechSynthesizer 類 mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");//設定發音人 mTts.setParameter(SpeechConstant.SPEED, "50");//設定語速 mTts.setParameter(SpeechConstant.VOLUME, "90");//設定音量,範圍0~100 mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); //設定雲端 }
為防止當服務開啟時改動開始設定的資訊,我這裡判斷當開啟了對點選事件的攔截
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (isClick){ Toast.makeText(makeText,"請先關閉自動刷單",Toast.LENGTH_SHORT).show(); } return isClick; }原始碼地址 http://pan.baidu.com/s/1c1NPGC4