抢红包,先看效果图~
实现自动抢红包,解决问题有两点:
一:如何实时监听发红包的事件
二:如何在红包到来的时候自动进入页面并自动点击红包
一、如何获取红包到来的事件
为了获取红包到来状态栏的变化,我们要用到一个类:Accessibility
许多Android使用者因为各种情况导致他们要以不同的方式与手机交互。
这包括了有些用户由于视力上,身体上,年龄上的问题致使他们不能看完整的屏幕或者使用触屏,也包括了无法很好接收到语音信息和提示的听力能力比较弱的用户。
Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音(这个不支持中文),触觉反馈,手势操作,轨迹球和手柄操作。
OK,了解到这一点,那么接下来就顺利点了,首先来看看Accessibility以及AccessibilityService的使用
1.新建一个类继承AccessibilityService,并在AndroidManifest文件里注册它:
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /> <application> <service android:name="com.zkhb.weixinqinghongbao.service.QiangHongBaoService" android:label="@string/app_name" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" > <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/qianghongbao_service_config" /> </service> </application>
在子类QiangHongBaoService里实现几个重要的重载方法:
onServiceConnected() - 可选。系统会在成功连接上你的服务的时候调用这个方法,在这个方法里你可以做一下初始化工作,例如设备的声音震动管理,也可以调用setServiceInfo()进行配置工作。
onAccessibilityEvent() - 必须。通过这个函数可以接收系统发送来的AccessibilityEvent,接收来的AccessibilityEvent是经过过滤的,过滤是在配置工作时设置的。
onInterrupt() - 必须。这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
onUnbind() - 可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。
然后在/res/xml/accessibility_service_config.xml:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="" android:canRetrieveWindowContent="true" android:description="@string/accessibility_description" android:notificationTimeout="100" android:packageNames="com.tencent.mm" />
二、主要关注点以及如何在红包到来的时候自动进入页面并自动点击红包
在onAccessibilityEvent方法中监听状态栏的变化,主要有:
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED、
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED、
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
在响应窗体以及窗体内容变化时处理相关逻辑,获取带微信消息时以下代码打开消息:
//将微信的通知栏消息打开 Notification notification = (Notification) event.getParcelableData(); PendingIntent pendingIntent = notification.contentIntent; try { pendingIntent.send(); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); }
具体代码网上有很多,一般都是通过:findAccessibilityNodeInfosByText、findAccessibilityNodeInfosByViewId查找文本或者资源节点进行点击操作,但新版微信开红包页面进行了处理,没有文本信息,而如果采用:
图中resouces-id这种形式就可能出现这种情况:
在了解整个核心后,获取事件不外乎就是通过文本与id判断,那么就可以将文本改为图标方式,将id改为动态id(每次显示都是随机生成),这样一来就可以提高外挂的门槛。
如何进行规避呢,目前我想的是既然开红包的页面文本和ID都有可能会变,那我们能不能找个不变的进行判断呢,考虑过后,我觉得最可能不变的地方就是开红包这个按钮的位置,也就是图中的bounds,于是在思考过后有了下面的处理:
for (int i = 0; i < nodeInfo.getChildCount(); i++) { //Log.e("TAG", "getViewIdResourceName :"+nodeInfo.getChild(i).getViewIdResourceName()); Rect outBounds = new Rect(); nodeInfo.getChild(i).getBoundsInScreen(outBounds); int left_dp = px2dip(this, 400); int top_dp = px2dip(this, 1035); int right_dp = px2dip(this, 682); int bottom_dp = px2dip(this, 1320); int left_px = dip2px(this, left_dp); int top_px = dip2px(this, top_dp); int right_px = dip2px(this, right_dp); int bottom_px = dip2px(this, bottom_dp); Rect mStandar = new Rect(left_px,top_px,right_px,bottom_px); if(mStandar.contains(outBounds)){ Log.e("TAG", "outBounds.left :"+outBounds.left+";outBounds.top :"+outBounds.top+";outBounds.right :"+outBounds.right+";outBounds.bottom :"+outBounds.bottom); nodeInfo.getChild(i).performAction(AccessibilityNodeInfo.ACTION_CLICK); break; } }
这里取的矩形区域要比按钮区域稍大点,然后判断到开红包页面按钮是否在我们预先设置的区域内,如果在,我们直接进行点击开红包操作:AccessibilityNodeInfo.ACTION_CLICK。
其他比如如何防止重复抢等细节问题,也是要处理的问题。
好了,直接放下关键代码,仅供参考:
package com.zkhb.weixinqinghongbao.service; import java.util.Date; import java.util.List; import android.accessibilityservice.AccessibilityService; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.KeyguardManager; import android.app.KeyguardManager.KeyguardLock; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Toast; import com.zkhb.weixinqinghongbao.MainActivity; import com.zkhb.weixinqinghongbao.R; import com.zkhb.weixinqinghongbao.entity.HongBaoInfo; import com.zkhb.weixinqinghongbao.util.DateFormatUtils; import com.zkhb.weixinqinghongbao.util.LogUtil; /** * * 抢红包服务 */ @SuppressLint("NewApi") public class QiangHongBaoService extends AccessibilityService { // static final String TAG = "QiangHongBao"; /** 微信的包名*/ static final String WECHAT_PACKAGENAME = "com.tencent.mm"; /** 红包消息的关键字*/ static final String HONGBAO_TEXT_KEY = "[微信红包]"; /** 红包消息的关键字*/ static final String HONGBAO_TEXT_KEY1 = "微信红包"; private static final int ENVELOPE_RETURN = 0; private static final String LOCK_TAG = "屏幕"; Handler handler = new Handler(); /** 是否在抢红包界面里*/ // public boolean isInMM=false; /** 是否可以点击*/ // public boolean ISCLICKED=false; /** 是否进入过拆红包界面*/ public static boolean ISCOMINQIANGCHB=false; //真正的 public static boolean ISCOMINQIANGCHB2=false; //真正的判断 public static boolean ISCOMINQIANGCHB3=false; /** 是否来自通知栏*/ private static boolean ISCOMNOTIFY=false; private PowerManager pm; //点亮屏幕 private WakeLock mWakeLock; //解锁锁定屏幕 private KeyguardLock keyguardLock; /**判断之前用户是否锁屏 */ private static boolean islock=false; /**通知服务 */ private NotificationManager n_manager; public void unlock(){ if(pm==null){ pm = (PowerManager) getApplication().getSystemService(Context.POWER_SERVICE); } boolean isScreenOn = pm.isScreenOn();//如果为true,则表示屏幕“亮”了,否则屏幕“暗”了。 if(!isScreenOn){ islock=true; KeyguardManager keyguardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE); keyguardLock = keyguardManager.newKeyguardLock(LOCK_TAG); keyguardLock.disableKeyguard(); mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, LOCK_TAG); mWakeLock.acquire(); } } public void lock(){ if(islock){ //释放屏幕常亮锁 if(null != mWakeLock) { mWakeLock.release(); } //屏幕锁定 if(keyguardLock!=null){ keyguardLock.reenableKeyguard(); } } } @Override public void onAccessibilityEvent(AccessibilityEvent event) { final int eventType = event.getEventType(); LogUtil.info("事件---->" + event); //通知栏事件 if(eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) { unlock(); List<CharSequence> texts = event.getText(); if(!texts.isEmpty()) { for(CharSequence t : texts) { String text = String.valueOf(t); if(text.contains(HONGBAO_TEXT_KEY)) { ISCOMNOTIFY=true; ISCOMINQIANGCHB=false; openNotify(event); break; } } } } else if(eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { unlock(); openHongBao(event); // AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); // List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText = nodeInfo.findAccessibilityNodeInfosByText("领取红包"); // if(findAccessibilityNodeInfosByText.isEmpty()){ // isInMM=false; // }else{ // isInMM=true; // } // List<CharSequence> text = event.getText(); // if(text.size()>=0){ // CharSequence charSequence = text.get(0); //// if(charSequence.equals("微信")){ //// isInMM=true; //// }else{ //// isInMM=false; //// } // } }else if(eventType==AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && ISCOMNOTIFY){ unlock(); openHongBao(event); List<AccessibilityNodeInfo> InfoText = getRootInActiveWindow().findAccessibilityNodeInfosByText("领取红包"); if(!InfoText.isEmpty()){ checkKey2(); ISCOMNOTIFY=false; } } } /*@Override protected boolean onKeyEvent(KeyEvent event) { //return super.onKeyEvent(event); return true; }*/ @Override public boolean onUnbind(Intent intent) { Toast.makeText(this, "断开抢红包服务", Toast.LENGTH_SHORT).show(); ISCOMINQIANGCHB=false; islock=false; ISCOMINQIANGCHB2=false; ISCOMINQIANGCHB3=false; ISCOMNOTIFY=false; setNotification("已关闭抢红包小助手服务~~",Notification.FLAG_AUTO_CANCEL,"已关闭抢红包小助手服务~~~"); return super.onUnbind(intent); } @Override public void onInterrupt() { Toast.makeText(this, "中断抢红包服务", Toast.LENGTH_SHORT).show(); } @Override protected void onServiceConnected() { super.onServiceConnected(); ISCOMINQIANGCHB=false; ISCOMINQIANGCHB2=false; ISCOMINQIANGCHB3=false; islock=false; ISCOMNOTIFY=false; setNotification("已开启抢红包小助手服务~~",Notification.FLAG_NO_CLEAR,"已开启抢红包小助手服务~~~"); Toast.makeText(this, "连接抢红包服务", Toast.LENGTH_SHORT).show(); } private void setNotification(String content,int flags,String title) { if(n_manager==null){ n_manager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); } n_manager.cancelAll(); Notification notification=new Notification(R.drawable.ic_launcher, content, System.currentTimeMillis()); // notification.defaults |= Notification.DEFAULT_VIBRATE; // long[] vibrate = {0,100,200,300}; //0毫秒后开始振动,振动100毫秒后停止,再过200毫秒后再次振动300毫秒 // notification.vibrate=vibrate; notification.flags |= flags; //表明在点击了通知栏中的"清除通知"后,此通知不清除, Intent notificationIntent = new Intent(this,MainActivity.class); //点击该通知后要跳转的Activity PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(),0,notificationIntent,0); notification.setLatestEventInfo(getApplicationContext(), title, "进入微信抢红包~~", contentIntent); n_manager.notify(0, notification); } private void sendNotifyEvent(){ AccessibilityManager manager= (AccessibilityManager)getSystemService(ACCESSIBILITY_SERVICE); if (!manager.isEnabled()) { return; } AccessibilityEvent event=AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); event.setPackageName(WECHAT_PACKAGENAME); event.setClassName(Notification.class.getName()); CharSequence tickerText = HONGBAO_TEXT_KEY; event.getText().add(tickerText); manager.sendAccessibilityEvent(event); } /** 打开通知栏消息*/ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void openNotify(AccessibilityEvent event) { if(event.getParcelableData() == null || !(event.getParcelableData() instanceof Notification)) { return; } //将微信的通知栏消息打开 Notification notification = (Notification) event.getParcelableData(); PendingIntent pendingIntent = notification.contentIntent; 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())) { //点中了红包,下一步就是去拆红包 ISCOMINQIANGCHB=true; ISCOMINQIANGCHB2=true; checkKey1(); } else if("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI".equals(event.getClassName())) { //拆完红包后看详细的纪录界面 LogUtil.info("事件---->com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI"); //nonething // if(ISCOMINQIANGCHB){ // ISCOMINQIANGCHB=false; // } if(ISCOMINQIANGCHB2){ ISCOMINQIANGCHB3=true; }else{ ISCOMINQIANGCHB3=false; } checkKey3(); ISCOMINQIANGCHB=true; ISCOMINQIANGCHB2=false; performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); if(getSharedPreferences("config", Context.MODE_PRIVATE).getBoolean("auto", false)){ performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME); } lock(); } else if("com.tencent.mm.ui.LauncherUI".equals(event.getClassName())) { // isInMM=true; //在聊天界面,去点中红包 LogUtil.info("事件---->com.tencent.mm.ui.LauncherUI"); checkKey2(); } } @SuppressLint("NewApi") @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void checkKey3() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if(nodeInfo == null) { LogUtil.info("rootWindow为空333"); return; } //TODO List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText = nodeInfo.findAccessibilityNodeInfosByText("元"); if(findAccessibilityNodeInfosByText.size()>=0){ AccessibilityNodeInfo accessibilityNodeInfo2 = findAccessibilityNodeInfosByText.get(0).getParent(); CharSequence money = accessibilityNodeInfo2.getChild(2).getText(); CharSequence name = accessibilityNodeInfo2.getChild(0).getText(); if(ISCOMINQIANGCHB3){ HongBaoInfo info=new HongBaoInfo(); info.setStrDateTime(DateFormatUtils.format("yyyy-MM-dd HH:mm:ss", new Date())); info.setStrMoney(money+""); info.setStrName(name+""); info.save(); } Toast.makeText(getApplicationContext(), money+":::"+name, 0).show(); } // List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/aw8"); // AccessibilityNodeInfo accessibilityNodeInfo = findAccessibilityNodeInfosByViewId.get(0); // CharSequence text = accessibilityNodeInfo.getText(); } private void checkKey1() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if(nodeInfo == null) { LogUtil.info("rootWindow为空11111"); return; } for (int i = 0; i < nodeInfo.getChildCount(); i++) { Log.e("TAG", "getViewIdResourceName :"+nodeInfo.getChild(i).getViewIdResourceName()); Rect outBounds = new Rect(); nodeInfo.getChild(i).getBoundsInScreen(outBounds); int left_dp = px2dip(this, 400); int top_dp = px2dip(this, 1035); int right_dp = px2dip(this, 682); int bottom_dp = px2dip(this, 1320); int left_px = dip2px(this, left_dp); int top_px = dip2px(this, top_dp); int right_px = dip2px(this, right_dp); int bottom_px = dip2px(this, bottom_dp); Rect mStandar = new Rect(left_px,top_px,right_px,bottom_px); if(mStandar.contains(outBounds)){ Log.e("TAG", "outBounds.left :"+outBounds.left+";outBounds.top :"+outBounds.top+";outBounds.right :"+outBounds.right+";outBounds.bottom :"+outBounds.bottom); nodeInfo.getChild(i).performAction(AccessibilityNodeInfo.ACTION_CLICK); break; } // nodeInfo.performAction(action)//[405,1042][675,1312] } //List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("拆红包"); // List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("com.tencent.mm:id/b5d"); // for(AccessibilityNodeInfo n : list) { // n.performAction(AccessibilityNodeInfo.ACTION_CLICK); // } } private void checkKey2() { AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); if(nodeInfo == null) { LogUtil.info("rootWindow为空222222"); return; } List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("领取红包"); if(list.isEmpty()) { list = nodeInfo.findAccessibilityNodeInfosByText(HONGBAO_TEXT_KEY); for(AccessibilityNodeInfo n : list) { LogUtil.info("-->微信红包:" + n); n.performAction(AccessibilityNodeInfo.ACTION_CLICK); break; } } else { //最新的红包领起 AccessibilityNodeInfo parent = list.get(list.size() - 1).getParent(); // Log.w(TAG, "ISCLICKED::"+ISCLICKED)!ISCLICKED; if(parent != null && !ISCOMINQIANGCHB) { parent.performAction(AccessibilityNodeInfo.ACTION_CLICK); } // for(int i = list.size() - 1; i >= 0; i --) { // AccessibilityNodeInfo parent = list.get(i).getParent(); // Log.i(TAG, "-->领取红包:" + parent); // if(parent != null && parent.isClickable()) { // parent.performAction(AccessibilityNodeInfo.ACTION_CLICK); // break; // } // } // performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); } } /** * @param info 当前节点 * @param matchFlag 需要匹配的文字 * @param type 操作的类型 */ @SuppressLint("NewApi") public void recycle(AccessibilityNodeInfo info, String matchFlag, int type) { if (info != null) { if (info.getChildCount() == 0) { CharSequence desrc = info.getContentDescription(); switch (type) { case ENVELOPE_RETURN://返回 if (desrc != null && matchFlag.equals(info.getContentDescription().toString().trim())) { if (info.isCheckable()) { info.performAction(AccessibilityNodeInfo.ACTION_CLICK); } else { performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK); } } break; } } else { int size = info.getChildCount(); for (int i = 0; i < size; i++) { AccessibilityNodeInfo childInfo = info.getChild(i); if (childInfo != null) { LogUtil.info("index: " + i + " info" + childInfo.getClassName() + " : " + childInfo.getContentDescription()+" : "+info.getText()); recycle(childInfo, matchFlag, type); } } } } } @Override public void onDestroy() { super.onDestroy(); lock(); } /** * 根据手机的分辨率从 dip 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ public static int px2dip(Context context, float pxValue) { // final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / 3 + 0.5f); //3是本人手机的设备密度 } }
AccessibilityService还可以用在智能安装、虚拟按键上都有很多应用,这样的实践只是给我们一种解决问题另外的一种思路,问题嘛,总还是要解决的。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Android实现QQ抢红包插件,包括了Android实现QQ抢红包插件的使用技巧和注意事项,需要的朋友参考一下 又想到快要过年了,到时候还不知道群里要发好多红包,所以我将之前在网上宕的一份微信抢红包的代码修改了一下,实现了QQ抢红包!可以支持抢QQ拼手气红包,普通红包,口令红包,现在再也不怕20年单身手速的人跟我抢红包了! 先看测试效果图: 1.抢QQ口令红包
本文向大家介绍Android中微信抢红包插件原理解析及开发思路,包括了Android中微信抢红包插件原理解析及开发思路的使用技巧和注意事项,需要的朋友参考一下 一、前言 自从去年中微信添加抢红包的功能,微信的电商之旅算是正式开始正式火爆起来。但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导致了。或许是网络的原因,而且这个也是最大
本文向大家介绍Android实现微信自动抢红包的程序,包括了Android实现微信自动抢红包的程序的使用技巧和注意事项,需要的朋友参考一下 简单实现了微信自动抢红包的服务,原理就是根据关键字找到相应的View, 然后自动点击。主要是用到AccessibilityService这个辅助服务,基本可以满足自动抢红包的功能,但是有些逻辑需要优化,比如,拆完一个红包后,必须手动点击返回键,才能进行下一次
捋一下思路,微信群发红包的基本情况是:每一次发红包都会与上一次有一些时间间隔,实现自动化抢红包的基本思路如下: 手动清空之前微信群中的红包记录 执行自动化抢红包程序,进入发红包的微信群(可以暂时将其顶置),循环检测群中是否有红包,发现红包则点击红包
Android 微信抢红包外挂是一个自动化抢微信红包的源码,很简单的方式实现(找关键字的View然后自动点击)。完全模拟人工操作,抢红包速度取决于手机的性能与网络,不涉及任何第三方软件接口。 当然原理很简单,过程很复杂,因为主要是用到AccessibilityService这个辅助服务,而网上关于这个用法也是很少讲解,都是靠自己一步一步研究出来。这份源码基本可以满足抢红包的功能,但仍需要进行一些逻辑优化。 效果预览:
本文向大家介绍Android辅助功能实现自动抢红包(附源码),包括了Android辅助功能实现自动抢红包(附源码)的使用技巧和注意事项,需要的朋友参考一下 一、描述 最近看到同事有用抢红包的软件,就想看看抢红包的具体实现是如何的,所以了解了一下,有用辅助功能实现的,所以在下面的示例中会展示一个抢红包的小Demo,附带源码抢红包源码。 二、效果图 在桌面收到红包进行抢 在聊天页面收到口令红包 三、A