如何读取 window 内容(使用 accessibilityService)并在 Android 中使用 draw over other app permission 唤起 UI?
How to read window content (using accessibilityService) and evoking UI using draw over other app permission in Android?
我的last关于同一主题的问题不够清楚,被社区搁置,后来被自动删除。因此,我正在详细解释该问题,以便社区可以更好地理解和提供帮助。
我想要类似于 Voodoo App and MySmartPrice 优惠的功能。
现在他们在做什么
第 1 步: 当我们第一次打开 Voodoo App 时,他们会显示一个小教程。在教程结束时有一个 "Activate Now" 按钮,按下该按钮后我们将进入辅助功能设置屏幕。
第 2 步:在辅助功能屏幕中,它进一步指导如何找到和禁用 Voodoo 服务。
第 3 步: 当我们启用它时,它进一步要求授予 "Observe Your Action" 和 "Retrieve Window Content" 权限。
第 4 步: 在辅助功能屏幕上完成授予权限后,转到某个购物应用程序或通过浏览器访问购物网站并到达产品页面时,会弹出一个巫毒浮动按钮自动。
第 5 步: 一旦我们点击它,它会显示 same/related/similar 产品在其他应用程序或网站上可用的价格,以便用户可以查看最优惠的价格.
Voodoo 参考截图
现在我想从社区了解什么:
- 如何在辅助功能屏幕和产品详细信息页面上显示我的帮助 UI。
- 如何检测产品页面何时出现在屏幕上并唤起我的浮动按钮。
- 如何获取屏幕上显示的产品名称。
- 如何限制某些应用程序的屏幕阅读功能? (因为我不想陷入某些版权问题)
- 有什么教程可以帮助我吗?尽管我已经在 Google 上尝试过任何直接教程,但没有取得任何成功。
现在我需要这些信息的原因:
我正在为学生设计一个应用程序,如果我的服务器或某个网站上有可用的网站,它将帮助他们免费获得所需的(或类似的)书籍(电子书)。由于某些书籍的版权问题,我只想将其功能限制在某些应用程序上。
现在我从我的研究中了解了关于这个主题的知识(但我不确定我是否在正确的轨道上)
- 我可以求助表格Accessibility Service
- 借鉴其他应用程序。 Example 1 , Example 2
我试图在博客中总结我是如何解决这个问题的post。有需要的朋友可以看看。
Accessibility Service to the next level
我之前的回答被版主删除了,可能是因为我刚刚 post 编辑了一个 link 到博客。所以我 post 在这里再次详细回答我的问题。
最简单的方法是android自己提供的。如果您在自己的应用程序上构建某些东西,这是最好和最简单的方法。你必须使用“findAccessibilityNodeInfosByViewId
()” 如下所述
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId = source.findAccessibilityNodeInfosByViewId("YOUR PACKAGE NAME:id/RESOURCE ID FROM WHERE YOU WANT DATA");
if (findAccessibilityNodeInfosByViewId.size() > 0) {
AccessibilityNodeInfo parent = (AccessibilityNodeInfo) findAccessibilityNodeInfosByViewId.get(0);
// You can also traverse the list if required data is deep in view hierarchy.
String requiredText = parent.getText().toString();
Log.i("Required Text", requiredText);
}
}
如果您正在其他应用程序上构建某些内容并且不知道资源 ID。您必须结合 parent、child 计数、找到数据的级别、事件类型来构建逻辑。您必须在事件或源中寻找模式或上述值的组合才能获得宝贵的所需数据。此外,您必须在代码中进行一些调整,以根据您的任务获取准确的数据。就像在我们的任务中一样,我们有一些正则表达式来获取宝贵的数据。
您必须注意,如果更改视图或 UI 或更改 res id,您当前的逻辑可能会失败。因此,您必须密切关注您在其上构建服务的应用程序。以下是一些示例代码,我们在其中获取没有 res id 的数据,这可能有助于您了解其工作原理。
打印源和事件
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
Log.i("Event", event.toString() + "");
Log.i("Source", source.toString() + "");
}
获取child计数
source.getChildCount();
如果 child 计数 >0,您可能需要调查一下。
示例代码 1
if (level == 0 && source.getClassName().equals("android.widget.TextView") && source.getText()!=null && !source.getText().toString().isEmpty()){
// here level is iteration of for loop
String recivedText = source.getText().toString();
if(source.getClassName().equals("android.widget.TextView") && source.getParent()!=null && source.getParent().getClassName().equals("android.widget.FrameLayout") && source.getParent().getParent()==null){
return recivedText;
}
}
示例代码 2
if (source.getPackageName().equals("PACKAGE NAME OF APP FOR WHICH YOUR EXPECTING EVENT")) {
if(event.getEventType()==AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && "COMPLETE NAME OF ACTIVITY CLASS WITH PACKAGE NAME (if you want it for some specific screen)".equals(event.getClassName())){
if(source.getText()!=null && source.getClassName().equals("android.widget.TextView") && source.getParent()!=null && source.getParent().getClassName().equals("android.widget.RelativeLayout")&& source.getParent().getParent()!=null && source.getParent().getParent().getClassName().equals("android.widget.ScrollView") && source.getParent().getParent().getParent()!=null && source.getParent().getParent().getParent().getClassName().equals("android.support.v4.view.ViewPager")&& source.getParent().getParent().getParent().getParent()!=null && source.getParent().getParent().getParent().getParent().getClassName().equals("android.widget.FrameLayout")&& source.getParent().getParent().getParent().getParent().getParent()==null){
return source.getText().toString();
}
}
它有点笨拙,但要使其正常工作,您必须深入查看日志以找到可以将您带到所需数据的模式。
2017 年 11 月 20 日更新
Google 正在暂停所有使用辅助功能服务的应用程序,而不是辅助功能。我收到了一封关于我的一个应用程序的电子邮件,其他开发人员也收到了来自 google (Reddit) 的相同电子邮件。所以,我认为我们应该寻找一些其他的做事方式,我请求你也请在这里更新它,如果有人提出其他实现来实现相同的行为,它也会帮助其他人。
谢谢
Umesh Chauhan
我已经成功完成了此类应用程序,例如 voodoo.and 成功获取了 Flipkart 产品页面标题、价格等。
以下方法用于让用户打开产品屏幕。
private boolean isOpenFlipkartProductScreen(AccessibilityNodeInfo nodeInfo){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(nodeInfo.getViewIdResourceName()!=null){
if(nodeInfo.getViewIdResourceName().equals("com.flipkart.android:id/callout_last_linearlayout")){
Utils.sPrint("You just open product page : " + nodeInfo.getClass());
isOpenProductPage = true;
return true;
}
}
}
return false;
}
和以下用于获取产品名称和价格的代码。
private String getObtainTitle(AccessibilityNodeInfo sourceInfo){
if(sourceInfo == null)
return "";
AccessibilityNodeInfo nodeInfo = sourceInfo.getParent().getParent();
if(nodeInfo!=null){
int i = 0;
int i2 = 0;
for(int i3=0;i3<nodeInfo.getChildCount();i3++){
try{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(nodeInfo.getChild(i3).getViewIdResourceName().equals("com.flipkart.android:id/title_layout")) {
i = 0;
// while ( i < nodeInfo.getChild(i).getChildCount()){
AccessibilityNodeInfo child = nodeInfo.getChild(i3).getChild(i);
String charSequence = child.getText().toString();
// Utils.sPrint("Title is : " + charSequence);
if(charSequence!=null){
return charSequence;
}
// }
}
}
/*
*
* Below code to used for getProduct prise in flipkart app product
*
* */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(nodeInfo.getChild(i3).getViewIdResourceName().equals("com.flipkart.android:id/price_layout")){
AccessibilityNodeInfo child = nodeInfo.getChild(i3).getChild(i);
String charSequence = child.getText().toString();
if (child.getText().toString().toUpperCase().startsWith("RS.") ||
child.getText().toString().toUpperCase().startsWith("\u20b9")) {
String replace = charSequence.replace("Rs.",
"").replace("\u20b9", "");
//return replace
// Utils.sPrint("Prise is : " + replace);
}
}
}
}catch (Exception ex){
// Utils.sPrint("Ex : " + ex.getMessage());
return "";
}
}
}
return "";
}
我的last关于同一主题的问题不够清楚,被社区搁置,后来被自动删除。因此,我正在详细解释该问题,以便社区可以更好地理解和提供帮助。
我想要类似于 Voodoo App and MySmartPrice 优惠的功能。
现在他们在做什么
第 1 步: 当我们第一次打开 Voodoo App 时,他们会显示一个小教程。在教程结束时有一个 "Activate Now" 按钮,按下该按钮后我们将进入辅助功能设置屏幕。
第 2 步:在辅助功能屏幕中,它进一步指导如何找到和禁用 Voodoo 服务。
第 3 步: 当我们启用它时,它进一步要求授予 "Observe Your Action" 和 "Retrieve Window Content" 权限。
第 4 步: 在辅助功能屏幕上完成授予权限后,转到某个购物应用程序或通过浏览器访问购物网站并到达产品页面时,会弹出一个巫毒浮动按钮自动。
第 5 步: 一旦我们点击它,它会显示 same/related/similar 产品在其他应用程序或网站上可用的价格,以便用户可以查看最优惠的价格.
Voodoo 参考截图
现在我想从社区了解什么:
- 如何在辅助功能屏幕和产品详细信息页面上显示我的帮助 UI。
- 如何检测产品页面何时出现在屏幕上并唤起我的浮动按钮。
- 如何获取屏幕上显示的产品名称。
- 如何限制某些应用程序的屏幕阅读功能? (因为我不想陷入某些版权问题)
- 有什么教程可以帮助我吗?尽管我已经在 Google 上尝试过任何直接教程,但没有取得任何成功。
现在我需要这些信息的原因:
我正在为学生设计一个应用程序,如果我的服务器或某个网站上有可用的网站,它将帮助他们免费获得所需的(或类似的)书籍(电子书)。由于某些书籍的版权问题,我只想将其功能限制在某些应用程序上。
现在我从我的研究中了解了关于这个主题的知识(但我不确定我是否在正确的轨道上)
- 我可以求助表格Accessibility Service
- 借鉴其他应用程序。 Example 1 , Example 2
我试图在博客中总结我是如何解决这个问题的post。有需要的朋友可以看看。
Accessibility Service to the next level
我之前的回答被版主删除了,可能是因为我刚刚 post 编辑了一个 link 到博客。所以我 post 在这里再次详细回答我的问题。
最简单的方法是android自己提供的。如果您在自己的应用程序上构建某些东西,这是最好和最简单的方法。你必须使用“findAccessibilityNodeInfosByViewId ()” 如下所述
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId = source.findAccessibilityNodeInfosByViewId("YOUR PACKAGE NAME:id/RESOURCE ID FROM WHERE YOU WANT DATA");
if (findAccessibilityNodeInfosByViewId.size() > 0) {
AccessibilityNodeInfo parent = (AccessibilityNodeInfo) findAccessibilityNodeInfosByViewId.get(0);
// You can also traverse the list if required data is deep in view hierarchy.
String requiredText = parent.getText().toString();
Log.i("Required Text", requiredText);
}
}
如果您正在其他应用程序上构建某些内容并且不知道资源 ID。您必须结合 parent、child 计数、找到数据的级别、事件类型来构建逻辑。您必须在事件或源中寻找模式或上述值的组合才能获得宝贵的所需数据。此外,您必须在代码中进行一些调整,以根据您的任务获取准确的数据。就像在我们的任务中一样,我们有一些正则表达式来获取宝贵的数据。
您必须注意,如果更改视图或 UI 或更改 res id,您当前的逻辑可能会失败。因此,您必须密切关注您在其上构建服务的应用程序。以下是一些示例代码,我们在其中获取没有 res id 的数据,这可能有助于您了解其工作原理。
打印源和事件
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source == null) {
return;
}
Log.i("Event", event.toString() + "");
Log.i("Source", source.toString() + "");
}
获取child计数
source.getChildCount();
如果 child 计数 >0,您可能需要调查一下。
示例代码 1
if (level == 0 && source.getClassName().equals("android.widget.TextView") && source.getText()!=null && !source.getText().toString().isEmpty()){
// here level is iteration of for loop
String recivedText = source.getText().toString();
if(source.getClassName().equals("android.widget.TextView") && source.getParent()!=null && source.getParent().getClassName().equals("android.widget.FrameLayout") && source.getParent().getParent()==null){
return recivedText;
}
}
示例代码 2
if (source.getPackageName().equals("PACKAGE NAME OF APP FOR WHICH YOUR EXPECTING EVENT")) {
if(event.getEventType()==AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && "COMPLETE NAME OF ACTIVITY CLASS WITH PACKAGE NAME (if you want it for some specific screen)".equals(event.getClassName())){
if(source.getText()!=null && source.getClassName().equals("android.widget.TextView") && source.getParent()!=null && source.getParent().getClassName().equals("android.widget.RelativeLayout")&& source.getParent().getParent()!=null && source.getParent().getParent().getClassName().equals("android.widget.ScrollView") && source.getParent().getParent().getParent()!=null && source.getParent().getParent().getParent().getClassName().equals("android.support.v4.view.ViewPager")&& source.getParent().getParent().getParent().getParent()!=null && source.getParent().getParent().getParent().getParent().getClassName().equals("android.widget.FrameLayout")&& source.getParent().getParent().getParent().getParent().getParent()==null){
return source.getText().toString();
}
}
它有点笨拙,但要使其正常工作,您必须深入查看日志以找到可以将您带到所需数据的模式。
2017 年 11 月 20 日更新 Google 正在暂停所有使用辅助功能服务的应用程序,而不是辅助功能。我收到了一封关于我的一个应用程序的电子邮件,其他开发人员也收到了来自 google (Reddit) 的相同电子邮件。所以,我认为我们应该寻找一些其他的做事方式,我请求你也请在这里更新它,如果有人提出其他实现来实现相同的行为,它也会帮助其他人。
谢谢 Umesh Chauhan
我已经成功完成了此类应用程序,例如 voodoo.and 成功获取了 Flipkart 产品页面标题、价格等。 以下方法用于让用户打开产品屏幕。
private boolean isOpenFlipkartProductScreen(AccessibilityNodeInfo nodeInfo){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(nodeInfo.getViewIdResourceName()!=null){
if(nodeInfo.getViewIdResourceName().equals("com.flipkart.android:id/callout_last_linearlayout")){
Utils.sPrint("You just open product page : " + nodeInfo.getClass());
isOpenProductPage = true;
return true;
}
}
}
return false;
}
和以下用于获取产品名称和价格的代码。
private String getObtainTitle(AccessibilityNodeInfo sourceInfo){
if(sourceInfo == null)
return "";
AccessibilityNodeInfo nodeInfo = sourceInfo.getParent().getParent();
if(nodeInfo!=null){
int i = 0;
int i2 = 0;
for(int i3=0;i3<nodeInfo.getChildCount();i3++){
try{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(nodeInfo.getChild(i3).getViewIdResourceName().equals("com.flipkart.android:id/title_layout")) {
i = 0;
// while ( i < nodeInfo.getChild(i).getChildCount()){
AccessibilityNodeInfo child = nodeInfo.getChild(i3).getChild(i);
String charSequence = child.getText().toString();
// Utils.sPrint("Title is : " + charSequence);
if(charSequence!=null){
return charSequence;
}
// }
}
}
/*
*
* Below code to used for getProduct prise in flipkart app product
*
* */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
if(nodeInfo.getChild(i3).getViewIdResourceName().equals("com.flipkart.android:id/price_layout")){
AccessibilityNodeInfo child = nodeInfo.getChild(i3).getChild(i);
String charSequence = child.getText().toString();
if (child.getText().toString().toUpperCase().startsWith("RS.") ||
child.getText().toString().toUpperCase().startsWith("\u20b9")) {
String replace = charSequence.replace("Rs.",
"").replace("\u20b9", "");
//return replace
// Utils.sPrint("Prise is : " + replace);
}
}
}
}catch (Exception ex){
// Utils.sPrint("Ex : " + ex.getMessage());
return "";
}
}
}
return "";
}