First, the preface
Since the last year, the micro-letter to add the function of red envelopes, micro-letter of the electric business trip is officially officially started to hot up. But as an Android developer, we're aware of a lot of problems while stealing red envelopes, and that's the slow pace of hand-grabbing, of course, for a number of reasons. Perhaps the reason for the network, and this is the biggest reason. But other factors that can not be ignored are also to be taken into account, such as when the mobile phone charge lock screen, we do not know that someone has begun to send red envelopes, then it is also let us lose a large number of red envelopes reasons. So the problem with the web is that we developers may not be able to solve it with the relevant technology (Google and Facebook, of course, are ideally able to connect to the Internet anywhere, and of course in remote rural areas, but we expect them to become popular one day.) The real internet is the time. It's a little far away. Let's get back to business, and today we'll look at using technology to solve other non-network problems. When the charge lock screen can also automatically help us Rob red envelopes. And you know, the accuracy of the machine to rob Red envelopes is 100%, this may be the difference between people and machines. So to ensure that the accuracy of the acquisition is 100%, it depends on our efficient and accurate algorithm implementation. Here's a look at the principle implementation.
When I saw the rush of red envelopes last year, as a developer in the heart of how eager to develop a plug-in out, but at that time we can think of is to use:
ADB Shell Monkey
command to simulate the click of the screen, but that way there is a problem is not a clue to the blind click, so almost will be delayed, click Success rate is extremely low. So there was no other way to think about it, because recently when it came to the work related to accessibility, it was found that this feature could be used to rob red envelopes.
In fact, now we can go to the major market search to see that there are many of the plug-ins to rob red envelopes. Of course, we are not used for commercialization, here is just to analyze the principle. We will find that the plug-ins have a common feature is: The first step is to guide users to open accessibility.
Second, the principle of analysis
About accessibility (accessibilityservice), if you do not understand the students can go to Google, the function is actually very useful, but his appearance is the starting point for those who are physically handicapped people use, such as the finger is not sound users, how to slide the screen, And then open an application? So the auxiliary function is to do these things, his function is actually can summarize two words:
First, find the view node that we want
Second, and then analog click to achieve specific functions
We know that the view system in Android is a tree structure, so each view is a node. So we can find the specified node, so how do we find the node we want? Here we'll look at the usage of accessibility (accessibilityservice)
The first step, we need to integrate the Accessibilityservice class
We need to customize a service and then inherit Accessibilityservice, and of course we need to declare it in Androidmanifest.xml:
Step two, declare permissions and configure
This service needs to specify a permission:
And, of course, a meta-data statement, which is the configuration of this accessibilityservice. Let's take a look at the configuration file contents:
<?xml version= "1.0" encoding= "Utf-8"?> <accessibility-service xmlns:android=
"http://" Schemas.android.com/apk/res/android "
android:accessibilityeventtypes=" typenotificationstatechanged| Typewindowstatechanged "
android:accessibilityfeedbacktype=" Feedbackgeneric "
android:accessibilityflags = "Flagdefault"
android:canretrievewindowcontent= "true"
android:description= "@string/desc"
android: notificationtimeout= "100"
Here we see a lot of options, and let's look at a few common properties:
1, android:accessibilityeventtypes= "Typeallmask"
Looking at the property name is also almost clear, this is used to set the type of response event, Typeallmask of course is to respond to all types of events. Of course, there are clicks, long press, sliding and so on.
2, android:accessibilityfeedbacktype= "Feedbackspoken"
Set feedback to the user in a way that has voice broadcast and vibration. You can configure some TTS engines to enable it to pronounce.
3, android:notificationtimeout= "100"
Response time settings don't have to say much.
4, android:packagenames= "Com.example.android.apis"
You can specify an event that responds to an application, because it is not filled in response to all applied events, and the default is to respond to all applied events. For example, we write a micro-letter to rob a Red Envelope helper program, you can fill in the micro-letter package name, you can listen to the micro-letter generated events.
Attention:
1, our configuration information in addition to the definition in XML, can also be defined in the code, we are generally in the onserviceconnected () method
@Override
protected void onserviceconnected () {
Accessibilityserviceinfo info = GetServiceInfo ();
Info.eventtypes = Accessibilityevent.types_all_mask;
Info.feedbacktype = Accessibilityserviceinfo.feedback_spoken;
Info.notificationtimeout = m;
Setserviceinfo (info);
Info.packagenames = new string[]{"Xxx.xxx.xxx", "yyy.yyy.yyy", "..."};
Setserviceinfo (info);
Super.onserviceconnected ();
}
2, here we will generally write here we need to listen to the application of the package name, but sometimes we need to listen to multiple applications, then what should we do?
This is what we can do:
The first: We register multiple application package names in code to monitor multiple applications
@Override
protected void onserviceconnected () {
Accessibilityserviceinfo info = GetServiceInfo ();
Here you can set up multiple package names, monitor multiple applications
info.packagenames = new string[]{"Xxx.xxx.xxx", "yyy.yyy.yyy", "..."};
Setserviceinfo (info);
Super.onserviceconnected ();
}
Second: We filter the package name in the Onaccessibilityevent event listener method (which is most commonly used)
@Override public
void Onaccessibilityevent (Accessibilityevent event) {
String pkgname = event.getpackagename (). toString ();
if ("Xxx.xxx.xxx". Equals (Pkgname)) {
}else if ("yyy.yyy.yyy". Equals (Pkgname)) {
}else if ("...". Equals ( Pkgname)) {
}
}
Step three, listen for the specified event in the Onaccessibilityevent method
For example, we need to listen for events with notification bar messages:
@Override public
void Onaccessibilityevent (Accessibilityevent event) {
int eventtype = Event.geteventtype ();
Switch (eventtype) {case
accessibilityevent.type_notification_state_changed:
//...
}
}
This event type is many, we can view the source code of the Accessibilityevent class:
@Deprecated public static final int max_text_length = 500; /** * Represents the event of clicking on a {@link Android.view.View} like * {@link Android.widget.Button}, {@link Andro
Id.widget.CompoundButton}, etc.
* * public static final int type_view_clicked = 0x00000001; /** * Represents the event of long clicking on a {@link Android.view.View} like * {@link Android.widget.Button}, {@link
Android.widget.CompoundButton}, etc.
* * public static final int type_view_long_clicked = 0x00000002;
/** * Represents the event of selecting an item usually in the context of a * {@link android.widget.AdapterView}.
* * public static final int type_view_selected = 0x00000004;
/** * Represents the event of setting input focus of a {@link Android.view.View}.
* * public static final int type_view_focused = 0x00000008;
/** * Represents the event of changing the text of an {@link android.widget.EditText}.
* * public static final int type_view_text_changed = 0x00000010; /** * RepresenTS the event of opening a {@link Android.widget.PopupWindow}, * {@link android.view.Menu}, {@link android.app.Dialog}, et
C. * * public static final int type_window_state_changed = 0x00000020;
/** * Represents the event showing a {@link android.app.Notification}.
* * public static final int type_notification_state_changed = 0x00000040;
/** * Represents the event of a hover enter over a {@link Android.view.View}.
* * public static final int type_view_hover_enter = 0x00000080;
/** * Represents the event of a hover exit over a {@link Android.view.View}.
* * public static final int type_view_hover_exit = 0x00000100;
/** * Represents the event of starting a touch exploration gesture.
* * public static final int type_touch_exploration_gesture_start = 0x00000200;
/** * Represents the event of ending a touch exploration gesture.
* * public static final int type_touch_exploration_gesture_end = 0x00000400; /** * Represents the event of changing the content of a window and morE * Specifically the sub-tree rooted at the event ' s source.
* * public static final int type_window_content_changed = 0x00000800;
/** * Represents the event of scrolling a view.
* * public static final int type_view_scrolled = 0x00001000;
/** * Represents the event of changing the selection in a {@link android.widget.EditText}.
* * public static final int type_view_text_selection_changed = 0x00002000;
/** * Represents the event of an application making a announcement.
* * public static final int type_announcement = 0x00004000;
/** * Represents the event of gaining accessibility focus.
* * public static final int type_view_accessibility_focused = 0x00008000;
/** * Represents the event of clearing accessibility focus.
* * public static final int type_view_accessibility_focus_cleared = 0x00010000;
/** * Represents the event of traversing the text of a view at a given movement granularity. * * public static final int Type_view_text_traversed_at_movement_granularitY = 0x00020000;
/** * Represents the event of beginning gesture detection.
* * public static final int type_gesture_detection_start = 0x00040000;
/** * Represents the event of ending gesture detection.
* * public static final int type_gesture_detection_end = 0x00080000;
/** * Represents the event of the "user starting to touch the" screen.
* * public static final int type_touch_interaction_start = 0x00100000;
/** * Represents the event of the "user ending to touch the" screen.
* * public static final int type_touch_interaction_end = 0x00200000;
/** * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: * The type of change is not defined.
* * public static final int content_change_type_undefined = 0x00000000; /** * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: * A node in the subtree rooted at the source node is
Added or removed.
* * public static final int content_change_type_subtree = 0x00000001; /** * Change type for {@link #TYPE_WINDOW_CONTEnt_changed} Event: * The node ' s text CHANGED.
* * public static final int content_change_type_text = 0x00000002;
/** * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: * The node ' s CONTENT description CHANGED. * * public static final int content_change_type_content_description = 0x00000004;
There are a lot of events here, and we can see by name that there are a lot of things that we probably know about, like when a window changes, when a view is clicked, and the message is scrolled. Then we have these events and we can do our thing because we know that the event is triggered.
Step fourth, find the node view we want to handle
Here the system provides two ways for us to find the desired node view
The first is through the node view text content to find
Findaccessibilitynodeinfosbytext ("Find what")
This way of looking, just like Textview,button and other view has text content, can use this way to quickly find.
The second is the ID name in the XML layout through node view
Findaccessibilitynodeinfosbyviewid ("@id/xxx")
This is generally difficult to know, but we can do it when looking for system controls, because the IDs of the system's controls can be known and unified.
(in the case of these two methods, we may know that we can find a node in HTML by TAG/NAME/ID and so on, and the principle is similar)
Fifth step, analog click designated event
We found the view node we wanted and called the method simulation event:
Performaction (Accessibilitynodeinfo.action_click)
Call this method, of course, the parameter here is the name of the specified event, this and accessibilityevent in the listening to those events are one by one corresponding, here is the analog click event, we can of course simulate view scrolling events, long by events.
Third, the actual case: micro-letter Rob Red Envelopes Plug-ins
Above we have introduced an auxiliary function development of the concrete steps, then the following is a simple example, to combat
Example: micro-letter Automatic grab Red envelopes Plugin
First, let's take a look at the micro-letter robbery process:
The first step, we will receive a message in the notification column of a micro-letter red Envelopes
We monitor the notification bar events:
Accessibilityevent.type_notification_state_changed
Then check the message in the notification bar for the contents of the [micro-letter Red Envelope] Text
Yes, let's go into step two.
The second step, we simulate to open the notification bar
Open the micro-letter as follows:
We look for the node view containing the text content of the red envelope, then simulate the click and enter the third step:
The third step, we click to collect red envelopes
' The following figure:
Here we look for a node view containing the text content of the red envelope, and then simulate the click
Let's take a look at the specific implementation in the code:
Package krelve.demo.rob;
Import java.util.List;
Import Android.accessibilityservice.AccessibilityService;
Import Android.accessibilityservice.AccessibilityServiceInfo;
Import Android.annotation.SuppressLint;
Import android.app.Notification;
Import android.app.PendingIntent;
Import android.app.PendingIntent.CanceledException;
Import Android.util.Log;
Import android.view.accessibility.AccessibilityEvent;
Import Android.view.accessibility.AccessibilityNodeInfo; public class Robmoney extends Accessibilityservice {@Override public void Onaccessibilityevent (Accessibilityevent event
{int eventtype = Event.geteventtype (); Switch (EventType) {//Step one: Listen for notification bar message case accessibilityevent.type_notification_state_changed:list<charsequence>
texts = Event.gettext ();
if (!texts.isempty ()) {for (charsequence text:texts) {String content = text.tostring ();
LOG.I ("Demo", "Text:" +content); if (Content.contains ("[micro-red envelope]") {//Analog open Notification Bar message if (event.getparcelabledata ()!= null
&& event.getparcelabledata () instanceof Notification) {Notification Notification = (Notification) event.getpa
Rcelabledata ();
Pendingintent pendingintent = notification.contentintent;
try {pendingintent.send ();
catch (Canceledexception e) {e.printstacktrace ();
The break is in}}}}; Step Two: Monitor whether enter the micro-letter Red Envelope Message interface case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:String ClassName = Event.getclassname ().
ToString ();
if (Classname.equals ("Com.tencent.mm.ui.LauncherUI")) {//start to rob a red envelope getpacket ();
else if (classname.equals ("Com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {//start opening the Red envelope openpacket ();
} break; }/** * Find to/@SuppressLint ("Newapi") private void Openpacket () {Accessibilitynodeinfo nodeinfo = Getrootinacti
Vewindow ();
if (nodeinfo!= null) {list<accessibilitynodeinfo> List = nodeinfo. Findaccessibilitynodeinfosbytext ("Rob a red envelope");
for (Accessibilitynodeinfo n:list) {n.performaction (Accessibilitynodeinfo.action_click); }}} @SuPpresslint ("Newapi") private void GetPacket () {Accessibilitynodeinfo rootnode = Getrootinactivewindow ();
Recycle (RootNode); /** * Print the structure of a node * @param info */@SuppressLint ("Newapi") public void Recycle (Accessibilitynodeinfo info) {if Fo.getchildcount () = = 0) {if (Info.gettext ()!= null) {if ("Collect red envelopes". Equals (Info.gettext (). toString ())) {//There's a problem here that needs to be noticed.
To find a clickable view log.i ("Demo", "click" + ", Isclick:" +info.isclickable ());
Info.performaction (Accessibilitynodeinfo.action_click);
Accessibilitynodeinfo parent = Info.getparent ();
while (parent!= null) {LOG.I ("demo", "Parent Isclick:" +parent.isclickable ());
if (parent.isclickable ()) {parent.performaction (Accessibilitynodeinfo.action_click);
Break
Parent = Parent.getparent (); else {for (int i = 0; i < Info.getchildcount (); i++) {if (Info.getchild (i)!=null) {Recycle (info.getchild
(i)); @Override public void Oninterrupt ()}}}
The code has nothing to say, just follow the three steps we said before, but here's a little bit of detail to look at:
1, when we hear the notification bar message, call the following code to do the notification bar message click
if (Content.contains ("[Micro Red Envelope]") {
//analog open notification Bar message
if (event.getparcelabledata ()!= null
&&
Event.getparcelabledata () instanceof Notification) {
Notification Notification = (Notification) Event.getparcelabledata ();
Pendingintent pendingintent = notification.contentintent;
try {
pendingintent.send ();
} catch (Canceledexception e) {
e.printstacktrace ();
}
}}
2, we simulate click on the notification bar message, or need to monitor: accessibilityevent.type_window_state_changed This incident, this event we will often use, this event is in the window when the change occurred, Very common, such as we can listen to this event to monitor topactivity, and then get the package name, this is a implementation of the application of the lock principle.
3, we look for red envelopes, when the analog click to do a job, that is, from the "Collect Red envelope" text control view online lookup, find a clickable view out, and then simulate the click
if (Info.gettext ()!= null) {
if ("Get red envelope". Equals (Info.gettext (). toString ())) {
//Here's a question to note, Just need to find a clickable view
log.i ("Demo", "click" + ", Isclick:" +info.isclickable ());
Info.performaction (Accessibilitynodeinfo.action_click);
Accessibilitynodeinfo parent = Info.getparent ();
while (parent!= null) {
LOG.I ("Demo", "Parent Isclick:" +parent.isclickable ());
if (parent.isclickable ()) {
parent.performaction (accessibilitynodeinfo.action_click);
break;
Parent = Parent.getparent ();
}
}
Why do this here, in fact, the principle is very simple, because we do not know the micro-trust his interface layout, also do not know which view he setonclicklistener. We can write an example, Performaction method only to swap with the Setonclicklistener method of view simulation clicks only effective, in fact, see the source of the view can also be seen. Here is not much explanation. So we need to get a view node and look up from the bottom until we find a view that we can click.
Technology extension:
We can actually use the dump View hierarchy for UI Automator in the Ddms tool to analyze the micro-letter UI structure, which I found later, is more effective than the above code, as shown in the following figure:
Here we can see the detailed layout of the view, the properties of each view, and the important information Resource-id, which is the ID we defined in the XML, This ID we can also use the Findaccessibilitynodeinfosbyviewid ("@id/xxx") mentioned earlier to find the control
This is also a study, learn to use DDMS to analyze the view structure.
Iv. extension of
On the principle of micro-letter robbery has been analyzed above, but to achieve the ultimate, there are many problems, such as we also need to filter some of the red envelopes have been received, so the efficiency is also very high. This is the exact problem of the algorithm, I would like to say here is that we can not only use the auxiliary function to achieve the red envelopes, but also to achieve many functions, such as
1. Silent Installation
For these two requirements, we may be difficult to get, so now if we have accessibility, we can do it:
We can listen to the installation of the system interface, and then get the installation node view, and then simulate the click, uninstall is the same principle
2. Force Stop Application
We know that there are many ways to stop apps in Android, kill Process, StopService, but these methods, there are some applications they all have countermeasures, then the method we used to force the stop is to get root permission to call the system's Forcestop API to stop, But the premise still has root. So now if we have accessibility, we can do this:
We can monitor the application Details page of the system, and then find: the end of the operation of the node view, and then simulate click
Of course I said two simple examples above, and there are a lot of accessibility features that can be done. His advantage is that you don't need root permissions. But he also needs the user authorization:
If the user is not authorized, then all the work will not be able to start, so that this method is not omnipotent. Of course to say a digression: with the auxiliary function, his risk is greater than the risk after the root, such as our above the robbery of red envelopes plug-ins, in fact, we make a slight modification, you can get micro-letter Address book information, micro-letter payment password. These things can be done, so we need to think twice before we act as users.
V. Summary
With regard to accessibility, there was not much contact before, is to use this function in a job, to learn a bit, as their interest, extended to learn how to write a micro-letter to rob the red envelopes, but also consider using accessibility to do what we need to do before root. Of course, the accessibility is Google for physically handicapped people developed a function, we developers may use this function, can do a product expansion function, of course, these are Google did not think of things, But this is at least our developers in the future development of a way to solve a problem and a way to remember this function!