Compare commits

2 Commits

Author SHA1 Message Date
renjianbo
a799067c64 commit 2026-01-06 11:26:14 +08:00
renjianbo
73f866ca5b commit 2026-01-06 10:32:14 +08:00
49 changed files with 6668 additions and 3186 deletions

View File

@@ -1,19 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="personal">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="order">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="wechat">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-01-06T02:09:44.948235800Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=jb7t9pdeydvcxcqk" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="personal">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="order">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="wechat">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@@ -73,11 +73,6 @@
android:theme="@style/AppTheme">
<activity android:name="com.fisherbone.fuzhu.Main2Activity"
android:exported="false"></activity>
<activity android:name="com.fisherbone.fuzhu.MainActivityy"
android:exported="false">
</activity>
<activity
android:name=".activity.CancelThumbUpActivity"
android:exported="false" />

View File

@@ -16,6 +16,8 @@ import com.lzy.okgo.interceptor.HttpLoggingInterceptor;
import com.lzy.okgo.model.HttpHeaders;
import com.lzy.okgo.model.HttpParams;
import com.blankj.utilcode.util.Utils;
import com.fisherbone.fuzhu.utils.LiveEventBusFix;
import com.jeremyliao.liveeventbus.LiveEventBus;
import com.tencent.bugly.crashreport.CrashReport;
import com.xiangxue.common.network.base.NetworkApi;
@@ -74,6 +76,12 @@ public class FuzhuApplication extends Application {
DeviceID.register(this);
privacyPolicyAgreed = true;
}
// 修复 LiveEventBus 在 Android 12+ 上的 BroadcastReceiver 注册问题
// 必须在 LiveEventBus 初始化之前调用
// 注意:此修复可能无法完全解决问题,因为 LiveEventBus 在静态初始化时会崩溃
// 如果修复失败MainActivity 中的 try-catch 会捕获异常,应用仍可继续运行
LiveEventBusFix.fix(this);
}

View File

@@ -1,102 +0,0 @@
package com.fisherbone.fuzhu;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import com.xiangxue.arouter_annotation.ARouter;
import com.xiangxue.arouter_annotation.Parameter;
import com.xiangxue.arouter_api.ParameterManager;
import com.xiangxue.arouter_api.RouterManager;
import com.xiangxue.common.bean.Student;
import com.xiangxue.common.order.OrderDrawable;
import com.xiangxue.common.order.user.IUser;
import com.xiangxue.common.utils.Cons;
@ARouter(path = "/app/MainActivityy")
public class MainActivityy extends AppCompatActivity {
@Parameter(name = "/order/getDrawable")
OrderDrawable orderDrawable; // 公共基础库common
@Parameter(name = "/order/getUserInfo")
IUser iUser; // 公共基础库common
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main1);
if (BuildConfig.isRelease) {
Log.e(Cons.TAG, "当前为集成化模式除app可运行其他子模块都是Android Library");
} else {
Log.e(Cons.TAG, "当前为组件化模式app/order/personal子模块都可独立运行");
}
// 懒加载方式,跳到哪加载哪个类
ParameterManager.getInstance().loadParameter(this);
// app模块本来就可以直接加载其他模块的资源 personal
// 拿到 order模块的图片 在app模块展示
int drawableId = orderDrawable.getDrawable();
ImageView img = findViewById(R.id.img);
img.setImageResource(drawableId);
// 我输出 order模块的Bean休息
Log.d(Cons.TAG, "order的Bean onCreate: " + iUser.getUserInfo().toString());
}
public void jumpOrder(View view) {
/*Intent intent = new Intent(this, Order_MainActivity.class);
intent.putExtra("name", "derry");
startActivity(intent);*/
// 使用我们自己写的路由 跳转交互
RouterManager.getInstance()
.build("/order/Order_MainActivity")
.withString("name", "杜子腾")
.navigation(this); // 组件和组件通信
}
public void jumpPersonal(View view) {
// 以前是这样跳转
/*Intent intent = new Intent(this, Personal_MainActivity.class);
intent.putExtra("name", "derry");
startActivity(intent);*/
// 现在是这样跳转 目前还要写这么多代码,是不是非常累
// TODO 最终的成效:用户 一行代码搞定,同时还可以传递参数,同时还可以懒加载
/*ARouter$$Group$$personal group$$personal = new ARouter$$Group$$personal();
Map<String, Class<? extends ARouterPath>> groupMap = group$$personal.getGroupMap();
Class<? extends ARouterPath> myClass = groupMap.get("personal");
try {
ARouter$$Path$$personal path = (ARouter$$Path$$personal) myClass.newInstance();
Map<String, RouterBean> pathMap = path.getPathMap();
RouterBean bean = pathMap.get("/personal/Personal_MainActivity");
if (bean != null) {
Intent intent = new Intent(this, bean.getMyClass());
startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}*/
Student student = new Student("Derry大大", "", 99);
// 使用我们自己写的路由 跳转交互
RouterManager.getInstance()
.build("/personal/Personal_MainActivity")
.withString("name", "史甄湘")
.withString("sex", "")
.withInt("age", 99)
.withSerializable("student", student)
.navigation(this);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,212 +1,230 @@
package com.fisherbone.fuzhu.abllib;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.LogUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 2019/4/21
* 14:36
* Levine
* wechat 1483232332
*/
public class AblStepHandler extends Handler {
private static AblStepHandler instance;
private List<StepListener> mStepListeners = new ArrayList<>();
private boolean isStop = true;//是否停止
private String[] mDatas;//数据
private long mStepMsgDelayMillis = 2000;//消息延迟发送时间
private List<String> mInitializedSteps = new ArrayList<>();//已经初始化过的步骤
private AblStepHandler() {
super(Looper.getMainLooper());
}
public static AblStepHandler getInstance() {
if (instance == null) {
synchronized (AblStepHandler.class) {
if (instance == null) {
instance = new AblStepHandler();
}
}
}
return instance;
}
/**
* 发送handler执行消息
*
* @param step 步骤
* @param delayMillis 延迟时间
* @param datas 数据
*/
public static void sendMsg(int step, long delayMillis, String... datas) {
AblStepHandler ablStepHandler = AblStepHandler.getInstance();
if (datas != null && datas.length > 0) {
ablStepHandler.setDatas(datas);
}
ablStepHandler.sendEmptyMessageDelayed(step, delayMillis);
}
/**
* 发送handler执行消息
*
* @param step 步骤
* @param delayMillis 延迟时间
*/
public static void sendMsg(int step, long delayMillis) {
sendMsg(step, delayMillis, new String[]{});
}
/**
* 发送handler执行消息
*
* @param step 步骤
* @param datas 数据
*/
public static void sendMsg(int step, String... datas) {
sendMsg(step, AblStepHandler.getInstance().getStepMsgDelayMillis(), datas);
}
/**
* 发送handler执行消息
*
* @param step 步骤
*/
public static void sendMsg(int step) {
sendMsg(step, AblStepHandler.getInstance().getStepMsgDelayMillis(), new String[]{});
}
/**
* 根据viewid或文字发送执行消息
*
* @param millisInFuture view查找超时时间
* @param countDownInterval 查找间隔时间
* @param step 步骤
*/
public static void sendMsgByView(
long millisInFuture,
long countDownInterval,
int step,
String... nextMsgViewIdOrText
) {
FindViewCountDown.start(millisInFuture, countDownInterval, step, nextMsgViewIdOrText);
}
/**
* 根据viewid或文字发送执行消息
*
* @param millisInFuture view查找超时时间
* @param countDownInterval 查找间隔时间
* @param callBack 回调,注:如果回调不为空将不发送消息,要发送消息就自己在成功的回调里发送
* @param viewIdOrText viewid或者文字内容
*/
public static void sendMsgByView(
long millisInFuture,
long countDownInterval,
@NonNull FindViewCountDown.CallBack callBack,
String... viewIdOrText
) {
FindViewCountDown.start(millisInFuture, countDownInterval, callBack, viewIdOrText);
}
/**
* 根据viewid或文字发送执行消息
*
* @param step 步骤
* @param viewIdOrText viewid或者文字内容
*/
public static void sendMsgByView(
int step,
String... viewIdOrText
) {
FindViewCountDown.start(step, viewIdOrText);
}
/**
* 根据viewid或文字发送执行消息
*
* @param callBack 回调,注:如果回调不为空将不发送消息,要发送消息就自己在成功的回调里发送
* @param viewIdOrText viewid或者文字内容
*/
public static void sendMsgByView(
@NonNull FindViewCountDown.CallBack callBack,
String... viewIdOrText
) {
FindViewCountDown.start(callBack, viewIdOrText);
}
public String[] getDatas() {
return mDatas;
}
public void setDatas(String... datas) {
mDatas = datas;
}
public long getStepMsgDelayMillis() {
return mStepMsgDelayMillis;
}
public void setStepMsgDelayMillis(long msgDelayMillis) {
mStepMsgDelayMillis = msgDelayMillis;
}
public boolean isStop() {
return isStop;
}
public void setStop(boolean stop) {
isStop = stop;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (isStop) return;
LogUtils.v("step", msg.what);
for (StepListener stepListener : mStepListeners) {
stepListener.onStep(msg.what, msg);
}
}
private void addStepListener(StepListener handleMessageListener) {
mStepListeners.add(handleMessageListener);
}
public void removeStepListener(StepListener handleMessageListener) {
mStepListeners.remove(handleMessageListener);
}
/**
* 初始化步骤实现类
*
* @param ablStepBases 继承了StepBase的实现类
*/
public void initStepClass(BaseAblStep... ablStepBases) {
for (BaseAblStep ablStepBase : ablStepBases) {
if (mInitializedSteps.contains(ablStepBase.getClass().getName())) {
LogUtils.e(ablStepBase.getClass().getName() + "已经初始化,请勿重复初始化");
continue;
}
addStepListener(ablStepBase);
mInitializedSteps.add(ablStepBase.getClass().getName());
}
}
public interface StepListener {
void onStep(int step, Message msg);
}
}
package com.fisherbone.fuzhu.abllib;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.LogUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 2019/4/21
* 14:36
* Levine
* wechat 1483232332
*/
public class AblStepHandler extends Handler {
private static AblStepHandler instance;
private List<StepListener> mStepListeners = new ArrayList<>();
private boolean isStop = true;//是否停止
private String[] mDatas;//数据
private long mStepMsgDelayMillis = 2000;//消息延迟发送时间
private List<String> mInitializedSteps = new ArrayList<>();//已经初始化过的步骤
private AblStepHandler() {
super(Looper.getMainLooper());
}
public static AblStepHandler getInstance() {
if (instance == null) {
synchronized (AblStepHandler.class) {
if (instance == null) {
instance = new AblStepHandler();
}
}
}
return instance;
}
/**
* 发送handler执行消息
*
* @param step 步骤
* @param delayMillis 延迟时间
* @param datas 数据
*/
public static void sendMsg(int step, long delayMillis, String... datas) {
AblStepHandler ablStepHandler = AblStepHandler.getInstance();
if (datas != null && datas.length > 0) {
ablStepHandler.setDatas(datas);
}
android.util.Log.e("TIAOSHI###", "AblStepHandler.sendMsg: 发送消息 step=" + step + ", delay=" + delayMillis + "ms, isStop=" + ablStepHandler.isStop());
boolean result = ablStepHandler.sendEmptyMessageDelayed(step, delayMillis);
android.util.Log.e("TIAOSHI###", "AblStepHandler.sendMsg: 消息已加入队列, sendEmptyMessageDelayed返回=" + result);
}
/**
* 发送handler执行消息
*
* @param step 步骤
* @param delayMillis 延迟时间
*/
public static void sendMsg(int step, long delayMillis) {
sendMsg(step, delayMillis, new String[]{});
}
/**
* 发送handler执行消息
*
* @param step 步骤
* @param datas 数据
*/
public static void sendMsg(int step, String... datas) {
sendMsg(step, AblStepHandler.getInstance().getStepMsgDelayMillis(), datas);
}
/**
* 发送handler执行消息
*
* @param step 步骤
*/
public static void sendMsg(int step) {
sendMsg(step, AblStepHandler.getInstance().getStepMsgDelayMillis(), new String[]{});
}
/**
* 根据viewid或文字发送执行消息
*
* @param millisInFuture view查找超时时间
* @param countDownInterval 查找间隔时间
* @param step 步骤
*/
public static void sendMsgByView(
long millisInFuture,
long countDownInterval,
int step,
String... nextMsgViewIdOrText
) {
FindViewCountDown.start(millisInFuture, countDownInterval, step, nextMsgViewIdOrText);
}
/**
* 根据viewid或文字发送执行消息
*
* @param millisInFuture view查找超时时间
* @param countDownInterval 查找间隔时间
* @param callBack 回调,注:如果回调不为空将不发送消息,要发送消息就自己在成功的回调里发送
* @param viewIdOrText viewid或者文字内容
*/
public static void sendMsgByView(
long millisInFuture,
long countDownInterval,
@NonNull FindViewCountDown.CallBack callBack,
String... viewIdOrText
) {
FindViewCountDown.start(millisInFuture, countDownInterval, callBack, viewIdOrText);
}
/**
* 根据viewid或文字发送执行消息
*
* @param step 步骤
* @param viewIdOrText viewid或者文字内容
*/
public static void sendMsgByView(
int step,
String... viewIdOrText
) {
FindViewCountDown.start(step, viewIdOrText);
}
/**
* 根据viewid或文字发送执行消息
*
* @param callBack 回调,注:如果回调不为空将不发送消息,要发送消息就自己在成功的回调里发送
* @param viewIdOrText viewid或者文字内容
*/
public static void sendMsgByView(
@NonNull FindViewCountDown.CallBack callBack,
String... viewIdOrText
) {
FindViewCountDown.start(callBack, viewIdOrText);
}
public String[] getDatas() {
return mDatas;
}
public void setDatas(String... datas) {
mDatas = datas;
}
public long getStepMsgDelayMillis() {
return mStepMsgDelayMillis;
}
public void setStepMsgDelayMillis(long msgDelayMillis) {
mStepMsgDelayMillis = msgDelayMillis;
}
public boolean isStop() {
return isStop;
}
public void setStop(boolean stop) {
android.util.Log.e("TIAOSHI###", "AblStepHandler.setStop: 设置 isStop=" + stop + ", 调用堆栈: " + android.util.Log.getStackTraceString(new Exception()));
isStop = stop;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
android.util.Log.e("TIAOSHI###", "AblStepHandler.handleMessage: 收到消息 step=" + msg.what + ", isStop=" + isStop + ", Thread=" + Thread.currentThread().getName() + ", Looper=" + (Looper.myLooper() != null ? Looper.myLooper().toString() : "null"));
if (isStop) {
android.util.Log.e("TIAOSHI###", "AblStepHandler: 收到消息 " + msg.what + " 但 isStop=true忽略");
return;
}
android.util.Log.e("TIAOSHI###", "AblStepHandler: 处理消息 step=" + msg.what + ", listeners=" + mStepListeners.size());
LogUtils.v("step", msg.what);
try {
for (StepListener stepListener : mStepListeners) {
android.util.Log.e("TIAOSHI###", "AblStepHandler: 调用 listener.onStep, listener=" + stepListener.getClass().getName());
try {
stepListener.onStep(msg.what, msg);
android.util.Log.e("TIAOSHI###", "AblStepHandler: listener.onStep 执行完成, listener=" + stepListener.getClass().getName());
} catch (Exception e) {
android.util.Log.e("TIAOSHI###", "AblStepHandler: listener.onStep 执行异常, listener=" + stepListener.getClass().getName(), e);
}
}
} catch (Exception e) {
android.util.Log.e("TIAOSHI###", "AblStepHandler.handleMessage: 处理消息异常", e);
}
}
private void addStepListener(StepListener handleMessageListener) {
mStepListeners.add(handleMessageListener);
}
public void removeStepListener(StepListener handleMessageListener) {
mStepListeners.remove(handleMessageListener);
}
/**
* 初始化步骤实现类
*
* @param ablStepBases 继承了StepBase的实现类
*/
public void initStepClass(BaseAblStep... ablStepBases) {
for (BaseAblStep ablStepBase : ablStepBases) {
if (mInitializedSteps.contains(ablStepBase.getClass().getName())) {
LogUtils.e(ablStepBase.getClass().getName() + "已经初始化,请勿重复初始化");
continue;
}
addStepListener(ablStepBase);
mInitializedSteps.add(ablStepBase.getClass().getName());
}
}
public interface StepListener {
void onStep(int step, Message msg);
}
}

View File

@@ -1,319 +1,321 @@
package com.fisherbone.fuzhu.abllib;
/**
* 2019/4/21
* 18:33
* Levine
* wechat 1483232332
*/
public class AblSteps {
/**
* 全局静态常量步骤标识1~200不够自己再加从200开始加基本应该都是够的了
*/
public final static int STEP_1 = 1;
public final static int STEP_2 = 2;
public final static int STEP_3 = 3;
public final static int STEP_4 = 4;
public final static int STEP_5 = 5;
public final static int STEP_6 = 6;
public final static int STEP_7 = 7;
public final static int STEP_8 = 8;
public final static int STEP_9 = 9;
/*****************************************/
public final static int STEP_10 = 10;
public final static int STEP_11 = 11;
public final static int STEP_12 = 12;
public final static int STEP_13 = 13;
public final static int STEP_14 = 14;
public final static int STEP_15 = 15;
public final static int STEP_16 = 16;
public final static int STEP_17 = 17;
public final static int STEP_18 = 18;
public final static int STEP_19 = 19;
/*****************************************/
public final static int STEP_20 = 20;
public final static int STEP_21 = 21;
public final static int STEP_22 = 22;
public final static int STEP_23 = 23;
public final static int STEP_24 = 24;
public final static int STEP_25 = 25;
public final static int STEP_26 = 26;
public final static int STEP_27 = 27;
public final static int STEP_28 = 28;
public final static int STEP_29 = 29;
/*****************************************/
public final static int STEP_30 = 30;
public final static int STEP_31 = 31;
public final static int STEP_32 = 32;
public final static int STEP_33 = 33;
public final static int STEP_34 = 34;
public final static int STEP_35 = 35;
public final static int STEP_36 = 36;
public final static int STEP_37 = 37;
public final static int STEP_38 = 38;
public final static int STEP_39 = 39;
/*****************************************/
public final static int STEP_40 = 40;
public final static int STEP_41 = 41;
public final static int STEP_42 = 42;
public final static int STEP_43 = 43;
public final static int STEP_44 = 44;
public final static int STEP_45 = 45;
public final static int STEP_46 = 46;
public final static int STEP_47 = 47;
public final static int STEP_48 = 48;
public final static int STEP_49 = 49;
/*****************************************/
public final static int STEP_50 = 50;
public final static int STEP_51 = 51;
public final static int STEP_52 = 52;
public final static int STEP_53 = 53;
public final static int STEP_54 = 54;
public final static int STEP_55 = 55;
public final static int STEP_56 = 56;
public final static int STEP_57 = 57;
public final static int STEP_58 = 58;
public final static int STEP_59 = 59;
/*****************************************/
public final static int STEP_60 = 60;
public final static int STEP_61 = 61;
public final static int STEP_62 = 62;
public final static int STEP_63 = 63;
public final static int STEP_64 = 64;
public final static int STEP_65 = 65;
public final static int STEP_66 = 66;
public final static int STEP_67 = 67;
public final static int STEP_68 = 68;
public final static int STEP_69 = 69;
/*****************************************/
public final static int STEP_70 = 70;
public final static int STEP_71 = 71;
public final static int STEP_72 = 72;
public final static int STEP_73 = 73;
public final static int STEP_74 = 74;
public final static int STEP_75 = 75;
public final static int STEP_76 = 76;
public final static int STEP_77 = 77;
public final static int STEP_78 = 78;
public final static int STEP_79 = 79;
/*****************************************/
public final static int STEP_80 = 80;
public final static int STEP_81 = 81;
public final static int STEP_82 = 82;
public final static int STEP_83 = 83;
public final static int STEP_84 = 84;
public final static int STEP_85 = 85;
public final static int STEP_86 = 86;
public final static int STEP_87 = 87;
public final static int STEP_88 = 88;
public final static int STEP_89 = 89;
/*****************************************/
public final static int STEP_90 = 90;
public final static int STEP_91 = 91;
public final static int STEP_92 = 92;
public final static int STEP_93 = 93;
public final static int STEP_94 = 94;
public final static int STEP_95 = 95;
public final static int STEP_96 = 96;
public final static int STEP_97 = 97;
public final static int STEP_98 = 98;
public final static int STEP_99 = 99;
/*****************************************/
public final static int STEP_100 = 100;
public final static int STEP_101 = 101;
public final static int STEP_102 = 102;
public final static int STEP_103 = 103;
public final static int STEP_104 = 104;
public final static int STEP_105 = 105;
public final static int STEP_106 = 106;
public final static int STEP_107 = 107;
public final static int STEP_108 = 108;
public final static int STEP_109 = 109;
/*****************************************/
public final static int STEP_110 = 110;
public final static int STEP_111 = 111;
public final static int STEP_112 = 112;
public final static int STEP_113 = 113;
public final static int STEP_114 = 114;
public final static int STEP_115 = 115;
public final static int STEP_116 = 116;
public final static int STEP_117 = 117;
public final static int STEP_118 = 118;
public final static int STEP_119 = 119;
/*****************************************/
public final static int STEP_120 = 120;
public final static int STEP_121 = 121;
public final static int STEP_122 = 122;
public final static int STEP_123 = 123;
public final static int STEP_124 = 124;
public final static int STEP_125 = 125;
public final static int STEP_126 = 126;
public final static int STEP_127 = 127;
public final static int STEP_128 = 128;
public final static int STEP_129 = 129;
/*****************************************/
public final static int STEP_130 = 130;
public final static int STEP_131 = 131;
public final static int STEP_132 = 132;
public final static int STEP_133 = 133;
public final static int STEP_134 = 134;
public final static int STEP_135 = 135;
public final static int STEP_136 = 136;
public final static int STEP_137 = 137;
public final static int STEP_138 = 138;
public final static int STEP_139 = 139;
/*****************************************/
public final static int STEP_140 = 140;
public final static int STEP_141 = 141;
public final static int STEP_142 = 142;
public final static int STEP_143 = 143;
public final static int STEP_144 = 144;
public final static int STEP_145 = 145;
public final static int STEP_146 = 146;
public final static int STEP_147 = 147;
public final static int STEP_148 = 148;
public final static int STEP_149 = 149;
/*****************************************/
public final static int STEP_150 = 150;
public final static int STEP_151 = 151;
public final static int STEP_152 = 152;
public final static int STEP_153 = 153;
public final static int STEP_154 = 154;
public final static int STEP_155 = 155;
public final static int STEP_156 = 156;
public final static int STEP_157 = 157;
public final static int STEP_158 = 158;
public final static int STEP_159 = 159;
/*****************************************/
public final static int STEP_160 = 160;
public final static int STEP_161 = 161;
public final static int STEP_162 = 162;
public final static int STEP_163 = 163;
public final static int STEP_164 = 164;
public final static int STEP_165 = 165;
public final static int STEP_166 = 166;
public final static int STEP_167 = 167;
public final static int STEP_168 = 168;
public final static int STEP_169 = 169;
/*****************************************/
public final static int STEP_170 = 170;
public final static int STEP_171 = 171;
public final static int STEP_172 = 172;
public final static int STEP_173 = 173;
public final static int STEP_174 = 174;
public final static int STEP_175 = 175;
public final static int STEP_176 = 176;
public final static int STEP_177 = 177;
public final static int STEP_178 = 178;
public final static int STEP_179 = 179;
/*****************************************/
public final static int STEP_180 = 180;
public final static int STEP_181 = 181;
public final static int STEP_182 = 182;
public final static int STEP_183 = 183;
public final static int STEP_184 = 184;
public final static int STEP_185 = 185;
public final static int STEP_186 = 186;
public final static int STEP_187 = 187;
public final static int STEP_188 = 188;
public final static int STEP_189 = 189;
/*****************************************/
public final static int STEP_190 = 190;
public final static int STEP_191 = 191;
public final static int STEP_192 = 192;
public final static int STEP_193 = 193;
public final static int STEP_194 = 194;
public final static int STEP_195 = 195;
public final static int STEP_196 = 196;
public final static int STEP_197 = 197;
public final static int STEP_198 = 198;
public final static int STEP_199 = 199;
public final static int STEP_200 = 200;
/*****************************************/
public final static int STEP_201 = 201;
public final static int STEP_202 = 202;
public final static int STEP_203 = 203;
public final static int STEP_204 = 24;
public final static int STEP_205 = 205;
public final static int STEP_206 = 206;
public final static int STEP_207 = 207;
public final static int STEP_208 = 208;
public final static int STEP_209 = 209;
public final static int STEP_210 = 210;
/*****************************************/
public final static int STEP_211 = 211;
public final static int STEP_212 = 212;
public final static int STEP_213 = 213;
public final static int STEP_214 = 214;
public final static int STEP_215 = 215;
public final static int STEP_216 = 216;
public final static int STEP_217 = 217;
public final static int STEP_218 = 218;
public final static int STEP_219 = 219;
public final static int STEP_220 = 220;
/*****************************************/
public final static int STEP_221 = 221;
public final static int STEP_222 = 222;
public final static int STEP_223 = 223;
public final static int STEP_224 = 224;
public final static int STEP_225 = 225;
public final static int STEP_226 = 226;
public final static int STEP_227 = 227;
public final static int STEP_228 = 228;
public final static int STEP_229 = 229;
public final static int STEP_230 = 230;
/*****************************************/
public final static int STEP_231 = 231;
public final static int STEP_232 = 232;
public final static int STEP_233 = 233;
public final static int STEP_234 = 234;
public final static int STEP_235 = 235;
public final static int STEP_236 = 236;
public final static int STEP_237 = 237;
public final static int STEP_238 = 238;
public final static int STEP_239 = 239;
public final static int STEP_240 = 240;
/*****************************************/
public final static int STEP_241 = 241;
public final static int STEP_242 = 242;
public final static int STEP_243 = 243;
public final static int STEP_244 = 244;
public final static int STEP_245 = 245;
public final static int STEP_246 = 246;
public final static int STEP_247 = 247;
public final static int STEP_248 = 248;
public final static int STEP_249 = 249;
/*****************************************/
public final static int STEP_250 = 250;
public final static int STEP_251 = 251;
public final static int STEP_252 = 252;
public final static int STEP_253 = 253;
public final static int STEP_254 = 254;
public final static int STEP_255 = 255;
public final static int STEP_256 = 256;
public final static int STEP_257 = 257;
public final static int STEP_258 = 258;
public final static int STEP_259 = 259;
/*****************************************/
public final static int STEP_260 = 260;
public final static int STEP_261 = 261;
public final static int STEP_262 = 262;
public final static int STEP_263 = 263;
public final static int STEP_264 = 264;
public final static int STEP_265 = 265;
public final static int STEP_266 = 266;
public final static int STEP_267 = 267;
public final static int STEP_268 = 268;
public final static int STEP_269 = 269;
}
package com.fisherbone.fuzhu.abllib;
/**
* 2019/4/21
* 18:33
* Levine
* wechat 1483232332
*/
public class AblSteps {
/**
* 全局静态常量步骤标识1~200不够自己再加从200开始加基本应该都是够的了
*/
public final static int STEP_1 = 1;
public final static int STEP_2 = 2;
public final static int STEP_3 = 3;
public final static int STEP_4 = 4;
public final static int STEP_5 = 5;
public final static int STEP_6 = 6;
public final static int STEP_7 = 7;
public final static int STEP_8 = 8;
public final static int STEP_9 = 9;
/*****************************************/
public final static int STEP_10 = 10;
public final static int STEP_11 = 11;
public final static int STEP_12 = 12;
public final static int STEP_13 = 13;
public final static int STEP_14 = 14;
public final static int STEP_15 = 15;
public final static int STEP_16 = 16;
public final static int STEP_17 = 17;
public final static int STEP_18 = 18;
public final static int STEP_19 = 19;
/*****************************************/
public final static int STEP_20 = 20;
public final static int STEP_21 = 21;
public final static int STEP_22 = 22;
public final static int STEP_23 = 23;
public final static int STEP_24 = 24;
public final static int STEP_25 = 25;
public final static int STEP_26 = 26;
public final static int STEP_27 = 27;
public final static int STEP_28 = 28;
public final static int STEP_29 = 29;
/*****************************************/
public final static int STEP_30 = 30;
public final static int STEP_31 = 31;
public final static int STEP_32 = 32;
public final static int STEP_33 = 33;
public final static int STEP_34 = 34;
public final static int STEP_35 = 35;
public final static int STEP_36 = 36;
public final static int STEP_37 = 37;
public final static int STEP_38 = 38;
public final static int STEP_39 = 39;
/*****************************************/
public final static int STEP_40 = 40;
public final static int STEP_41 = 41;
public final static int STEP_42 = 42;
public final static int STEP_43 = 43;
public final static int STEP_44 = 44;
public final static int STEP_45 = 45;
public final static int STEP_46 = 46;
public final static int STEP_47 = 47;
public final static int STEP_48 = 48;
public final static int STEP_49 = 49;
/*****************************************/
public final static int STEP_50 = 50;
public final static int STEP_51 = 51;
public final static int STEP_52 = 52;
public final static int STEP_53 = 53;
public final static int STEP_54 = 54;
public final static int STEP_55 = 55;
public final static int STEP_56 = 56;
public final static int STEP_57 = 57;
public final static int STEP_58 = 58;
public final static int STEP_59 = 59;
/*****************************************/
public final static int STEP_60 = 60;
public final static int STEP_61 = 61;
public final static int STEP_62 = 62;
public final static int STEP_63 = 63;
public final static int STEP_64 = 64;
public final static int STEP_65 = 65;
public final static int STEP_66 = 66;
public final static int STEP_67 = 67;
public final static int STEP_68 = 68;
public final static int STEP_69 = 69;
/*****************************************/
public final static int STEP_70 = 70;
public final static int STEP_71 = 71;
public final static int STEP_72 = 72;
public final static int STEP_73 = 73;
public final static int STEP_74 = 74;
public final static int STEP_75 = 75;
public final static int STEP_76 = 76;
public final static int STEP_77 = 77;
public final static int STEP_78 = 78;
public final static int STEP_79 = 79;
/*****************************************/
public final static int STEP_80 = 80;
public final static int STEP_81 = 81;
public final static int STEP_82 = 82;
public final static int STEP_83 = 83;
public final static int STEP_84 = 84;
public final static int STEP_85 = 85;
public final static int STEP_86 = 86;
public final static int STEP_87 = 87;
public final static int STEP_88 = 88;
public final static int STEP_89 = 89;
/*****************************************/
public final static int STEP_90 = 90;
public final static int STEP_91 = 91;
public final static int STEP_92 = 92;
public final static int STEP_93 = 93;
public final static int STEP_94 = 94;
public final static int STEP_95 = 95;
public final static int STEP_96 = 96;
public final static int STEP_97 = 97;
public final static int STEP_98 = 98;
public final static int STEP_99 = 99;
/*****************************************/
public final static int STEP_100 = 100;
public final static int STEP_101 = 101;
public final static int STEP_102 = 102;
public final static int STEP_103 = 103;
public final static int STEP_104 = 104;
public final static int STEP_105 = 105;
public final static int STEP_106 = 106;
public final static int STEP_107 = 107;
public final static int STEP_108 = 108;
public final static int STEP_109 = 109;
/*****************************************/
public final static int STEP_110 = 110;
public final static int STEP_111 = 111;
public final static int STEP_112 = 112;
public final static int STEP_113 = 113;
public final static int STEP_114 = 114;
public final static int STEP_115 = 115;
public final static int STEP_116 = 116;
public final static int STEP_117 = 117;
public final static int STEP_118 = 118;
public final static int STEP_119 = 119;
/*****************************************/
public final static int STEP_120 = 120;
public final static int STEP_121 = 121;
public final static int STEP_122 = 122;
public final static int STEP_123 = 123;
public final static int STEP_124 = 124;
public final static int STEP_125 = 125;
public final static int STEP_126 = 126;
public final static int STEP_127 = 127;
public final static int STEP_128 = 128;
public final static int STEP_129 = 129;
/*****************************************/
public final static int STEP_130 = 130;
public final static int STEP_131 = 131;
public final static int STEP_132 = 132;
public final static int STEP_133 = 133;
public final static int STEP_134 = 134;
public final static int STEP_135 = 135;
public final static int STEP_136 = 136;
public final static int STEP_137 = 137;
public final static int STEP_138 = 138;
public final static int STEP_139 = 139;
/*****************************************/
public final static int STEP_140 = 140;
public final static int STEP_141 = 141;
public final static int STEP_142 = 142;
public final static int STEP_143 = 143;
public final static int STEP_144 = 144;
public final static int STEP_145 = 145;
public final static int STEP_146 = 146;
public final static int STEP_147 = 147;
public final static int STEP_148 = 148;
public final static int STEP_149 = 149;
/*****************************************/
public final static int STEP_150 = 150;
public final static int STEP_151 = 151;
public final static int STEP_152 = 152;
public final static int STEP_153 = 153;
public final static int STEP_154 = 154;
public final static int STEP_155 = 155;
public final static int STEP_156 = 156;
public final static int STEP_157 = 157;
public final static int STEP_158 = 158;
public final static int STEP_159 = 159;
/*****************************************/
public final static int STEP_160 = 160;
public final static int STEP_161 = 161;
public final static int STEP_162 = 162;
public final static int STEP_163 = 163;
public final static int STEP_164 = 164;
public final static int STEP_165 = 165;
public final static int STEP_166 = 166;
public final static int STEP_167 = 167;
public final static int STEP_168 = 168;
public final static int STEP_169 = 169;
/*****************************************/
public final static int STEP_170 = 170;
public final static int STEP_171 = 171;
public final static int STEP_172 = 172;
public final static int STEP_173 = 173;
public final static int STEP_174 = 174;
public final static int STEP_175 = 175;
public final static int STEP_176 = 176;
public final static int STEP_177 = 177;
public final static int STEP_178 = 178;
public final static int STEP_179 = 179;
/*****************************************/
public final static int STEP_180 = 180;
public final static int STEP_181 = 181;
public final static int STEP_182 = 182;
public final static int STEP_183 = 183;
public final static int STEP_184 = 184;
public final static int STEP_185 = 185;
public final static int STEP_186 = 186;
public final static int STEP_187 = 187;
public final static int STEP_188 = 188;
public final static int STEP_189 = 189;
/*****************************************/
public final static int STEP_190 = 190;
public final static int STEP_191 = 191;
public final static int STEP_192 = 192;
public final static int STEP_193 = 193;
public final static int STEP_194 = 194;
public final static int STEP_195 = 195;
public final static int STEP_196 = 196;
public final static int STEP_197 = 197;
public final static int STEP_198 = 198;
public final static int STEP_199 = 199;
public final static int STEP_200 = 200;
/*****************************************/
public final static int STEP_201 = 201;
public final static int STEP_202 = 202;
public final static int STEP_203 = 203;
public final static int STEP_204 = 24;
public final static int STEP_205 = 205;
public final static int STEP_206 = 206;
public final static int STEP_207 = 207;
public final static int STEP_208 = 208;
public final static int STEP_209 = 209;
public final static int STEP_210 = 210;
/*****************************************/
public final static int STEP_211 = 211;
public final static int STEP_212 = 212;
public final static int STEP_213 = 213;
public final static int STEP_214 = 214;
public final static int STEP_215 = 215;
public final static int STEP_216 = 216;
public final static int STEP_217 = 217;
public final static int STEP_218 = 218;
public final static int STEP_219 = 219;
public final static int STEP_220 = 220;
/*****************************************/
public final static int STEP_221 = 221;
public final static int STEP_222 = 222;
public final static int STEP_223 = 223;
public final static int STEP_224 = 224;
public final static int STEP_225 = 225;
public final static int STEP_226 = 226;
public final static int STEP_227 = 227;
public final static int STEP_228 = 228;
public final static int STEP_229 = 229;
public final static int STEP_230 = 230;
/*****************************************/
public final static int STEP_231 = 231;
public final static int STEP_232 = 232;
public final static int STEP_233 = 233;
public final static int STEP_234 = 234;
public final static int STEP_235 = 235;
public final static int STEP_236 = 236;
public final static int STEP_237 = 237;
public final static int STEP_238 = 238;
public final static int STEP_239 = 239;
public final static int STEP_240 = 240;
/*****************************************/
public final static int STEP_241 = 241;
public final static int STEP_242 = 242;
public final static int STEP_243 = 243;
public final static int STEP_244 = 244;
public final static int STEP_245 = 245;
public final static int STEP_246 = 246;
public final static int STEP_247 = 247;
public final static int STEP_248 = 248;
public final static int STEP_249 = 249;
/*****************************************/
public final static int STEP_250 = 250;
public final static int STEP_251 = 251;
public final static int STEP_252 = 252;
public final static int STEP_253 = 253;
public final static int STEP_254 = 254;
public final static int STEP_255 = 255;
public final static int STEP_256 = 256;
public final static int STEP_257 = 257;
public final static int STEP_258 = 258;
public final static int STEP_259 = 259;
/*****************************************/
public final static int STEP_260 = 260;
public final static int STEP_261 = 261;
public final static int STEP_262 = 262;
public final static int STEP_263 = 263;
public final static int STEP_264 = 264;
public final static int STEP_265 = 265;
public final static int STEP_266 = 266;
public final static int STEP_267 = 267;
public final static int STEP_268 = 268;
public final static int STEP_269 = 269;
public final static int STEP_270 = 270;
public final static int STEP_271 = 271;
public final static int STEP_272 = 272;
}

View File

@@ -11,6 +11,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -18,13 +19,16 @@ import android.os.Handler;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;
import com.blankj.utilcode.util.ToastUtils;
import com.fisherbone.fuzhu.step.DingDingDakaStep;
import com.fisherbone.fuzhu.utils.SafeToastUtils;
import com.fisherbone.fuzhu.BaseActivity;
import com.derry.wechat.debug.BuildConfig;
@@ -231,72 +235,85 @@ public class MainActivity extends BaseActivity implements InfoMessage {
ChangLiang.isstart = "0";
ChangLiang.isstarttwo = "0";
controlWindow = new ControlWindow(MainActivity.this);
LiveEventBus.get("some_key", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
controlWindow.showRunInfo((String) o);
// LiveEventBus 1.7.3 在 Android 12+ 上有兼容性问题,静态初始化时会崩溃
// 在 Android 12+ 上完全禁用 LiveEventBus避免应用崩溃
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
// Android 12 以下正常使用 LiveEventBus
try {
LiveEventBus.get("some_key", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
controlWindow.showRunInfo((String) o);
}
});
LiveEventBus.get("run_time", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
controlWindow.showRunTimeInfo((String) o);
}
});
LiveEventBus.get("success", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
Log.e("TIAOSHI###", "getcontrolStatus==执行了");
getcontrolStatus();
}
});
LiveEventBus.get("ablservice", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
tvShowInfo.setText((String)o);
if("无障碍服务被销毁".equals((String)o)){
againStart("0");
}
}
});
LiveEventBus.get("jiaguantime", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
Log.w("TIAOSHI###运行时长", (String) o);
//开启运行倒计时
daojishitwo(Integer.parseInt((String) o));
}
});
LiveEventBus.get("closedtime", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
if(mdDisposable!=null) {
mdDisposable.dispose();
}
}
});
LiveEventBus.get("runningstate", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
againStart((String) o);
}
});
LiveEventBus.get("yunkong", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
if("open".equals((String) o)){
}else {
closeService();
}
}
});
LiveEventBus.get("task_runtime", MessageBean.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
MessageBean mesg = (MessageBean) o;
taskDaojishi(mesg.getRuntime(),mesg);
}
});
} catch (Exception e) {
Log.e("MainActivity", "LiveEventBus 初始化失败", e);
}
});
LiveEventBus.get("run_time", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
controlWindow.showRunTimeInfo((String) o);
}
});
LiveEventBus.get("success", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
Log.e("TIAOSHI###", "getcontrolStatus==执行了");
getcontrolStatus();
}
});
LiveEventBus.get("ablservice", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
tvShowInfo.setText((String)o);
if("无障碍服务被销毁".equals((String)o)){
againStart("0");
}
}
});
LiveEventBus.get("jiaguantime", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
Log.w("TIAOSHI###运行时长", (String) o);
//开启运行倒计时
daojishitwo(Integer.parseInt((String) o));
}
});
LiveEventBus.get("closedtime", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
if(mdDisposable!=null) {
mdDisposable.dispose();
}
}
});
LiveEventBus.get("runningstate", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
againStart((String) o);
}
});
LiveEventBus.get("yunkong", String.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
if("open".equals((String) o)){
}else {
closeService();
}
}
});
LiveEventBus.get("task_runtime", MessageBean.class).observe(this, new Observerlife<String>() {
@Override
public void onChanged(Object o) {
MessageBean mesg = (MessageBean) o;
taskDaojishi(mesg.getRuntime(),mesg);
}
});
} else {
// Android 12+ 上禁用 LiveEventBus避免静态初始化崩溃
Log.w("MainActivity", "Android 12+ 上已禁用 LiveEventBus兼容性问题相关功能可能不可用");
}
InjectUtils.injectEvent(this);
requestVersion();
@@ -323,7 +340,7 @@ public class MainActivity extends BaseActivity implements InfoMessage {
.setFindViewMillisInFuture(10000)//寻找界面超时时间
.setFindViewCountDownInterval(200)//寻找界面间隔时间
.build().init();
AblStepHandler.getInstance().initStepClass(new TestAblStep0(), new TestAblStep1(), new TestAblStep2(), new TestAblStep8(), new TestAblStep9(), new TestAblStep10(), new TestAblStep12(), new TestAblStep13(), new TestAblStep18(), new TestAblStep19(), new TestAblStep23(), new TestAblStep25(), new TestAblStep26(), new TestAblStep27(), new TestAblStep28(), new TestAblStep29(), new TestAblStep31(), new TestAblStep32(), new TestAblStep33());
AblStepHandler.getInstance().initStepClass(new TestAblStep0(), new TestAblStep1(), new TestAblStep2(), new TestAblStep8(), new TestAblStep9(), new TestAblStep10(), new TestAblStep12(), new TestAblStep13(), new TestAblStep18(), new TestAblStep19(), new TestAblStep23(), new TestAblStep25(), new TestAblStep26(), new TestAblStep27(), new TestAblStep28(), new TestAblStep29(), new TestAblStep31(), new TestAblStep32(), new TestAblStep33(),new DingDingDakaStep());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.READ_CONTACTS);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
@@ -369,7 +386,7 @@ public class MainActivity extends BaseActivity implements InfoMessage {
}
@OnClickk({R.id.rl_05, R.id.rl_12, R.id.rl_011, R.id.rl_10, R.id.rl_09, R.id.rl_08, R.id.rl_07, R.id.rl_06, R.id.rl_04, R.id.rl_03, R.id.rl_01, R.id.rl_02, R.id.rl_16})
@OnClickk({R.id.rl_05, R.id.rl_12, R.id.rl_011, R.id.rl_10, R.id.rl_09, R.id.rl_08, R.id.rl_07, R.id.rl_06, R.id.rl_04, R.id.rl_03, R.id.rl_01, R.id.rl_02, R.id.rl_16, R.id.rl_17})
public void click(View view) {
if(ProfileSpUtils.getInstance().getSetBean().isRedswitchone()){
SafeToastUtils.showShort(MainActivity.this, "请关闭云端控制");
@@ -398,9 +415,29 @@ public class MainActivity extends BaseActivity implements InfoMessage {
JumpUtils.gotoActivity(MainActivity.this, KeyWordActivity.class, false, "", "");
} else if (id == R.id.rl_01) {
JumpUtils.gotoActivity(MainActivity.this, RecFollowActivity.class, false, "", "");
// JumpUtils.gotoActivity(MainActivity.this, MainActivityy.class, false, "", "");
} else if (id == R.id.rl_16) {
JumpUtils.gotoActivity(MainActivity.this,CancelThumbUpActivity.class, false, "", "");
} else if (id == R.id.rl_17) {
// 钉钉打卡
if (AblUtil.isAccessibilityServiceOpen(MainActivity.this)) {
// 启动钉钉应用并执行打卡操作
executeDingDingDaka();
// 启动自动化打卡流程
// 延迟1秒后发送 STEP_270 消息,让钉钉应用有时间启动
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("TIAOSHI###", "MainActivity: 启动钉钉打卡自动化流程");
// 必须先设置 setStop(false),否则消息不会被处理
AblStepHandler.getInstance().setStop(false);
Log.e("TIAOSHI###", "MainActivity: 已设置 setStop(false),发送 STEP_270 消息");
AblStepHandler.sendMsg(AblSteps.STEP_270);
}
}, 1000);
} else {
SafeToastUtils.showShort(MainActivity.this, "请先开启辅助服务");
AblUtil.openAccessibilitySettings();
}
} else if (id == R.id.rl_02) {
// service.stopSelf();
@@ -563,6 +600,157 @@ public class MainActivity extends BaseActivity implements InfoMessage {
}
}
/**
* 执行钉钉打卡
*/
private void executeDingDingDaka() {
// 钉钉应用包名
String dingDingPackageName = "com.alibaba.android.rimet";
// 钉钉的主 Activity已知的正确启动 Activity
String dingDingMainActivity = "com.alibaba.android.rimet.biz.LaunchHomeActivity";
// 检查钉钉应用是否已安装(可能是权限问题导致检查失败,但应用实际已安装)
boolean isInstalled = isAppInstalled(dingDingPackageName);
if (!isInstalled) {
Log.w("MainActivity", "应用检查失败,但继续尝试启动(可能是权限问题)");
// 不直接返回,继续尝试启动
}
try {
// 方法1: 优先使用系统推荐的方式启动应用getLaunchIntentForPackage
PackageManager packageManager = getPackageManager();
Intent launchIntent = packageManager.getLaunchIntentForPackage(dingDingPackageName);
if (launchIntent != null) {
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(launchIntent);
SafeToastUtils.showShort(MainActivity.this, "正在启动钉钉...");
Log.d("MainActivity", "成功启动钉钉应用(使用 getLaunchIntentForPackage");
} else {
// 方法2: 如果 getLaunchIntentForPackage 返回 null使用已知的 Activity 名称
Log.w("MainActivity", "getLaunchIntentForPackage 返回 null使用已知的 Activity 名称");
Intent intent = new Intent();
intent.setComponent(new ComponentName(dingDingPackageName, dingDingMainActivity));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
SafeToastUtils.showShort(MainActivity.this, "正在启动钉钉...");
Log.d("MainActivity", "成功启动钉钉应用(使用 LaunchHomeActivity");
}
// 启动自动化打卡流程
// 延迟1秒后启动打卡流程让钉钉应用有时间启动
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("TIAOSHI###", "MainActivity: 启动钉钉打卡自动化流程");
try {
AblService.getInstance().startDingDingDaka();
Log.e("TIAOSHI###", "MainActivity: 已调用 AblService.startDingDingDaka()");
} catch (Exception e) {
Log.e("MainActivity", "启动钉钉打卡流程失败", e);
SafeToastUtils.showShort(MainActivity.this, "启动打卡流程失败:" + e.getMessage());
}
}
}, 1000);
} catch (Exception e) {
e.printStackTrace();
Log.e("MainActivity", "启动钉钉失败", e);
SafeToastUtils.showShort(MainActivity.this, "启动钉钉失败:" + e.getMessage());
}
}
/**
* 检查应用是否已安装
* @param packageName 应用包名
* @return true 表示已安装false 表示未安装
*/
private boolean isAppInstalled(String packageName) {
try {
PackageManager packageManager = getPackageManager();
// 方法1: 尝试获取包信息(最简单的方式)
try {
packageManager.getPackageInfo(packageName, 0);
Log.d("MainActivity", "✓ 通过 getPackageInfo 检测到应用已安装: " + packageName);
return true;
} catch (PackageManager.NameNotFoundException e) {
Log.d("MainActivity", "✗ getPackageInfo 未找到应用: " + packageName);
}
// 方法2: 尝试获取启动 Intent更可靠
try {
Intent launchIntent = packageManager.getLaunchIntentForPackage(packageName);
if (launchIntent != null) {
Log.d("MainActivity", "✓ 通过 getLaunchIntentForPackage 检测到应用已安装: " + packageName);
return true;
} else {
Log.d("MainActivity", "✗ getLaunchIntentForPackage 返回 null: " + packageName);
}
} catch (Exception e) {
Log.w("MainActivity", "getLaunchIntentForPackage 异常: " + packageName, e);
}
// 方法3: 尝试查询 Intent 活动
try {
Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);
resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);
resolveIntent.setPackage(packageName);
List<ResolveInfo> apps = packageManager.queryIntentActivities(resolveIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (apps != null && !apps.isEmpty()) {
Log.d("MainActivity", "✓ 通过 queryIntentActivities 检测到应用已安装: " + packageName + ", 找到 " + apps.size() + " 个活动");
return true;
} else {
Log.d("MainActivity", "✗ queryIntentActivities 未找到活动: " + packageName);
}
} catch (Exception e) {
Log.w("MainActivity", "queryIntentActivities 异常: " + packageName, e);
}
// 方法4: 尝试使用 MATCH_UNINSTALLED_PACKAGES需要权限但更全面
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
packageManager.getPackageInfo(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES);
Log.d("MainActivity", "✓ 通过 MATCH_UNINSTALLED_PACKAGES 检测到应用: " + packageName);
return true;
}
} catch (PackageManager.NameNotFoundException e) {
Log.d("MainActivity", "✗ MATCH_UNINSTALLED_PACKAGES 未找到应用: " + packageName);
} catch (Exception e) {
Log.w("MainActivity", "MATCH_UNINSTALLED_PACKAGES 异常: " + packageName, e);
}
Log.w("MainActivity", "⚠ 所有方法都未检测到应用: " + packageName);
return false;
} catch (Exception e) {
Log.e("MainActivity", "检查应用安装状态失败: " + packageName, e);
return false;
}
}
/**
* 打开应用商店(可选功能)
* @param packageName 应用包名
*/
private void openAppStore(String packageName) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=" + packageName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception e) {
// 如果应用商店不可用,尝试使用浏览器打开
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("https://play.google.com/store/apps/details?id=" + packageName));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} catch (Exception ex) {
e.printStackTrace();
}
}
}
private void openService() {
//单击了“bindService”按钮
Intent intent = new Intent(MainActivity.this, TestTwoService.class);
@@ -1397,7 +1585,10 @@ public class MainActivity extends BaseActivity implements InfoMessage {
long b = a - aLong;
long c = 0 + aLong;
Log.e("TIAOSHI###运行剩余时长", b + "");
LiveEventBus.get("run_time").post("任务已运行"+c/60+"分钟");
// Android 12+ 上禁用 LiveEventBus
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
LiveEventBus.get("run_time").post("任务已运行"+c/60+"分钟");
}
}
})
.doOnComplete(new Action() {
@@ -1409,7 +1600,10 @@ public class MainActivity extends BaseActivity implements InfoMessage {
AblViewUtil.potgegin("停止");
break;
case "6":
LiveEventBus.get("some_key").post("任务运行即将结束");
// Android 12+ 上禁用 LiveEventBus
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
LiveEventBus.get("some_key").post("任务运行即将结束");
}
break;
default:

View File

@@ -0,0 +1,212 @@
package com.fisherbone.fuzhu.step;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
import com.fisherbone.fuzhu.abllib.AblService;
import com.fisherbone.fuzhu.abllib.AblStepHandler;
import com.fisherbone.fuzhu.abllib.AblSteps;
import com.fisherbone.fuzhu.abllib.BaseAblStep;
import com.fisherbone.fuzhu.abllib.utils.AblViewUtil;
/**
* 钉钉打卡自动化步骤
* 参考 TestAblStep0.java 的风格
* 流程:
* 1. STEP_270: 等待钉钉应用启动完成
* 2. STEP_271: 在钉钉首页查找并点击"打卡"按钮
* 3. STEP_272: 在打卡页面查找并点击"打卡"按钮
*/
public class DingDingDakaStep extends BaseAblStep {
@Override
public void onStep(int step, Message msg) {
Log.e("TIAOSHI###", "DingDingDakaStep.onStep: 收到步骤 step=" + step + ", Thread=" + Thread.currentThread().getName());
try {
switch (step) {
case AblSteps.STEP_270:
Log.e("TIAOSHI###", "钉钉打卡-步骤1: 等待应用启动");
// 确保 isStop 为 false
if (AblStepHandler.getInstance().isStop()) {
Log.e("TIAOSHI###", "钉钉打卡-步骤1: 检测到 isStop=true重新设置为 false");
AblStepHandler.getInstance().setStop(false);
}
// 使用 postDelayed 直接在当前线程延迟执行,避免 Handler 消息丢失
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("TIAOSHI###", "钉钉打卡-步骤1: postDelayed 回调执行,发送 STEP_271");
// 再次确保 isStop 为 false
if (AblStepHandler.getInstance().isStop()) {
Log.e("TIAOSHI###", "钉钉打卡-步骤1: postDelayed 中检测到 isStop=true重新设置为 false");
AblStepHandler.getInstance().setStop(false);
}
AblStepHandler.sendMsg(AblSteps.STEP_271, 0);
Log.e("TIAOSHI###", "钉钉打卡-步骤1: 已发送 STEP_271 消息(立即执行)");
}
}, 2000);
Log.e("TIAOSHI###", "钉钉打卡-步骤1: 已设置 postDelayed延迟2000ms");
break;
case AblSteps.STEP_271:
// 步骤2: 在钉钉首页查找并点击"打卡"按钮
Log.e("TIAOSHI###", "钉钉打卡-步骤2: STEP_271 开始执行");
Log.e("TIAOSHI###", "钉钉打卡-步骤2: isStop=" + AblStepHandler.getInstance().isStop());
// 确保 isStop 为 false
if (AblStepHandler.getInstance().isStop()) {
Log.e("TIAOSHI###", "钉钉打卡-步骤2: 检测到 isStop=true重新设置为 false");
AblStepHandler.getInstance().setStop(false);
}
Log.e("TIAOSHI###", "钉钉打卡-步骤2: 查找并点击打卡按钮");
if (clickDakaButton("打卡")) {
Log.e("TIAOSHI###", "成功点击打卡按钮,等待跳转到打卡页面");
// 使用 postDelayed 直接延迟执行,避免 Handler 消息丢失
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.e("TIAOSHI###", "钉钉打卡-步骤2: postDelayed 回调执行,发送 STEP_272");
// 再次确保 isStop 为 false
if (AblStepHandler.getInstance().isStop()) {
AblStepHandler.getInstance().setStop(false);
}
AblStepHandler.sendMsg(AblSteps.STEP_272, 0);
Log.e("TIAOSHI###", "钉钉打卡-步骤2: 已发送 STEP_272 消息(立即执行)");
}
}, 2000);
Log.e("TIAOSHI###", "钉钉打卡-步骤2: 已设置 postDelayed延迟2000ms");
} else {
// 尝试其他可能的文本
Log.e("TIAOSHI###", "未找到'打卡',尝试其他文本");
if (clickDakaButton("考勤打卡") || clickDakaButton("工作") || clickDakaButton("工作台")) {
Log.e("TIAOSHI###", "找到并点击了其他入口");
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (AblStepHandler.getInstance().isStop()) {
AblStepHandler.getInstance().setStop(false);
}
AblStepHandler.sendMsg(AblSteps.STEP_272, 0);
}
}, 2000);
Log.e("TIAOSHI###", "钉钉打卡-步骤2: 已设置 postDelayed延迟2000ms");
} else {
Log.e("TIAOSHI###", "未找到打卡入口");
}
}
break;
case AblSteps.STEP_272:
// 步骤3: 在打卡页面查找并点击"打卡"按钮
Log.e("TIAOSHI###", "钉钉打卡-步骤3: STEP_272 开始执行");
Log.e("TIAOSHI###", "钉钉打卡-步骤3: isStop=" + AblStepHandler.getInstance().isStop());
// 确保 isStop 为 false
if (AblStepHandler.getInstance().isStop()) {
Log.e("TIAOSHI###", "钉钉打卡-步骤3: 检测到 isStop=true重新设置为 false");
AblStepHandler.getInstance().setStop(false);
}
Log.e("TIAOSHI###", "钉钉打卡-步骤3: 在打卡页面点击打卡");
if (clickDakaButton("打卡")) {
Log.e("TIAOSHI###", "成功完成打卡操作");
} else {
// 尝试其他可能的打卡按钮文本
if (clickDakaButton("上班打卡") || clickDakaButton("下班打卡") ||
clickDakaButton("外勤打卡") || clickDakaButton("立即打卡")) {
Log.e("TIAOSHI###", "成功完成打卡操作");
} else {
Log.e("TIAOSHI###", "未找到打卡按钮");
}
}
break;
default:
Log.e("TIAOSHI###", "DingDingDakaStep.onStep: 未知步骤 step=" + step);
break;
}
} catch (Exception e) {
Log.e("TIAOSHI###", "DingDingDakaStep.onStep: 执行异常 step=" + step, e);
}
}
/**
* 查找并点击包含指定文本的按钮
* 参考 TestAblStep0.java 的风格,直接遍历节点
*/
private boolean clickDakaButton(String text) {
try {
AccessibilityNodeInfo root = AblService.getInstance().getRootInActiveWindow();
if (root == null) {
Log.e("TIAOSHI###", "无法获取根节点");
return false;
}
// 遍历所有节点查找文本
return findAndClickNode(root, text);
} catch (Exception e) {
Log.e("TIAOSHI###", "查找打卡按钮异常: " + text, e);
return false;
}
}
/**
* 递归查找并点击包含指定文本的节点
* 参考 TestAblStep0.java 的遍历方式
*/
private boolean findAndClickNode(AccessibilityNodeInfo node, String text) {
if (node == null) {
return false;
}
try {
// 检查当前节点的文本和描述
CharSequence nodeText = node.getText();
CharSequence nodeContentDesc = node.getContentDescription();
// 输出节点信息用于调试(参考 TestAblStep0.java
if (nodeText != null || nodeContentDesc != null) {
Log.e("TIAOSHI###", "节点: " + node.getClassName() + " 文本: " + nodeText + " 描述: " + nodeContentDesc);
}
// 检查是否包含目标文本
if ((nodeText != null && nodeText.toString().contains(text)) ||
(nodeContentDesc != null && nodeContentDesc.toString().contains(text))) {
Rect rect = new Rect();
node.getBoundsInScreen(rect);
if (rect.width() > 0 && rect.height() > 0 && node.isEnabled()) {
Log.e("TIAOSHI###", "找到匹配节点: " + text + " 坐标: [" + rect.left + "," + rect.top + "][" + rect.right + "," + rect.bottom + "]");
// 尝试点击节点
if (node.isClickable()) {
boolean clicked = node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
if (clicked) {
Log.e("TIAOSHI###", "成功点击节点: " + text);
return true;
}
} else {
// 如果节点不可点击,使用 performViewClick会自动向上查找可点击的父节点
AblViewUtil.performViewClick(node);
Log.e("TIAOSHI###", "使用 performViewClick 点击: " + text);
return true;
}
}
}
// 递归查找子节点(参考 TestAblStep0.java 的遍历方式)
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (findAndClickNode(child, text)) {
return true;
}
}
} catch (Exception e) {
Log.w("TIAOSHI###", "查找节点异常", e);
}
return false;
}
}

View File

@@ -0,0 +1,60 @@
package com.fisherbone.fuzhu.utils;
import android.content.Context;
import android.os.Build;
/**
* 修复 LiveEventBus 在 Android 12+ 上的 BroadcastReceiver 注册问题
*
* 注意:由于 Android 12+ 的限制,无法通过反射完全修复此问题。
* 实际解决方案:在 MainActivity 中使用 try-catch 包裹所有 LiveEventBus 调用。
*/
public class LiveEventBusFix {
private static boolean isFixed = false;
private static boolean isUnavailable = false;
/**
* 标记 LiveEventBus 为不可用
*/
public static void markAsUnavailable() {
isUnavailable = true;
android.util.Log.w("LiveEventBusFix", "LiveEventBus 已标记为不可用");
}
/**
* 检查 LiveEventBus 是否可用
*/
public static boolean isAvailable() {
return !isUnavailable;
}
/**
* 修复 LiveEventBus 的 BroadcastReceiver 注册问题
* 必须在 Application.onCreate() 中尽早调用,在 LiveEventBus 初始化之前
*
* 注意:由于 Android 12+ 的限制,无法通过反射完全修复此问题。
* 建议在 MainActivity 中使用 try-catch 包裹所有 LiveEventBus 调用。
*
* @param context Application Context
*/
public static void fix(Context context) {
if (isFixed) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12+ 上LiveEventBus 1.7.3 版本存在兼容性问题
// 无法通过反射修复,因为替换 Context 会导致类型转换错误
// 解决方案:在 MainActivity 中使用 try-catch 包裹所有 LiveEventBus 调用
android.util.Log.w("LiveEventBusFix", "⚠ LiveEventBus 1.7.3 在 Android 12+ 上有兼容性问题");
android.util.Log.w("LiveEventBusFix", "建议1) 在 MainActivity 中使用 try-catch 包裹所有 LiveEventBus 调用");
android.util.Log.w("LiveEventBusFix", " 2) 升级 LiveEventBus 到支持 Android 12+ 的版本");
android.util.Log.w("LiveEventBusFix", " 3) 使用其他事件总线库(如 EventBus");
}
isFixed = true; // 标记为已处理,避免重复调用
}
}

View File

@@ -0,0 +1,93 @@
package com.fisherbone.fuzhu.utils;
import android.util.Log;
import com.jeremyliao.liveeventbus.LiveEventBus;
/**
* LiveEventBus 包装类,用于处理 Android 12+ 兼容性问题
* 如果 LiveEventBus 初始化失败,所有调用会静默失败,不会导致崩溃
*/
public class LiveEventBusWrapper {
private static final String TAG = "LiveEventBusWrapper";
private static boolean isAvailable = true;
/**
* 检查 LiveEventBus 是否可用
*/
private static boolean checkAvailable() {
if (!isAvailable) {
return false;
}
try {
// 尝试获取一个测试实例,检查是否初始化成功
LiveEventBus.get("_test_", String.class);
return true;
} catch (Exception e) {
isAvailable = false;
Log.w(TAG, "LiveEventBus 不可用(可能是 Android 12+ 兼容性问题)", e);
return false;
}
}
/**
* 安全地获取 LiveEventBus 实例
*/
public static <T> Object get(String key, Class<T> type) {
if (!checkAvailable()) {
return null;
}
try {
return LiveEventBus.get(key, type);
} catch (Exception e) {
Log.w(TAG, "LiveEventBus.get() 失败: " + key, e);
isAvailable = false;
return null;
}
}
/**
* 安全地获取 LiveEventBus 实例(无类型)
*/
public static Object get(String key) {
if (!checkAvailable()) {
return null;
}
try {
return LiveEventBus.get(key);
} catch (Exception e) {
Log.w(TAG, "LiveEventBus.get() 失败: " + key, e);
isAvailable = false;
return null;
}
}
/**
* 安全地发送消息
*/
public static void post(String key, Object value) {
if (!checkAvailable()) {
return;
}
try {
Object bus = get(key);
if (bus != null) {
// 使用反射调用 post 方法
try {
java.lang.reflect.Method postMethod = bus.getClass().getMethod("post", Object.class);
postMethod.invoke(bus, value);
} catch (Exception e) {
Log.w(TAG, "LiveEventBus.post() 失败: " + key, e);
}
}
} catch (Exception e) {
Log.w(TAG, "LiveEventBus.post() 失败: " + key, e);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.fisherbone.fuzhu.MainActivityy"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="App首页"
android:textColor="@android:color/black"
android:textSize="30sp"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="jumpOrder"
android:text="订单-Order"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="jumpPersonal"
android:text="我的-Personal"
/>
<ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

501
atom项目文档.txt Normal file
View File

@@ -0,0 +1,501 @@
================================================================================
抖音自动化辅助工具 - 项目文档
================================================================================
项目名称fuzhu辅助工具
应用包名com.fisherbone.fuzhu
当前版本1.8.2 (versionCode: 182)
开发语言Java
构建工具Gradle 8.7.3
最低SDK26 (Android 8.0)
目标SDK34 (Android 14)
编译SDK34
================================================================================
一、项目概述
================================================================================
本项目是一个抖音自动化辅助工具,采用组件化架构设计,主要功能包括:
- 抖音账号管理和激活
- 自动化任务执行(关注、取关、点赞、评论等)
- 群控功能(云端/本地控制)
- 设备管理和授权
- 通讯录导入
- 版本更新管理
================================================================================
二、项目架构
================================================================================
2.1 模块结构
------------
项目采用组件化架构,支持独立运行和集成模式切换:
主模块:
- app主应用模块包含核心业务逻辑
- common公共基础库提供通用功能
- order订单模块组件化示例
- personal个人中心模块组件化示例
- wechat微信模块独立功能模块
路由框架模块:
- arouter_annotation路由注解模块
- arouter_api路由API模块
- arouter_compiler路由注解处理器
2.2 架构模式
------------
- 组件化架构:支持各模块独立开发和运行
- 路由框架使用自定义ARouter实现模块间通信
- 依赖注入使用APT技术实现参数自动注入
- 数据绑定使用DataBinding简化UI代码
2.3 构建模式
------------
- 组件化模式isRelease = false各模块可独立运行
- 集成化模式isRelease = true所有模块打包成一个APK
================================================================================
三、核心功能模块
================================================================================
3.1 账号管理模块
---------------
功能:
- 抖音账号登录与激活
- 设备授权(扫码激活)
- 设备注销
- 账号状态管理
关键类:
- SettingActivity系统设置页面
- DouyinDao抖音账号数据访问
- DouyinBean抖音账号实体类
3.2 自动化任务模块
-----------------
功能:
- 推荐加关(自动关注)
- 一键取关
- 点赞、评论、私信
- 粉丝互动
- 大V粉丝截流
- 潜在客户加关
- 直播间加热
- 直播间抢抖币
关键类:
- AblService无障碍服务核心类
- AblStepHandler任务步骤处理器
- step包各种任务步骤实现类
- ChangLiang任务常量配置
3.3 群控功能模块
---------------
功能:
- 云端/本地控制切换
- 多设备管理
- 任务分发与执行
- 设备状态监控
关键类:
- MainActivity主界面任务轮询和状态管理
- TestTwoService后台服务处理任务轮询
- ControlWindow控制浮窗
3.4 无障碍服务模块
-----------------
功能:
- 自动化操作核心
- UI元素查找和操作
- 手势模拟
- 事件监听
关键类:
- AblService无障碍服务主类
- AblUtil无障碍工具类
- AblViewUtil视图操作工具类
- AblConfig无障碍配置
3.5 数据库模块
-------------
功能:
- 本地数据存储
- 账号信息管理
- 任务数据缓存
数据库OrmLite
数据表:
- tab_douyin抖音账号表
- 其他业务数据表
关键类:
- DataBaseHelper数据库帮助类
- DouyinDao抖音账号DAO
- CommentDao评论DAO
- KeyWordDao关键词DAO
- RedenvDao红包DAO
3.6 网络请求模块
---------------
功能:
- 与服务器交互
- 任务获取和提交
- 设备状态上报
网络框架:
- OkGoHTTP请求框架
- RetrofitRESTful API框架
- RxJava响应式编程
关键类:
- NetApi网络请求封装
- HttpConstantsAPI接口常量
- NewsApiInterfaceAPI接口定义
================================================================================
四、项目结构
================================================================================
4.1 主应用模块app结构
------------------------
app/src/main/java/com/fisherbone/fuzhu/
├── activity/ # Activity页面
│ ├── MainActivity.java # 主界面
│ ├── SettingActivity.java # 系统设置
│ ├── DakaActivity.java # 大咖/视频关键词
│ ├── RecFollowActivity.java # 推荐加关
│ ├── CancleFollowActivity.java # 一键取关
│ ├── InteraActivity.java # 粉丝互动
│ ├── DavActivity.java # 大V粉丝截流
│ ├── HongBaoActivity.java # 红包功能
│ ├── LiveActivity.java # 直播间
│ └── ...
├── abllib/ # 无障碍服务库
│ ├── AblService.java # 无障碍服务主类
│ ├── AblStepHandler.java # 步骤处理器
│ ├── AblSteps.java # 步骤常量
│ └── utils/ # 工具类
├── step/ # 任务步骤实现
│ ├── TestAblStep0.java # 步骤0
│ ├── TestAblStep1.java # 步骤1
│ └── ... # 其他步骤
├── db/ # 数据库相关
│ ├── DouyinBean.java # 抖音账号实体
│ ├── KeyWordBean.java # 关键词实体
│ ├── LetterBean.java # 话术实体
│ └── dao/ # 数据访问对象
├── entity/ # 数据实体类
├── api/ # API接口
├── utils/ # 工具类
├── dialog/ # 对话框
├── widget/ # 自定义组件
├── hook/ # Hook相关
└── zxing/ # 二维码扫描
4.2 组件化模块结构
-----------------
personal模块
- Personal_MainActivity个人中心主界面
- Personal_DebugActivity调试界面
order模块
- Order_MainActivity订单模块主界面
- Order_DebugActivity调试界面
wechat模块
- W_MainActivity微信模块主界面
- Login_DebugActivity登录调试界面
- WeChatChannelActivity微信频道界面
================================================================================
五、核心业务流程
================================================================================
5.1 设备激活流程
---------------
1. 用户打开应用
2. 检查设备是否已激活
3. 如果未激活,引导用户扫码
4. 扫码后提交设备信息和抖音账号信息
5. 服务器验证并激活设备
6. 激活成功后可以执行任务
5.2 任务执行流程
---------------
1. 后台服务每5秒轮询一次紧急任务
2. 获取到任务后,根据任务类型执行相应操作
3. 任务类型包括:
- 账号登录任务
- 推荐加关任务
- 一键取关任务
- 撞号任务
- 停止任务
- 紧急任务
- 固定任务
4. 执行完成后,调用接口上报结果
5. 继续轮询下一个任务
5.3 自动化操作流程
---------------
1. 开启无障碍服务
2. 启动目标应用(抖音)
3. 通过无障碍服务查找UI元素
4. 执行点击、滑动等操作
5. 监听页面变化
6. 根据步骤执行相应操作
7. 完成任务后返回结果
================================================================================
六、技术栈
================================================================================
6.1 开发框架
-----------
- Android SDK
- Java 8
- Gradle 8.7.3
- Android Gradle Plugin 8.7.3
6.2 第三方库
-----------
网络请求:
- OkGo 3.0.4
- Retrofit 2.5.0
- RxJava 2.0.1
- RxAndroid 2.0.1
UI框架
- DataBinding
- BaseRecyclerViewAdapterHelper 2.9.30
- ImmersionBar 3.0.0
数据库:
- OrmLite 5.1
工具库:
- UtilCode 1.30.6
- Gson
- EventBus 2.4.0
- LiveEventBus 1.7.3
其他:
- ZXing二维码扫描
- Bugly崩溃上报
- XposedHook框架可选
6.3 自定义框架
-------------
- ARouter自定义路由框架
- 无障碍服务封装AblService及相关工具类
================================================================================
七、配置文件
================================================================================
7.1 构建配置
-----------
- build.gradle根目录构建配置
- app_config.gradle应用配置版本、依赖等
- settings.gradle模块配置
7.2 应用配置
-----------
- AndroidManifest.xml应用清单文件
- proguard-rules.pro混淆规则
- network_security_config.xml网络安全配置
7.3 关键配置项
-------------
服务器地址:
- 测试环境http://139.186.151.48
- 正式环境http://139.186.151.48
应用ID
- 主应用com.fisherbone.fuzhu
- 微信模块com.derry.wechat
命名空间:
- 主应用com.fisherbone.fuzhu
- personal模块com.fisherbone.fuzhu.personal
- order模块com.fisherbone.fuzhu.order
- wechat模块com.derry.wechat
================================================================================
八、API接口说明
================================================================================
8.1 设备相关接口
---------------
- /app/Appscanlogin/scanInfo扫码提交信息
- /app/Appoperate/checkDevice检查设备是否注册
- /app/Appscanlogin/cancellationDevice注销设备
8.2 任务相关接口
---------------
- /app/Appscanlogin/urgentTask获取紧急任务每5秒轮询
- /app/Appscanlogin/fixedTask获取固定任务
- /app/Appscanlogin/updateTaskStatus更新任务状态
- /app/Appscanlogin/FinishTask完成任务上报
8.3 账号相关接口
---------------
- /app/Appscanlogin/sendCode发送验证码
- /app/Appscanlogin/getCode获取验证码
- /app/Appscanlogin/loginResult登录结果上报
- /app/Appscanlogin/touchTask获取撞号账号
- /app/Appscanlogin/touchResult撞号结果上报
8.4 控制相关接口
---------------
- /app/Appoperate/controlStatus获取控制状态
- /app/Appoperate/updateControlStatus更新控制状态
8.5 数据统计接口
---------------
- /app/Appscanlogin/fasStatistics每日数据统计
- /app/Appscanlogin/taskDbTj抖币统计
================================================================================
九、数据库设计
================================================================================
9.1 主要数据表
-------------
tab_douyin抖音账号表
- short_id抖音号
- short_name抖音昵称
- login_status登录状态
其他业务表:
- 关键词表
- 话术表
- 评论表
- 红包表
- 大咖表
9.2 数据访问
-----------
使用OrmLite进行数据访问通过DAO模式封装数据操作。
================================================================================
十、权限说明
================================================================================
应用所需权限:
- 无障碍服务权限(核心功能)
- 悬浮窗权限(控制浮窗)
- 通讯录权限(导入联系人)
- 存储权限(文件读写)
- 网络权限API请求
- 相机权限(扫码激活)
- 位置权限(部分功能需要)
- 电话权限(设备信息获取)
================================================================================
十一、开发注意事项
================================================================================
11.1 组件化开发
--------------
- 独立运行时R类使用模块命名空间
- AndroidManifest.xml中不能使用package属性AGP 8.0+
- 有intent filter的组件必须设置android:exported属性Android 12+
11.2 无障碍服务
--------------
- 需要用户手动开启无障碍服务
- 不同Android版本的行为可能不同
- 需要适配不同手机型号的UI差异
11.3 任务执行
--------------
- 任务执行是异步的,需要处理各种异常情况
- 需要合理控制操作频率,避免被平台检测
- 需要处理网络异常、应用崩溃等异常情况
11.4 代码规范
--------------
- 使用统一的代码风格
- 关键功能需要添加日志
- 异常情况需要妥善处理
================================================================================
十二、版本历史
================================================================================
当前版本1.8.2 (versionCode: 182)
主要功能:
- 抖音自动化操作
- 群控功能
- 设备管理
- 任务调度
================================================================================
十三、项目依赖关系
================================================================================
app模块依赖
- common模块
- arouter_annotation模块
- arouter_compiler注解处理器
集成模式下还依赖:
- order模块
- personal模块
- wechat模块
common模块
- 公共基础库,提供通用功能
================================================================================
十四、构建和运行
================================================================================
14.1 构建命令
------------
- 组件化模式构建isRelease = false
- 集成化模式构建isRelease = true
14.2 运行要求
------------
- JDK 11或更高版本AGP 8.7.3要求)
- Android SDK 34
- Gradle 8.9
14.3 签名配置
------------
- 签名文件app/fuzhu.jks
- 签名密码123456
- Key别名fuzhu
================================================================================
十五、常见问题
================================================================================
15.1 编译问题
------------
- R类找不到检查命名空间配置和导入路径
- Manifest合并失败检查android:exported属性
- 包名冲突:检查命名空间配置
15.2 运行时问题
------------
- 无障碍服务未开启:引导用户开启
- 任务执行失败:检查日志和网络连接
- 设备未激活:引导用户扫码激活
================================================================================
十六、联系方式
================================================================================
项目维护fisherbone团队
服务器地址http://139.186.151.48
================================================================================
文档生成时间2026-01-05
文档版本1.0
================================================================================

25
doc/1.01版本方向 Normal file
View File

@@ -0,0 +1,25 @@
开发atom1.0版本
1. 推荐加关:关注抖音【推荐】中的用户;
2. 同城加关:关注抖音指定【同城】中的用户;
3. 关键词 加关:对搜索指定关键词中的【用户】加关;
4. 智能加关(后台群控):对后台指定的一批抖音用户进行加关;
5. 私信(后台群控):对后台指定的一批抖音用户进行私信(文字、图片、作品);
6. 一键取关:取消已关的粉丝;
7. 话术设置:私信话术、直播话术、回访话术、首次关注话术;
1.01版本阿童木:
1.稳定性,无法长时间运行。
2.推荐加关,运行时长,单位改为分钟。
3.粉丝、关注增加区间设置。
4.广告不需要进详情。
5.无障碍模式出错。
6.退出、重新开始时,需要重新打开无障碍。(无障碍已打开)
7.悬浮窗消失(养号功能还在继续跑)
8.一键取关,取关数无效。
9.一键取关取消互关点击后弹窗false
10.一键取关手动停止第二次运行点开始后无反应需要重新打开APP。
11.一键取关:取消互关,卡死在【我】的页面
12.多次点击开始任务,会弹出多个悬浮窗。

18
doc/1.1版本 Normal file
View File

@@ -0,0 +1,18 @@
ATOM v.1.1
修改:
1. 推荐关键:
a. 视频观看时长改为2个输入框
b. 加关关注数、加关粉丝数、最小作品数改为范围设置;
c. 取消最大作品时间;
2. 一键取关:可以多选已关、互关,此时不做条件判断,依次取关;
3. 话术设置每种话术默认设置10条最少不能少于10条
4. 多个账号同组同手机,执行任务时每次随机账号顺序执行;
新增:
5. 关键词加关:设置指定关键词后,抖音搜索关键词,浏览视频、加关;
6. 同城加关:设置城市后,抖音切换该城市后,在该城市下浏览、加关;
7. 粉丝互动:给自己的粉丝批量发私信(文字、链接、图片);
8. 大V粉截流
a. 指定抖音账号,在该抖音账号的粉丝中添加关注;
b. 设置屏蔽关键词,不加关含有关键词的粉丝;
c. 不加关默认头像、默认用户名的粉丝;

16
doc/14.6版本适配 Normal file
View File

@@ -0,0 +1,16 @@
1.推荐加关可以正常使用
2.获取当前抖音帐号可以正常使用(修改地方)
// if ("dmt.viewpager.DmtViewPager$d".equals(child.getClassName())) {//抖音版本13.1写法
if ("dmt.viewpager.DmtViewPager$MyAccessibilityDelegate".equals(child.getClassName())) {
3.退出抖音可以正常使用
4.关机重启可以正常使用
5.一键取关取消已关功能正常使用(修改地方)
// if ("dmt.viewpager.DmtViewPager$d".equals(child.getClassName())) {//13.1版本写法
if ("dmt.viewpager.DmtViewPager$MyAccessibilityDelegate".equals(child.getClassName())) {
6.一键取关取消互关功能正常使用(修改地方)
// if ("dmt.viewpager.DmtViewPager$d".equals(child.getClassName())) {//13.1版本写法
if ("dmt.viewpager.DmtViewPager$MyAccessibilityDelegate".equals(child.getClassName())) {
7.帐号切换可以正常使用
8.导入通讯录可以正常使用
9.

39
doc/README.md Normal file
View File

@@ -0,0 +1,39 @@
# fuzhu
#### 介绍
{**以下是 Gitee 平台说明,您可以替换此简介**
Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN。专为开发者提供稳定、高效、安全的云端软件开发协作平台
无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
#### 软件架构
软件架构说明
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
#### 使用说明
1. xxxx
2. xxxx
3. xxxx
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

7
doc/atom操作手册 Normal file
View File

@@ -0,0 +1,7 @@
1.开允许在其他应用上显示权限
2.打开辅助
3.激活
adb shell pm grant com.fisherbone.fuzhu android.permission.WRITE_SECURE_SETTINGS
4.开云端群控
5.云端群控需点击环境检测

88
doc/基类代码 Normal file
View File

@@ -0,0 +1,88 @@
/**
* 点击直播间下方的更多按钮
* 1.说点什么
* 2.更多
* 3.礼物
*/
private void clickmore(String type) {
accessibilityNodeInfos = new ArrayList<>();
AccessibilityNodeInfo root = AblService.getInstance().getRootInActiveWindow();
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = root.getChild(i);
// Log.e("TIAOSHI###", "----oneNode:" + child.getClassName() + ":" + child.getText() + ":" + child.getContentDescription());
for (int y = 0; y < child.getChildCount(); y++) {
AccessibilityNodeInfo child1 = child.getChild(y);
// Log.e("TIAOSHI###", "----twoNode:" + child1.getClassName() + ":" + child1.getText() + ":" + child1.getContentDescription());
switch (type) {
case "1"://说点什么
if ("android.widget.TextView".equals(child1.getClassName())) {
if (child1.getText() != null) {
if (child1.getText().toString().equals("说点什么...")) {
AblService.getInstance().clickcommontwo(child1, "");
}
}
}
break;
case "2"://更多
if ("android.widget.Button".equals(child1.getClassName())) {
if (child1.getContentDescription().equals("更多")) {
AblService.getInstance().clickcommontwo(child1, "");
}
}
break;
case "3"://礼物
if ("android.widget.Button".equals(child1.getClassName())) {
if (child1.getContentDescription().equals("礼物")) {
AblService.getInstance().clickcommontwo(child1, "");
}
}
break;
}
}
}
}
/**
* 分享
* 1为分享 2为私信朋友
*/
private void share(String type) {
accessibilityNodeInfos = new ArrayList<>();
AccessibilityNodeInfo root = AblService.getInstance().getRootInActiveWindow();
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = root.getChild(i);
// Log.e("TIAOSHI###", "----oneNode:" + child.getClassName() + ":" + child.getText() + ":" + child.getContentDescription());
for (int y = 0; y < child.getChildCount(); y++) {
AccessibilityNodeInfo child1 = child.getChild(y);
// Log.e("TIAOSHI###", "----twoNode:" + child1.getClassName() + ":" + child1.getText() + ":" + child1.getContentDescription());
for (int j = 0; j < child1.getChildCount(); j++) {
AccessibilityNodeInfo child2 = child1.getChild(j);
// Log.e("TIAOSHI###", "----threeNode:" + child2.getClassName() + ":" + child2.getText() + ":" + child1.getContentDescription());
for (int x = 0; x < child2.getChildCount(); x++) {
AccessibilityNodeInfo child3 = child2.getChild(x);
// Log.e("TIAOSHI###", "----fourNode:" + child3.getClassName() + ":" + child3.getText() + ":" + child1.getContentDescription());
switch (type) {
case "1":
if ("android.widget.TextView".equals(child3.getClassName())) {
if (child3.getText() != null) {
if (child3.getText().toString().equals("分享")) {
AblService.getInstance().clickcommontwo(child3, "");
}
}
}
break;
case "2":
if ("android.widget.TextView".equals(child3.getClassName())) {
if (child3.getText() != null) {
if (child3.getText().toString().equals("私信朋友")) {
AblService.getInstance().clickcommontwo(child3, "");
}
}
}
break;
}
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
https://zhuanlan.zhihu.com/p/263414387
https://blog.csdn.net/weixin_42474371/article/details/104405463
第一步:
<uses-permission android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />
第二部:
Settings.Secure.putString(getContentResolver(),Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.fisherbone.fuzhu.abllib/AblService");
Settings.Secure.putString(getContentResolver(),Settings.Secure.ACCESSIBILITY_ENABLED, "0");//1表示开启

92
doc/工作日志 Normal file
View File

@@ -0,0 +1,92 @@
1.修改了推荐加关的参数
2.项目启动提示添加评论话术
3.一键取关参数调整
4.添加视频观看时长
5.详情页回关判断直接返回
6.默认评论话术
7.添加关注太快了,先休息一下,直接让停止继续加关注
8.推荐加关流程优化
9.运行稳定性
10.完成了ATOM版本自动更新,并处理了抖音的一些弹框。
______________________________________________________________________
测试帐号登录
验证码故意输错返回后重新授权ChangLiang.task_type = "";则会重新领取任务
loginResult()方法的成功回调中
ChangLiang.task_type = "";
ChangLiang.istask = false;
ChangLiang.isstart="0";
实现闭环登录
版本: 1.0.1
TIAOSHI: 有弹出==软件版本更新==classname==android.app.AlertDialog
TIAOSHI: 有弹出软件版本更新
TIAOSHILatest Toast Message:: 软件版本更新 [Source: com.fisherbone.fuzhu]
TIAOSHI: 有弹出==应用包管理组件==classname==com.android.packageinstaller.PackageInstallerActivity
TIAOSHI: 有弹出应用包管理组件
TIAOSHILatest Toast Message:: 应用包管理组件 [Source: com.miui.packageinstaller]
TIAOSHI: 有弹出==应用包管理组件==classname==com.android.packageinstaller.InstallAppProgress
TIAOSHI: 有弹出应用包管理组件
_________________________________________________________________________________________________
E/TIAOSHI: 有弹出==抖音
E/TIAOSHI: 有弹出抖音
E/TIAOSHI: 有弹出==当前为非Wi-Fi环境请注意流量消耗
E/TIAOSHI: 有弹出当前为非Wi-Fi环境请注意流量消耗
E/TIAOSHI: 有弹出==为呵护未成年人健康成长,抖音推出儿童/青少年模式。该模式下部分功能无法正常使用。请监护人主动选择,并设置监护密码。《儿童/青少年使用须知》详情
_______________这个点击系统返回键不起作用
E/TIAOSHI: 有弹出==发现通讯录好友==classname==com.ss.android.ugc.aweme.main.dw
E/TIAOSHI: 有弹出发现通讯录好友
E/TIAOSHILatest Toast Message:: 发现通讯录好友 [Source: com.ss.android.ugc.aweme]
E/TIAOSHI: 有弹出==个人信息保护指引==classname==com.ss.android.ugc.aweme.main.dg
E/TIAOSHI: 有弹出个人信息保护指引
E/TIAOSHILatest Toast Message:: 个人信息保护指引 [Source: com.ss.android.ugc.aweme]
——————————————————————————————可以使用系统返回键关闭
E/TIAOSHI: 有弹出==好友推荐==classname==android.app.Dialog
E/TIAOSHI: 有弹出好友推荐
E/TIAOSHILatest Toast Message:: 好友推荐 [Source: com.ss.android.ugc.aweme]
_______________这个点击系统返回键不起作用
E/TIAOSHI: 有弹出==检测到更新==classname==com.ss.android.ugc.aweme.update.a.a
E/TIAOSHI: 有弹出检测到更新
E/TIAOSHILatest Toast Message:: 检测到更新 [Source: com.ss.android.ugc.aweme]
_____________________________________________________________可以使用系统返回键关闭
E/TIAOSHI: 有弹出==为呵护未成年人健康成长,抖音推出儿童/青少年模式。该模式下部分功能无法正常使用。请监护人主动选择,并设置监护密码。《儿童/青少年使用须知》详情==classname==androidx.appcompat.app.AlertDialog
_____________________________________________________________个人信息保护指引系统返回键不起作用
E/TIAOSHI: 有弹出==个人信息保护指引==classname==com.ss.android.ugc.aweme.main.dg
E/TIAOSHI: 有弹出个人信息保护指引
E/TIAOSHILatest Toast Message:: 个人信息保护指引 [Source: com.ss.android.ugc.aweme]
_________________________________________________________________________________
有弹出==抖音==classname==com.ss.android.ugc.aweme.profile.ui.UserProfileActivity
有弹出抖音
E/TIAOSHI: 有弹出==确认取消关注?==classname==com.google.android.material.bottomsheet.BottomSheetDialog
E/TIAOSHI: 有弹出确认取消关注?
E/TIAOSHILatest Toast Message:: 确认取消关注? [Source: com.ss.android.ugc.aweme]

129
doc/异常处理 Normal file
View File

@@ -0,0 +1,129 @@
1.访问接口出错
java.net.UnknownServiceException: CLEARTEXT communication to * not permitted by network
在Android9版以上会发生如下异常
java.net.UnknownServiceException: CLEARTEXT communication to * not permitted by network
原因是Android9版本默认不允许http访问他们认为http是不安全的只能用https
解决办法
1请求修改为https
2targetSdkVersion 降到27以下
3在 res 下新增一个 xml 目录然后创建一个名为network_security_config.xml 文件(名字自定) 内容如下大概意思就是允许开启http请求
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
   <base-config cleartextTrafficPermitted="true" />
</network-security-config>
在AndroidManifest.xml的Application中添加配置引用
<application
   ..
  android:networkSecurityConfig="@xml/network_security_config"
  ... >
------------------------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------------------------------------------------------------------------------------------------------
2.Android-Android 10 创建不了文件夹
尝试了很久AndroidManifest已加了权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
也动态申请了读写权限但是创建文件夹一直失败而运行以前的工程没有问题最后发现在AndroidManifest中配置一下android:requestLegacyExternalStorage="true" 搞定!!
<application
...
android:requestLegacyExternalStorage="true" // 这样就可以采用原有的存储策略
android:theme="@style/AppTheme">
更为诡异的是这时你再把这个属性去掉把apk卸载重装它都有权限创建了
------------------------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------------------------------------------------------------------------------------------------------
3.解决Android10 imei不能获取的问题适配Android 10
https://blog.csdn.net/zqd1984309864/article/details/107410758?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.control
------------------------------------------------------------------------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------------------------------------------------------------------------------------------------------
4.处理账号登录过程中弹出的推荐好友弹框
/**
* 关闭弹框
* @param instance
*/
public void closedDialog(AblService instance) {
//检查是否有弹框,并关闭所有弹窗
//处理发现通讯好好友的弹框
AccessibilityNodeInfo liebiaokong = AblViewUtil.findByText("好友推荐", 0);
if (liebiaokong != null) {
Log.e("TIAOSHI###", "弹出了好友推荐的弹框");
AccessibilityNodeInfo parent = liebiaokong.getParent();
AccessibilityNodeInfo child = parent.getChild(2);
instance.clickcommontwo(child, "关闭好友推荐弹框");
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------------------------------------------------------------------------------------------------------
5.getText().toString() //要做空指针检测
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
------------------------------------------------------------------------------------------------------------------------------------------
6.字符串中去除表情符号
String s = EmojiFilter.filterEmoji(s1);//去除字符串中包含的表情符号
Log.e("提交的字符串(不包含表情符号)", s);
7.文本框自动输入功能
AccessibilityNodeInfo自动输入
在AccessibilityNoteInfo中实现自动输入时有两种不同的方法
1.在android版本>21时可以使用两种第一种是AccessibilityService自带的方法
Bundle arguments = new Bundle();
arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, Constents.mobile[mobile_j]);
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
2.ClipboardManager clipboard = (ClipboardManager)this.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("text", "15066306736");
clipboard.setPrimaryClip(clip);
//焦点n是AccessibilityNodeInfo对象
info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
粘贴进入内容
info.performAction(AccessibilityNodeInfo.ACTION_PASTE);
8.在悬浮窗上显示运行的信息时候,会有延迟的原因是因为线程等待,将线程等待的代码写在发送消息的方法行面后正常,或者写在下一个步骤上也可以实现同步。
compileSdkVersion 29
defaultConfig {
applicationId "com.fisherbone.fuzhu"
minSdkVersion 24
targetSdkVersion 29
versionCode 121
versionName "1.2.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.myapplication"
minSdkVersion 16
targetSdkVersion 30
versionCode 121
versionName "1.2.1"
multiDexEnabled true //突破应用方法数65535的一个限制
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

66
doc/异常检测 Normal file
View File

@@ -0,0 +1,66 @@
1.轮询服务检测Kathy
2.调试浮窗 showFloatingWindow()
3. /**
* 撞号任务完成的通知
* @param str
*/
@Override
public void mesagezhuang(String str) {
tv_run_info.setText(str);
if(str.equals("停止")){
AblStepHandler.getInstance().setStop(true);
touchResult();
stutas(true, tv_1, tv_2);
}
}
(server)' ~ Channel is unrecoverably broken and will be disposed!
2021-03-23 16:47:10.470 4153-4163/? I/droid.ugc.awem: Background concurrent copying GC freed 454215(22MB) AllocSpace objects, 54(1504KB) LOS objects, 48% free, 25MB/49MB, paused 290us total 362.688ms
2021-03-23 16:47:11.023 1457-2525/? I/ActivityManager: Force stopping com.fisherbone.fuzhu appid=10313 user=0: from process:7884
2021-03-23 16:47:11.024 1457-2525/? I/ActivityManager: Force finishing activity ActivityRecord{3b7d9c1 u0 com.fisherbone.fuzhu/.MainActivity t2146}
2021-03-23 16:47:11.027 1457-2525/? I/ActivityManager: Force finishing activity ActivityRecord{8e26758 u0 com.fisherbone.fuzhu/.activity.SettingActivity t2146}
2021-03-23 16:47:11.029 1457-2525/? I/ActivityManager: Force stopping service ServiceRecord{6fd05e6 u0 com.fisherbone.fuzhu/.abllib.AblService}
___________________________________________________________________________________________________________________
内存优化第一招https://blog.csdn.net/u013110200/article/details/100080824
1未设定属性android:largeheap = "true"时可以申请到的最大内存空间为221M。
     2设定属性android:largeheap = "true"时, 可以申请的最大内存空间为478M是原来的两倍多一些。
2021-03-24 14:23:10.016 1457-1521/? W/BroadcastQueue: Timeout of broadcast BroadcastRecord{4261d0 u-1 android.intent.action.TIME_TICK} - receiver=android.os.BinderProxy@62898f0, started 10000ms ago
2021-03-24 14:23:10.016 1457-1521/? W/BroadcastQueue: Receiver during timeout of BroadcastRecord{4261d0 u-1 android.intent.action.TIME_TICK} : BroadcastFilter{e6330ee u0 ReceiverList{7826e69 7125 com.fisherbone.fuzhu/10313/u0 remote:62898f0}}
2021-03-24 14:23:10.055 3094-3094/? I/Timeline: Timeline: Activity_launch_request time:20206285
2021-03-24 14:23:10.059 1457-2090/? I/ActivityManager: START u0 {dat=snssdk1128://user/profile pkg=com.ss.android.ugc.aweme cmp=com.ss.android.ugc.aweme/.profile.ui.UserProfileActivity (has extras)} from uid 10319
2021-03-24 14:23:10.065 1457-2090/? D/CompatibilityInfo: mCompatibilityFlags - 0
2021-03-24 14:23:10.066 1457-2090/? D/CompatibilityInfo: applicationDensity - 320
2021-03-24 14:23:10.066 1457-2090/? D/CompatibilityInfo: applicationScale - 1.0
2021-03-24 14:23:10.066 1457-2090/? D/ActivityTrigger: ActivityTrigger activityPauseTrigger
2021-03-24 14:23:10.081 1457-1521/? I/ActivityManagerServiceInjector: finish silent ANR: ProcessRecord{a6256d5 7125:com.fisherbone.fuzhu/u0a313}
2021-03-24 14:23:10.081 1457-1521/? I/ActivityManager: Killing 7125:com.fisherbone.fuzhu/u0a313 (adj 100): bg anr
2021-03-24 14:23:10.082 2949-3567/? D/PowerKeeper.Event: notifyActiveKilled processName: com.fisherbone.fuzhu, pid:7125, reason:bg anr
2021-03-24 14:23:10.082 1457-1526/? W/libprocessgroup: kill(-7125, 9) failed: No such process
2021-03-24 14:23:10.094 1457-1526/? I/chatty: uid=1000(system) ActivityManager identical 2 lines
2021-03-24 14:23:10.099 1457-1526/? W/libprocessgroup: kill(-7125, 9) failed: No such process
2021-03-24 14:23:10.100 1457-3397/? D/CompatibilityInfo: mCompatibilityFlags - 0
2021-03-24 14:23:10.100 1457-3397/? D/CompatibilityInfo: applicationDensity - 320
2021-03-24 14:23:10.100 1457-3397/? D/CompatibilityInfo: applicationScale - 1.0
2021-03-24 14:23:10.104 1457-1526/? W/libprocessgroup: kill(-7125, 9) failed: No such process
2021-03-24 14:23:10.108 1457-3397/? I/Timeline: Timeline: App_transition_ready time:20206337
2021-03-24 14:23:10.110 1457-1526/? W/libprocessgroup: kill(-7125, 9) failed: No such process
2021-03-24 14:23:10.110 3094-3094/? W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@66063ae
2021-03-24 14:23:10.116 1457-1526/? W/libprocessgroup: kill(-7125, 9) failed: No such process
2021-03-24 14:23:10.120 3094-3265/? D/ViewContentFactory: initViewContentFetcherClass
2021-03-24 14:23:10.120 3094-3265/? I/ContentCatcher: ViewContentFetcher : ViewContentFetcher
2021-03-24 14:23:10.120 3094-3265/? D/ViewContentFactory: createInterceptor took 0ms
2021-03-24 14:23:10.122 1457-1526/? W/libprocessgroup: kill(-7125, 9) failed: No such process
异常弹框的包名
com.bytedance.android.livesdk.dislike.b

31
doc/技术代码 Normal file
View File

@@ -0,0 +1,31 @@
1.左滑动操作
// AblViewUtil.onMessage("不满足点赞关注评论条件");
// AblViewUtil.scrollHorizontal(10, 500, 100, 300, new GestureCallBack() {
// @Override
// public void succ(GestureDescription gestureDescription) {
//
// }
//
// @Override
// public void fail(GestureDescription gestureDescription) {
//
// }
// });
2.坐标点点击
AblService.getInstance().clickPoint(659, 666, 300);//红米7a
@RequiresApi(api = Build.VERSION_CODES.N)
public void clickcommontwo(AccessibilityNodeInfo accessibilityNodeInfo, String s) {
if (accessibilityNodeInfo!=null) {
Rect rect = new Rect();
accessibilityNodeInfo.getBoundsInScreen(rect);
this.clickPoint((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2, 300);
Log.e("TIAOSHI###", "点击"+s+"按钮"+ "("+ (rect.left + rect.right) / 2 + "," + (rect.top + rect.bottom) / 2 + ")");
// ToastUtils.showShort("点击"+s+"按钮"+ "("+ (rect.left + rect.right) / 2 + "," + (rect.top + rect.bottom) / 2 + ")");
} else {
Log.e("TIAOSHI###", "没有找到"+s+"按钮,直接退出了。。。。");
return;
}
}

395
doc/技术文档 Normal file
View File

@@ -0,0 +1,395 @@
一、三种常用的定时器
1.Handler类的postDelayed方法
final Handler mHandler = new Handler();
Runnable r = new Runnable() {
@Override
public void run() {
//do something
//每隔1s循环执行run方法
mHandler.postDelayed(this, 1000);
}
};
//主线程中调用:
mHandler.postDelayed(r, 100);//延时100毫秒
2.用handler+timer+timeTask方法
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
//do something
}
super.handleMessage(msg);
}
};
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
//主线程中调用:
timer.schedule(timerTask, 1000, 500);//延时1s每隔500毫秒执行一次run方法
3.Thread+handler方法
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
//do something
}
super.handleMessage(msg);
}
};
class MyThread extends Thread {//这里也可用Runnable接口实现
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);//每隔1s执行一次
Message msg = new Message();
msg.what = 1;
handler.sendMessage(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//主线程中调用:
new Thread(new MyThread()).start();
4.ScheduledThreadPoolExecutor方法
private ScheduledThreadPoolExecutor mThreadPoolExecutor;
if (mThreadPoolExecutor != null) {
mThreadPoolExecutor.shutdownNow();
}
mThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);
mThreadPoolExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
}
}, 0, 500, TimeUnit.MILLISECONDS);
二、三种延时的快捷方法:
1.Handler的postDelayed方法
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//do something
}
}, 1000); //延时1s执行
2.timer + TimerTask方法
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//do something
}
},1000);//延时1s执行
3.Thread方法
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);//延时1s
//do something
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
自带定时器 CountDownTimer
利用 CountDownTimer 实现一个时长为 5 秒的 View 倒计时显示,要求从 5s 开始每隔 1 秒倒计时显示到 1s
CountDownTimer countDownTimer = new CountDownTimer(totalTime, 2000) {
@Override
public void onTick(long millisUntilFinished) {
updateFileData(fileId); //执行任务
}
@Override
public void onFinish() {
if (User.getInstance().isLoginIng()) {
if (countDownTimer != null) {
countDownTimer.start();
}
}
}
};
countDownTimer.start();
三、定时轮询
为什么使用Service
普通的线程也可以达到在后台做事情的功能,那么为什么使用 Service呢是因为 Service是系统的组件它的优先级比普通的线程要高
不容易被系统回收。而且 线程不好控制Service相对好控制一些。运行在前台的Activity是不会被系统回收 的而Service如果不想被
系统回收就需要在Service中设置一下
startForeground(int,Notification)
具体的使用场景有:
a、拥有长连接QQ
b、定时轮询
c、服务里面注册广播接收者。有些广播接收者只能通过代码注册比如屏幕锁屏、 屏幕解锁、电量发生变化等。
e、IntentService
Android RxJava 实际应用讲解:(无条件)网络请求轮询
https://www.jianshu.com/p/11b3ec672812
四、沉浸式框架implementation 'com.hannesdorfmann.mosby3:mvp:3.0.0-alpha4'
五、获取结点元素 方式一:
// 遍历root下的子节点有哪些
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = root.getChild(i);
Log.e("TIAOSHI###", "----oneNode:" + child.getClassName() + ":" + child.getText() + ":" + child.getContentDescription());
if ("dmt.viewpager.DmtViewPager$d".equals(child.getClassName())) {
Log.e("TIAOSHI###ViewPager", "----twoNode:" + child.getChildCount());
for (int y = 0; y < child.getChildCount(); y++) {
AccessibilityNodeInfo child1 = child.getChild(y);
Log.e("TIAOSHI###", "----twoNode:" + child1.getClassName() + ":" + child1.getText() + ":" + child1.getContentDescription());
if ("android.widget.TabHost".equals(child1.getClassName())) {
for (int j = 0; j < child1.getChildCount(); j++) {
AccessibilityNodeInfo child2 = child1.getChild(j);
Log.e("TIAOSHI###", "----threeNode:" + child2.getClassName() + ":" + child2.getText());
}
}
}
}
}
六、横向滑动
AblViewUtil.scrollHorizontal(10,600,100,600, new GestureCallBack() {
@Override
public void succ(GestureDescription gestureDescription) {
}
@Override
public void fail(GestureDescription gestureDescription) {
}
});
七、停止服务
AblStepHandler.getInstance().setStop(true);
八、定时器
private void daojishizreo(int a) {
//启动计时器
//从0开始发射11个数字为0-10依次输出延时0s执行每1s发射一次。
mdDisposable = Flowable.intervalRange(0, a + 1, 0, 1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
long b = a - aLong;
}
})
.doOnComplete(new Action() {
@Override
public void run() throws Exception {
AblStepHandler.getInstance().setStop(true);
Log.e("TIAOSHI###", "停止了");
}
})
.subscribe();
}
九、for循环找控件
/**
* 寻找留下你的精彩评论
*/
private AccessibilityNodeInfo findcoment() {
AccessibilityNodeInfo infooos=null;
AccessibilityNodeInfo rootttt = AblService.getInstance().getRootInActiveWindow();
for (int j = 0; j < rootttt.getChildCount(); j++) {
AccessibilityNodeInfo child = rootttt.getChild(j);
Log.e("TIAOSHI###", "----1Node:" + child.getClassName() + ":" + child.getText());
if (!"dmt.viewpager.DmtViewPager$d".equals(child.getClassName())) {
// Log.e("TIAOSHI###ViewPager", "----2Node:" + child.getChildCount());
for (int y = 0; y < child.getChildCount(); y++) {
AccessibilityNodeInfo child1 = child.getChild(y);
Log.e("TIAOSHI###", "----2Node:" + child1.getClassName() + ":" + child1.getText());
// if ("android.widget.LinearLayout".equals(child1.getClassName())) {
for (int x = 0; x < child1.getChildCount(); x++) {
AccessibilityNodeInfo child6 = child1.getChild(x);
Log.e("TIAOSHI###", "----3Node:" + child6.getClassName() + ":" + child6.getText());
for (int z = 0; z < child6.getChildCount(); z++) {
AccessibilityNodeInfo child7 = child6.getChild(z);
// Log.e("TIAOSHI###", "----4Node:" + child7.getClassName() + ":" + child7.getText());
// if ("android.widget.LinearLayout".equals(child7.getClassName())) {
for (int r = 0; r < child7.getChildCount(); r++) {
AccessibilityNodeInfo child2 = child7.getChild(r);
// Log.e("TIAOSHI###", "----5Node:" + child2.getClassName() + ":" + child2.getText());
for (int m = 0; m < child2.getChildCount(); m++) {
AccessibilityNodeInfo child3 = child2.getChild(m);
// Log.e("TIAOSHI###", "----6Node:" + child3.getClassName() + ":" + child3.getText());
for (int n = 0; n < child3.getChildCount(); n++) {
AccessibilityNodeInfo child4 = child3.getChild(n);
// Log.e("TIAOSHI###", "----7Node:" + child4.getClassName() + ":" + child4.getText());
for (int k = 0; k < child4.getChildCount(); k++) {
AccessibilityNodeInfo child5 = child4.getChild(k);
// Log.e("TIAOSHI###", "----8Node:" + child5.getClassName() + ":" + child5.getText());
for (int p = 0; p < child5.getChildCount(); p++) {
AccessibilityNodeInfo child8 = child5.getChild(p);
// Log.e("TIAOSHI###", "----9Node:" + child8.getClassName() + ":" + child8.getText());
}
}
}
}
}
// }
//
}
}
}
}
// }
}
return infooos;
}
十、超级大坑 目前无解
在做验证码登录的是时候getRootInActiveWindow()方法的返回值为null,getWindow()方法也为null,网上查遍资料也没有
印象笔记搜索getRootInActiveWindow()方法的返回值为null
解决办法是让也没先返回home,然后再打开就可以了。
十一、导入手机联系人
遇到的两个问题1是内容提供者的配置
2是权限的问题
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.READ_CONTACTS);
if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED){
requestPermissions(new String[]{Manifest.permission.WRITE_CONTACTS},REQUEST_CODE_ASK_PERMISSIONS);
// return;
}
}
加入这段代码就好了。
十二、绑定服务 https://blog.csdn.net/imxiangzi/article/details/76039978?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromBaidu-1.control
十三、WindowManager.LayoutParams详解 https://blog.csdn.net/weixin_34055787/article/details/92576555
十四、在Android中用广播监听AccessibilityService的开启状态并更新UI界面https://blog.csdn.net/huang826336127/article/details/80139543
十五、Android RxJava/RxAndroidtakeWhile直test测试条件通过才执行链式操作TestAblStep12
https://blog.csdn.net/zhangphil/article/details/79882700
十六、关机重启
AblStepHandler.getInstance().setStop(false);
AblStepHandler.sendMsg(AblSteps.STEP_25);
十七、退出抖音
AblStepHandler.getInstance().setStop(false);
AblStepHandler.sendMsg(AblSteps.STEP_192);//检测我的页面弹出了好友推荐对话框,并关闭
十八、捕捉弹框(获取事件)
// TODO Auto-generated method stub
// System.out.println("Enter->onAccessibilityEvent");
//判断是否是通知事件typeAnnouncement
if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED){
List<CharSequence> text = event.getText();
if(text.size()>0) {
CharSequence charSequence = text.get(0);
String s = charSequence.toString();
Log.e("TIAOSHI", "有弹出");
Log.e("TIAOSHI", "有弹出==" + s);
//获取消息来源
String sourcePackageName = (String) event.getPackageName();
//获取事件具体信息
Parcelable parcelable = event.getParcelableData();
//如果是下拉通知栏消息
if (parcelable instanceof Notification) {
} else {
//其它通知信息包括Toast
String toastMsg = (String) event.getText().get(0);
Log.e("TIAOSHI", "有弹出" + toastMsg);
Log.e("Latest Toast Message:", "" + toastMsg + " [Source: " + sourcePackageName + "]");
EventBus.getDefault().post(new SixEvent("success", toastMsg));
}
}
}
private void getdouyin() {
int num = 0;
accessibilityNodeInfos = new ArrayList<>();
AccessibilityNodeInfo root = AblService.getInstance().getRootInActiveWindow();
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = root.getChild(i);
Log.e("TIAOSHI###", "----oneNode:" + child.getClassName() + ":" + child.getText() + ":" + child.getContentDescription());
// if ("androidx.viewpager.widget.ViewPager".equals(child.getClassName())) {
for (int y = 0; y < child.getChildCount(); y++) {
AccessibilityNodeInfo child1 = child.getChild(y);
Log.e("TIAOSHI###", "----twoNode:" + child1.getClassName() + ":" + child1.getText() + ":" + child1.getContentDescription());
// if ("androidx.recyclerview.widget.RecyclerView".equals(child1.getClassName())) {
for (int j = 0; j < child1.getChildCount(); j++) {
AccessibilityNodeInfo child2 = child1.getChild(j);
Log.e("TIAOSHI###", "----threeNode:" + child2.getClassName() + ":" + child2.getText() + ":" + child1.getContentDescription());
for (int x = 0; x < child2.getChildCount(); x++) {
AccessibilityNodeInfo child3 = child2.getChild(x);
Log.e("TIAOSHI###", "----fourNode:" + child3.getClassName() + ":" + child3.getText() + ":" + child1.getContentDescription());
for (int r = 0; r < child3.getChildCount(); r++) {
AccessibilityNodeInfo child4 = child3.getChild(r);
Log.e("TIAOSHI###", "----5Node:" + child4.getClassName() + ":" + child4.getText()+ ":" + child4.getContentDescription());
for (int m = 0; m < child4.getChildCount(); m++) {
AccessibilityNodeInfo child5 = child4.getChild(m);
Log.e("TIAOSHI###", "----6Node:" + child5.getClassName() + ":" + child5.getText()+ ":" + child5.getContentDescription());
for (int p = 0; p < child5.getChildCount();p++) {
AccessibilityNodeInfo child6 = child5.getChild(p);
Log.e("TIAOSHI###", "----7Node:" + child6.getClassName() + ":" + child6.getText()+ ":" + child6.getContentDescription());
for (int t = 0; t < child6.getChildCount();t++) {
AccessibilityNodeInfo child7 = child6.getChild(t);
Log.e("TIAOSHI###", "----8Node:" + child7.getClassName() + ":" + child7.getText()+ ":" + child7.getContentDescription());
}
}
}
}
}
}
}
// }
// }
}
}
Rect rect = new Rect();
child7.getBoundsInScreen(rect);
Log.e("TIAOSHI###", "----8Node:" + "(" + rect.left + "," + rect.top + ")" + "," + "(" + rect.right + "," + rect.bottom + ")");
数据更新进度条http://www.zyiz.net/tech/detail-56306.html
抢红包的demo http://www.voidcn.com/article/p-cujkxxnt-bpb.html

View File

@@ -0,0 +1,85 @@
-- 直播间抢抖币
{
"success": 4,
"msg": "执行固定任务",
"data": {
"task_type": "4", -- 固定任务
"short_id": "tangjie0501", -- 抖音号
"short_name": "软件工程师蝴蝶剑", -- 抖音名称
"task_id": "3000def9108812dbd2df76980516532e", -- 任务id
"start_time": "09:00:00", -- 开始时间
"end_time": "16:00:00", -- 结束时间
"create_time": "2021-01-12 18:31:22", -- 创建时间
"function_execute": {
"id": 1, -- 任务功能id
"task_id": "3000def9108812dbd2df76980516532e", -- 任务id
"account_duration": 2, -- 单账号时长h
"live_num": 10, -- 直播间个数
"change_time": 3, -- 切换时间设置s
"live_speech": 1, -- 直播间话术是否开启 1开启 0关闭'
"live_click": 1, -- 是否点赞 1是 0
"grab_lucky_bag": 1, -- 是否抢福袋 1是 0
"bag_min_coin": 1, -- 抢福袋时,抖币的最小数字,小于则不参与
"grab_red_envelopes": 1, -- 是否抢红包 1是 0
"red_min_coin": 1, -- 抢红包时,抖币的最小数字,大于参与
"create_time": "2021-07-01 10:48:37", -- 创建时间
"task_type": 1, -- 任务类型1固定 0临时
"if_quguan": 1, -- 0:要取关1不要取关
"touch_num": "0" -- 撞号数量
},
"func_type": "5" -- 任务功能是直播间抢抖币
}
}
-- 直播间加热
{
"success": 4,
"msg": "执行固定任务",
"data": {
"task_type": "4", -- 固定任务
"short_id": "tangjie0501", -- 抖音id
"short_name": "软件工程师蝴蝶剑", -- 抖音名
"task_id": "3000def9108812dbd2df76980516532e", -- 任务id
"start_time": "09:00:00",-- 开始时间
"end_time": "16:00:00", -- 结束时间
"create_time": "2021-01-12 18:31:22", -- 创建时间
"function_execute": {
"id": 1, -- 任务功能id
"task_id": "3000def9108812dbd2df76980516532e", -- 任务id
"account_duration": 1, -- 单账号时长h
"live_nickname": "12", -- 加热直播间抖音昵称
"live_come": 1, -- 进直播间方式 1直播广场 2关注列表
"live_speech": 1, -- 直播间话术是否开启 1开启 0关闭
"live_click": 0, -- 是否点赞 1是 0
"live_documentary": 1, -- 直播间跟单 1是 0
"fans_gift": 1, -- 优先粉丝团礼物 1是 0
"fans_gift_type": 1, -- 礼物类型 1固定礼物 2全部礼物
"gift_heart": 1, -- 心心相吸的个数
"gift_time_min": 3, -- 随机间隔时间的最小时间(分)
"gift_time_max": 10, -- 随机间隔时间的最大时间(分)
"grab_lucky_bag": 1, -- 是否抢福袋 1是 0
"bag_min_coin": 1, -- 抢福袋时,抖币的最小数字,小于则不参与
"grab_red_envelopes": 1, -- 是否抢红包 1是 0
"red_min_coin": 2, -- 抢红包时,抖币的最小数字,大于参与
"create_time": "2021-07-01 10:50:51", -- 创建事假
"task_type": 1, -- 任务类型1固定 0临时
"if_quguan": 1, -- 0:要取关1不要取关
"touch_num": "0" -- -- 撞号数量
},
"func_type": "6" -- 任务功能是直播间加热
}
}
-- 直播间抢抖币任务、或 直播间加热任务执行完毕后,调取的接口,统计抖币
app/Appscanlogin/taskDbTj
-- 直播间话术接口
app/Appscanlogin/liveScript
-- 获取直播间数据
app/Appoperate/LiveRoom
-- 返回最后一次关注直播间id
app/Appoperate/returnRoomid

8
doc/硬件信息 Normal file
View File

@@ -0,0 +1,8 @@
红米7A分辨率屏幕分辨率 1520 x 720 HD
红米10屏幕分辨率: 宽1080==高2167
红米9note宽1080==高2111

104
doc/策略模式 Normal file
View File

@@ -0,0 +1,104 @@
看下策略模式的定义
策略模式定义了一些列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变换。
首先,需要定义一个策略接口。
public interface Strategy {
void travel();
}
然后根据不同的出行方式实行对应的接口
public class WalkStrategy implements Strategy{
@Override
public void travel() {
System.out.println("walk");
}
}
public class PlaneStrategy implements Strategy{
@Override
public void travel() {
System.out.println("plane");
}
}
public class SubwayStrategy implements Strategy{
@Override
public void travel() {
System.out.println("subway");
}
}
此外还需要一个包装策略的类,并调用策略接口中的方法
public class TravelContext {
Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void travel() {
if (strategy != null) {
strategy.travel();
}
}
}
测试一下代码
public class Main {
public static void main(String[] args) {
TravelContext travelContext=new TravelContext();
travelContext.setStrategy(new PlaneStrategy());
travelContext.travel();
travelContext.setStrategy(new WalkStrategy());
travelContext.travel();
travelContext.setStrategy(new SubwayStrategy());
travelContext.travel();
}
}
输出结果如下
plane
walk
subway
5 策略模式的优点与缺点
策略模式主要用来分离算法,根据相同的行为抽象来做不同的具体策略实现。很好的实现了开闭原则,也就是定义抽象,注入具体实现,从而达到很好的可扩展性。
优点
使用了组合,使架构更加灵活
富有弹性,可以较好的应对变化(开闭原则)
更好的代码复用性(相对于继承)
消除大量的条件语句
缺点
随着策略的增加,子类也会变得繁多。
选择何种算法需要客户端来创建对象,增加了耦合,这里可以通过与工厂模式结合解决该问题;
策略模式设计原则:
找出应用中需要变化的部分,把它们独立出来,不要和那些不需要变化的代码混在一起
面向接口编程,而不是面向实现编程
多用组合,少用继承
6 总结
策略模式在开发应用场景很多,平时有意识的运用好策略模式,可以很好的提高的程序的可维护性和可扩展性。
策略模式和工厂模式搭配使用

15
doc/评论话术 Normal file
View File

@@ -0,0 +1,15 @@
1.好几次看到你了~缘分互关一下吧!互赞三个!
2.拍的不错,好几次看到你了~缘分互关一下吧!,互赞三个!
3.你的视频真好~互关一下,互赞三个!
4.唉~好几次看到你了~缘分互关一下,互赞三个!
5.看到你好几次了~互赞三个互关吧!
6.在吗?互关一下互赞三个吧,我看到你好几次了
7.感谢关注~我的作品评论一下吧
8.最新的作品评论一下吧!感谢
9.最近发了几个好笑视频~求点赞
10.忙啥呢~互赞三个好吗?
11.一直关注你互赞三个吧!
12.hi我发的搞笑视频看了吗赞一下吧
13.互赞三个~好吗?
14.挺喜欢你的视频的,互关然后互赞三个吧
15.期待更新更多作品啊~互关后互赞三个吧

39
doc/适配资料 Normal file
View File

@@ -0,0 +1,39 @@
1.//加关注(659,576)
AccessibilityNodeInfo guanzhu = guanzhu();
遍历找结点有可能出错,此处用到坐标点点击
2.点击评论
// (659, 810);
commentClick(AblService.getInstance());
用到坐标点点击
3.点击留下你的精彩评论吧
AblService.getInstance().clickPoint(153, 1292, 300);//红米7a
用到坐标点点击
4.匹配抖音14.6版本
推荐加关的修改地方,
_______________________________________________________________________________________________________
_______________________________________________________________________________________________________
适配华为手机需要修改的地方
AblService.getInstance().clickPoint(153, 1292, 300);//红米7a

21
doc/项目进度 Normal file
View File

@@ -0,0 +1,21 @@
1.后台派取关任务可以执行
2.后台派登录任务可以执行
3.后台派撞号任务可以执行
4.后台派停止任务可以执行
5.测试网络版本的帐号登录成功
6.测试后台派一个固定的推荐加关的任务,然后再将任务停止。 此处场景是先开启一个固定任务,然后再接收一个紧急的停任务。
此处有一个核心逻辑,最后三个参数,传值分两种情况,一、如果来的紧急任务是一个停任务,那么就传空。二、如果来的紧急任务是一个其它任务。那么就传值
7.测试后台派一个固定的推荐加关的任务,然后再将任务停止,再拍一条紧急加关的任务,再停止。
核心逻辑: //是否有当前有执行任务,如果没有任务执行,则:
//初始化5秒的接口参数
ChangLiang.task_id = "";//当前执行任务id
ChangLiang.id = "";//当前执行任务功能id
ChangLiang.task_type = "";//当前执行任务类型
8.后台派固定任务撞号可以执行成功
9.后台派紧急任务撞号可以执行成功
8月16日 完成了 自动打开无障碍功能
8月17日 完成了 直播间加热点赞逻辑的功能修复若干闪退的bug
8月18日 完成了 修复了直播间加热bug,对软件运行性能进行优化

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fisherbone.fuzhu.order">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"

View File

@@ -1,25 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xiangxue.new_modular_customarouter.order">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.fisherbone.fuzhu.order.debug.Order_DebugActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.fisherbone.fuzhu.order.Order_MainActivity" />
</application>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.fisherbone.fuzhu.order.debug.Order_DebugActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.fisherbone.fuzhu.order.Order_MainActivity"
android:exported="false" />
</application>
</manifest>

View File

@@ -1,29 +1,29 @@
package com.fisherbone.fuzhu.order.debug;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.xiangxue.common.utils.Cons;
import com.fisherbone.fuzhu.order.Order_MainActivity;
import com.xiangxue.new_modular_customarouter.order.R;
// TODO 同学们注意:这是 测试环境下的代码 Debug
public class Order_DebugActivity extends Order_DebugBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.order_activity_debug);
Log.e(Cons.TAG, "order/debug/Order_DebugActivity");
}
public void jumpPersonal(View view) {
Intent intent = new Intent(this, Order_MainActivity.class);
startActivity(intent);
}
}
package com.fisherbone.fuzhu.order.debug;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.xiangxue.common.utils.Cons;
import com.fisherbone.fuzhu.order.Order_MainActivity;
import com.fisherbone.fuzhu.order.R;
// TODO 同学们注意:这是 测试环境下的代码 Debug
public class Order_DebugActivity extends Order_DebugBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.order_activity_debug);
Log.e(Cons.TAG, "order/debug/Order_DebugActivity");
}
public void jumpPersonal(View view) {
Intent intent = new Intent(this, Order_MainActivity.class);
startActivity(intent);
}
}

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fisherbone.fuzhu.personal">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"

View File

@@ -1,25 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xiangxue.new_modular_customarouter.personal">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.fisherbone.fuzhu.personal.debug.Personal_DebugActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.fisherbone.fuzhu.personal.Personal_MainActivity" />
</application>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.fisherbone.fuzhu.personal.debug.Personal_DebugActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.fisherbone.fuzhu.personal.Personal_MainActivity"
android:exported="false" />
</application>
</manifest>

View File

@@ -1,27 +1,27 @@
package com.fisherbone.fuzhu.personal.debug;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.xiangxue.common.utils.Cons;
import com.fisherbone.fuzhu.personal.Personal_MainActivity;
import com.xiangxue.new_modular_customarouter.personal.R;
// TODO 同学们注意:这是 测试环境下的代码 Debug
public class Personal_DebugActivity extends Personal_DebugBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.personal_activity_debug);
Log.e(Cons.TAG, "personal/debug/Personal_DebugActivity");
}
public void jump(View view) {
Intent intent = new Intent(this, Personal_MainActivity.class);
startActivity(intent);
}
}
package com.fisherbone.fuzhu.personal.debug;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.xiangxue.common.utils.Cons;
import com.fisherbone.fuzhu.personal.Personal_MainActivity;
import com.fisherbone.fuzhu.personal.R;
// TODO 同学们注意:这是 测试环境下的代码 Debug
public class Personal_DebugActivity extends Personal_DebugBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.personal_activity_debug);
Log.e(Cons.TAG, "personal/debug/Personal_DebugActivity");
}
public void jump(View view) {
Intent intent = new Intent(this, Personal_MainActivity.class);
startActivity(intent);
}
}

View File

@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.derry.wechat">
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera.autofocus" />
<!-- 权限声明(去重后) -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission
android:name="android.permission.INJECT_EVENTS"
tools:ignore="ProtectedPermissions" />
@@ -19,46 +22,25 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 这个权限用于进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 这个权限用于访问GPS定位 -->
<uses-permission android:name="android.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 读取手机通讯录 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 写入手机通讯录 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 这个权限用于进行网络定位 -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- 这个权限用于访问GPS定位 -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" /> <!-- 用于访问wifi网络信息wifi信息会用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 用于读取手机当前的状态 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据 -->
<!-- 下载权限 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> <!-- 访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 腾讯bugly权限 -->
<uses-permission android:name="android.permission.CALL_PHONE" /> <!-- Bugly升级SDK权限配置开始 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 二维码扫描 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 震动权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 摄像头权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 用于申请调用A-GPS模块 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 这个权限用于获取wifi的获取权限wifi信息会用来进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 用于读取手机当前的状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 写入扩展存储,向扩展卡写入数据,用于写入缓存定位数据 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- 读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!-- 网络权限,加载网络网页需要联网 -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- 精确定位权限,允许一个程序访问精确位置(GPS定位) -->
<uses-permission android:name="android.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS"
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"
<uses-permission
android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<application
@@ -85,7 +67,7 @@
android:name=".abllib.AblService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="true"
android:exported="true">
android:exported="false">
<intent-filter android:priority="1000">
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
@@ -95,4 +77,4 @@
</service>
</application>
</manifest>
</manifest>

View File

@@ -1,104 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.derry.wechat.debug">
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission
android:name="android.permission.INJECT_EVENTS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 这个权限用于进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 这个权限用于访问GPS定位 -->
<uses-permission android:name="android.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 读取手机通讯录 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 写入手机通讯录 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 这个权限用于进行网络定位 -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- 这个权限用于访问GPS定位 -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" /> <!-- 用于访问wifi网络信息wifi信息会用于进行网络定位 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 用于读取手机当前的状态 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据 -->
<!-- 下载权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> <!-- 访问网络,网络定位需要上网 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 腾讯bugly权限 -->
<uses-permission android:name="android.permission.CALL_PHONE" /> <!-- Bugly升级SDK权限配置开始 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /> <!-- 二维码扫描 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 震动权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 摄像头权限 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 用于申请调用A-GPS模块 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 这个权限用于获取wifi的获取权限wifi信息会用来进行网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 用于读取手机当前的状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 写入扩展存储,向扩展卡写入数据,用于写入缓存定位数据 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.INTERNET" /> <!-- 读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!-- 网络权限,加载网络网页需要联网 -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- 精确定位权限,允许一个程序访问精确位置(GPS定位) -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<application
android:name="com.xiangxue.common.base.BaseApplication"
android:allowBackup="true"
android:icon="@drawable/wechat"
android:label="@string/app_name"
android:largeHeap="true"
android:enabled="true"
android:roundIcon="@drawable/wechat"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/Theme.Design.NoActionBar">
<activity
android:name="com.derry.wechat.Activity.WeChatChannelActivity"
android:exported="false" />
<activity android:name="com.derry.wechat.Activity.WeChatPlate"
android:exported="true"/>
<activity android:name=".Login_DebugActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.derry.wechat.abllib.AblService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="true">
<intent-filter android:priority="1000">
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/abl_service" />
</service>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera.autofocus" />
<!-- 权限声明(去重后) -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission
android:name="android.permission.INJECT_EVENTS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<application
android:name="com.xiangxue.common.base.BaseApplication"
android:allowBackup="true"
android:icon="@drawable/wechat"
android:label="@string/app_name"
android:largeHeap="true"
android:enabled="true"
android:roundIcon="@drawable/wechat"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/Theme.Design.NoActionBar">
<activity
android:name="com.derry.wechat.Activity.WeChatChannelActivity"
android:exported="false" />
<activity android:name="com.derry.wechat.Activity.WeChatPlate"
android:exported="true"/>
<activity android:name="com.derry.wechat.debug.Login_DebugActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.derry.wechat.abllib.AblService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="true"
android:exported="false">
<intent-filter android:priority="1000">
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/abl_service" />
</service>
</application>
</manifest>

View File

@@ -1,128 +1,128 @@
package com.derry.wechat.Activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.PopupWindow;
import androidx.appcompat.app.AppCompatActivity;
import com.blankj.utilcode.util.ToastUtils;
import com.derry.wechat.steps.TestAblStep1;
import com.derry.wechat.abllib.AblConfig;
import com.derry.wechat.abllib.AblService;
import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.abllib.AblSteps;
import com.derry.wechat.abllib.utils.AblUtil;
import com.derry.wechat.debug.R;
import com.derry.wechat.steps.TestAblStep2;
import com.xiangxue.arouter_annotation.ARouter;
import io.reactivex.disposables.Disposable;
/**
* xiaoading
* @author Administrator
* DYX 2021-12-2
*/
@ARouter(path = "/wechat/W_MainActivity")
public class W_MainActivity extends AppCompatActivity {
public static String APP_PACKAGE_NAME = "com.tencent.mm";
private PopupWindow mPopCut;
private Disposable mdDisposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.w_activity_main);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
AblConfig.Builder()
.setMonitoringPackageNames("com.tencent.mm")//微信包
.setLogTag("DDD")//logtag不设置默认是abllib
.setStepMsgDelayMillis(3000)//步骤延迟时间
.setFindViewMillisInFuture(10000)//寻找界面超时时间
.setFindViewCountDownInterval(200)//寻找界面间隔时间
.build().init();
AblStepHandler.getInstance().initStepClass(new TestAblStep1() , new TestAblStep2());
AblUtil.getPermissions(W_MainActivity.this);
//adb shell pm grant com.derry.wechat android.permission.WRITE_SECURE_SETTINGS
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.derry.wechat/com.derry.wechat.abllib.AblService");
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, "1");//1表示开启
if (AblUtil.isAccessibilityServiceOpen(W_MainActivity.this)) {
// Log.e("TIAOSHI###", "无障碍服务正常");
ToastUtils.showShort( "无障碍服务正常");
}else {
ToastUtils.showShort( "无障碍服务异常");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.derry.wechat/com.derry.wechat.abllib.AblService");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "0");//1表示开启
}
/**
* 底部导航栏
*/
Button mBtnCut = findViewById(R.id.cut);
mBtnCut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@SuppressLint("InflateParams") View v = getLayoutInflater().inflate(R.layout.layout_pop,null);
mPopCut = new PopupWindow(v, ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
mPopCut.setTouchable(true);
mPopCut.setFocusable(true);
mPopCut.showAtLocation(getWindow().getDecorView(), Gravity.BOTTOM,0,0);
}
});
/**
* 菜单
*/
Button mBtnRecommend = findViewById(R.id.recommed);
mBtnRecommend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(W_MainActivity.this, WeChatPlate.class);
startActivity(intent);
}
});
Button mBtnChannel = findViewById(R.id.channel);
mBtnChannel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(W_MainActivity.this,WeChatChannelActivity.class);
startActivity(intent);
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.fisherbone.fuzhu/com.fisherbone.fuzhu.abllib.AblService");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "0");//1表示开启
if (mdDisposable != null) {
mdDisposable.dispose();
}
}
}
package com.derry.wechat.Activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.PopupWindow;
import androidx.appcompat.app.AppCompatActivity;
import com.blankj.utilcode.util.ToastUtils;
import com.derry.wechat.steps.TestAblStep1;
import com.derry.wechat.abllib.AblConfig;
import com.derry.wechat.abllib.AblService;
import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.abllib.AblSteps;
import com.derry.wechat.abllib.utils.AblUtil;
import com.derry.wechat.R;
import com.derry.wechat.steps.TestAblStep2;
import com.xiangxue.arouter_annotation.ARouter;
import io.reactivex.disposables.Disposable;
/**
* xiaoading
* @author Administrator
* DYX 2021-12-2
*/
@ARouter(path = "/wechat/W_MainActivity")
public class W_MainActivity extends AppCompatActivity {
public static String APP_PACKAGE_NAME = "com.tencent.mm";
private PopupWindow mPopCut;
private Disposable mdDisposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.w_activity_main);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
AblConfig.Builder()
.setMonitoringPackageNames("com.tencent.mm")//微信包
.setLogTag("DDD")//logtag不设置默认是abllib
.setStepMsgDelayMillis(3000)//步骤延迟时间
.setFindViewMillisInFuture(10000)//寻找界面超时时间
.setFindViewCountDownInterval(200)//寻找界面间隔时间
.build().init();
AblStepHandler.getInstance().initStepClass(new TestAblStep1() , new TestAblStep2());
AblUtil.getPermissions(W_MainActivity.this);
//adb shell pm grant com.derry.wechat android.permission.WRITE_SECURE_SETTINGS
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.derry.wechat/com.derry.wechat.abllib.AblService");
Settings.Secure.putString(getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED, "1");//1表示开启
if (AblUtil.isAccessibilityServiceOpen(W_MainActivity.this)) {
// Log.e("TIAOSHI###", "无障碍服务正常");
ToastUtils.showShort( "无障碍服务正常");
}else {
ToastUtils.showShort( "无障碍服务异常");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.derry.wechat/com.derry.wechat.abllib.AblService");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "0");//1表示开启
}
/**
* 底部导航栏
*/
Button mBtnCut = findViewById(R.id.cut);
mBtnCut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
@SuppressLint("InflateParams") View v = getLayoutInflater().inflate(R.layout.layout_pop,null);
mPopCut = new PopupWindow(v, ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
mPopCut.setTouchable(true);
mPopCut.setFocusable(true);
mPopCut.showAtLocation(getWindow().getDecorView(), Gravity.BOTTOM,0,0);
}
});
/**
* 菜单
*/
Button mBtnRecommend = findViewById(R.id.recommed);
mBtnRecommend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(W_MainActivity.this, WeChatPlate.class);
startActivity(intent);
}
});
Button mBtnChannel = findViewById(R.id.channel);
mBtnChannel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(W_MainActivity.this,WeChatChannelActivity.class);
startActivity(intent);
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "com.fisherbone.fuzhu/com.fisherbone.fuzhu.abllib.AblService");
Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "0");//1表示开启
if (mdDisposable != null) {
mdDisposable.dispose();
}
}
}

View File

@@ -1,89 +1,89 @@
package com.derry.wechat.Activity;
import static com.derry.wechat.Activity.W_MainActivity.APP_PACKAGE_NAME;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.abllib.AblSteps;
import com.derry.wechat.abllib.utils.AblUtil;
import com.derry.wechat.abllib.utils.AblViewUtil;
import com.derry.wechat.debug.R;
/**
* @author Administrator
* Dyx 2021-12-2
*/
public class WeChatChannelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_we_chat_channel);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
WeChatControlWindow weChatControlWindow = new WeChatControlWindow(WeChatChannelActivity.this);
Button mBtnChannelStart = findViewById(R.id.wechat_channel_start);
mBtnChannelStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (AblUtil.isAccessibilityServiceOpen(WeChatChannelActivity.this)) {
AblStepHandler.getInstance().setStop(false);
//AblUtil.addSuspensionWindowView(WeChatChannelActivity.this,weChatControlWindow.initWindowView());
launchapp(WeChatChannelActivity.this);
AblViewUtil.mySleep(5);
AblStepHandler.sendMsg(AblSteps.STEP_10);
} else {
AblUtil.openAccessibilitySettings();
//ToastUtils.showShort("请先开启辅助服务");
}
}
});
}
//跳转页面的方法
private void launchapp(Context context) {
//判断当前手机是否有要跳入的app
if (isAppInstalled(context)){
//如果有根据包名跳转
context.startActivity(context.getPackageManager().getLaunchIntentForPackage(APP_PACKAGE_NAME));
}else{
//如果没有走进入系统商店找到这款APP提示你去下载这款APP的程序
goToMarket(context);
}
}
//这里是进入应用商店下载指定APP的方法。
private void goToMarket(Context context) {
Uri uri = Uri.parse("market://details?id=" + W_MainActivity.APP_PACKAGE_NAME);
Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);
try {
context.startActivity(goToMarket);
} catch (Exception e) {
e.printStackTrace();
}
}
//这里是判断APP中是否有相应APP的方法
private boolean isAppInstalled(Context context) {
try {
context.getPackageManager().getPackageInfo(W_MainActivity.APP_PACKAGE_NAME,0);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
package com.derry.wechat.Activity;
import static com.derry.wechat.Activity.W_MainActivity.APP_PACKAGE_NAME;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.abllib.AblSteps;
import com.derry.wechat.abllib.utils.AblUtil;
import com.derry.wechat.abllib.utils.AblViewUtil;
import com.derry.wechat.R;
/**
* @author Administrator
* Dyx 2021-12-2
*/
public class WeChatChannelActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_we_chat_channel);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
WeChatControlWindow weChatControlWindow = new WeChatControlWindow(WeChatChannelActivity.this);
Button mBtnChannelStart = findViewById(R.id.wechat_channel_start);
mBtnChannelStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (AblUtil.isAccessibilityServiceOpen(WeChatChannelActivity.this)) {
AblStepHandler.getInstance().setStop(false);
//AblUtil.addSuspensionWindowView(WeChatChannelActivity.this,weChatControlWindow.initWindowView());
launchapp(WeChatChannelActivity.this);
AblViewUtil.mySleep(5);
AblStepHandler.sendMsg(AblSteps.STEP_10);
} else {
AblUtil.openAccessibilitySettings();
//ToastUtils.showShort("请先开启辅助服务");
}
}
});
}
//跳转页面的方法
private void launchapp(Context context) {
//判断当前手机是否有要跳入的app
if (isAppInstalled(context)){
//如果有根据包名跳转
context.startActivity(context.getPackageManager().getLaunchIntentForPackage(APP_PACKAGE_NAME));
}else{
//如果没有走进入系统商店找到这款APP提示你去下载这款APP的程序
goToMarket(context);
}
}
//这里是进入应用商店下载指定APP的方法。
private void goToMarket(Context context) {
Uri uri = Uri.parse("market://details?id=" + W_MainActivity.APP_PACKAGE_NAME);
Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);
try {
context.startActivity(goToMarket);
} catch (Exception e) {
e.printStackTrace();
}
}
//这里是判断APP中是否有相应APP的方法
private boolean isAppInstalled(Context context) {
try {
context.getPackageManager().getPackageInfo(W_MainActivity.APP_PACKAGE_NAME,0);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

View File

@@ -1,65 +1,65 @@
package com.derry.wechat.Activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import com.derry.wechat.abllib.AblService;
import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.debug.R;
/**
* @author Administrator
* DYX 2021-12-3
*/
public class WeChatControlWindow {
Context mContext;
public WeChatControlWindow (Context mContext){
this.mContext=mContext;
}
public View initWindowView(){//悬浮窗
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View view = View.inflate(mContext, R.layout.view_test_w, null);
// view.findViewById(R.id.fu_btn_1).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// if (AblUtil.isAccessibilityServiceOpen(W_MainActivity.this)) {
// AblStepHandler.getInstance().setStop(false);
// AblStepHandler.sendMsg(AblSteps.STEP_1);
// } else {
// ToastUtils.showShort("请先开启辅助服务");
// }
// }
// });
view.findViewById(R.id.fu_btn_2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
windowManager.removeView(view);
}
});
view.findViewById(R.id.fu_btn_3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AblStepHandler.getInstance().setStop(true);
}
});
view.findViewById(R.id.fu_btn_4).setOnClickListener(new View.OnClickListener() {
@SuppressLint("ObsoleteSdkInt")
@Override
public void onClick(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
AblService.getInstance().disableSelf();
}
}
});
return view;
}
}
package com.derry.wechat.Activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import com.derry.wechat.abllib.AblService;
import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.R;
/**
* @author Administrator
* DYX 2021-12-3
*/
public class WeChatControlWindow {
Context mContext;
public WeChatControlWindow (Context mContext){
this.mContext=mContext;
}
public View initWindowView(){//悬浮窗
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
View view = View.inflate(mContext, R.layout.view_test_w, null);
// view.findViewById(R.id.fu_btn_1).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// if (AblUtil.isAccessibilityServiceOpen(W_MainActivity.this)) {
// AblStepHandler.getInstance().setStop(false);
// AblStepHandler.sendMsg(AblSteps.STEP_1);
// } else {
// ToastUtils.showShort("请先开启辅助服务");
// }
// }
// });
view.findViewById(R.id.fu_btn_2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
windowManager.removeView(view);
}
});
view.findViewById(R.id.fu_btn_3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AblStepHandler.getInstance().setStop(true);
}
});
view.findViewById(R.id.fu_btn_4).setOnClickListener(new View.OnClickListener() {
@SuppressLint("ObsoleteSdkInt")
@Override
public void onClick(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
AblService.getInstance().disableSelf();
}
}
});
return view;
}
}

View File

@@ -1,30 +1,30 @@
package com.derry.wechat.Activity;
import android.os.Bundle;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.derry.wechat.debug.R;
/**
* @author Administrator
*/
public class WeChatPlate extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_wechatplate);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
package com.derry.wechat.Activity;
import android.os.Bundle;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.derry.wechat.R;
/**
* @author Administrator
*/
public class WeChatPlate extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_wechatplate);
//透明状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}

View File

@@ -26,6 +26,7 @@ import com.derry.wechat.abllib.AblStepHandler;
import com.derry.wechat.abllib.AblSteps;
import com.derry.wechat.abllib.utils.AblUtil;
import com.derry.wechat.steps.TestAblStep2;
import com.derry.wechat.R;
/**

167
钉钉打卡.txt Normal file
View File

@@ -0,0 +1,167 @@
钉钉打卡功能实现文档
====================
一、功能概述
-----------
实现了钉钉应用的自动化打卡功能通过无障碍服务AccessibilityService自动完成以下操作
1. 启动钉钉应用
2. 在钉钉首页查找并点击"打卡"按钮
3. 等待跳转到打卡页面
4. 在打卡页面点击"打卡"按钮完成打卡
二、实现方案
-----------
将钉钉打卡的业务逻辑直接集成到 AblService无障碍服务而不是使用 AblStepHandler。
这样做的优势:
- 避免 Handler 消息丢失:业务逻辑在无障碍服务中执行,即使主应用在后台也能正常运行
- 更可靠:无障碍服务是系统级服务,不会被轻易暂停
- 代码集中:所有打卡逻辑都在 AblService 中,便于维护
三、修改文件清单
---------------
1. app/src/main/java/com/fisherbone/fuzhu/abllib/AblService.java
2. app/src/main/java/com/fisherbone/fuzhu/activity/MainActivity.java
四、详细修改说明
---------------
【文件1】AblService.java
1. 添加导入
- import android.os.Handler;
2. 添加成员变量第47-51行
```java
// 钉钉打卡相关
private static final String DINGDING_PACKAGE = "com.alibaba.android.rimet";
private boolean isDingDingDakaStarted = false; // 是否已启动钉钉打卡流程
private int dingDingDakaStep = 0; // 打卡步骤0-未开始1-等待启动2-点击首页打卡3-点击打卡页面打卡
private Handler dingDingHandler = new Handler(); // 钉钉打卡专用 Handler
```
3. 修改 onAccessibilityEvent 方法第155-162行
在 TYPE_WINDOW_STATE_CHANGED 事件处理中添加钉钉窗口检测:
```java
case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
String classname = event.getClassName().toString();
String packageName = event.getPackageName() != null ? event.getPackageName().toString() : "";
Log.e("TIAOSHI", "有弹出==22222" + "==classname==" + classname + ", packageName=" + packageName);
// 检测钉钉应用窗口,处理打卡流程
if (DINGDING_PACKAGE.equals(packageName) && isDingDingDakaStarted) {
handleDingDingDakaWindow(classname);
}
```
4. 添加核心方法
a) startDingDingDaka() - 启动打卡流程第634-649行
- 由 MainActivity 调用
- 设置 isDingDingDakaStarted = true
- 设置 dingDingDakaStep = 1等待应用启动
- 延迟2秒后执行步骤2
b) handleDingDingDakaWindow() - 处理钉钉窗口状态变化第654-664行
- 当检测到钉钉应用窗口变化时调用
- 根据当前步骤执行相应操作
c) executeDingDingDakaStep2() - 执行步骤2点击首页打卡按钮第669-703行
- 查找并点击包含"打卡"文本的按钮
- 如果未找到,尝试"考勤打卡"、"工作"、"工作台"
- 点击成功后延迟2秒执行步骤3
d) executeDingDingDakaStep3() - 执行步骤3点击打卡页面打卡按钮第708-724行
- 当前已注释,等待用户根据实际需求实现
- 可以查找并点击"打卡"、"上班打卡"、"下班打卡"、"外勤打卡"、"立即打卡"等按钮
e) clickDakaButton() - 查找并点击包含指定文本的按钮第729-744行
- 获取根节点
- 调用 findAndClickDakaNode 递归查找
f) findAndClickDakaNode() - 递归查找并点击节点第749-801行
- 检查节点的文本和描述是否包含目标文本
- 如果节点可点击,直接点击
- 如果节点不可点击,使用 AblViewUtil.performViewClick() 自动查找可点击的父节点
- 递归查找子节点
g) resetDingDingDaka() - 重置打卡状态第806-811行
- 重置所有状态变量
- 清除所有延迟任务
【文件2】MainActivity.java
1. 修改 executeDingDingDaka() 方法第640-654行
将原来的 AblStepHandler 调用改为直接调用 AblService 的方法:
原代码:
```java
AblStepHandler.getInstance().setStop(false);
AblStepHandler.sendMsg(AblSteps.STEP_270);
```
新代码:
```java
AblService.getInstance().startDingDingDaka();
```
五、执行流程
-----------
1. 用户在 MainActivity 中点击"钉钉打卡"按钮
2. MainActivity.executeDingDingDaka() 被调用
3. 检查钉钉应用是否安装使用多种方法getPackageInfo、getLaunchIntentForPackage、queryIntentActivities
4. 启动钉钉应用(优先使用 getLaunchIntentForPackage失败则使用 LaunchHomeActivity
5. 延迟1秒后调用 AblService.startDingDingDaka()
6. AblService 设置 isDingDingDakaStarted = truedingDingDakaStep = 1
7. 延迟2秒后执行步骤2executeDingDingDakaStep2
8. 在钉钉首页查找并点击"打卡"按钮
9. 点击成功后,设置 dingDingDakaStep = 3延迟2秒后执行步骤3
10. 在打卡页面查找并点击"打卡"按钮(当前已注释,需要根据实际需求实现)
11. 打卡完成后调用 resetDingDingDaka() 重置状态
六、关键日志标识
---------------
所有钉钉打卡相关的日志都使用 "TIAOSHI###" 作为标识,便于过滤和调试:
- "AblService: 启动钉钉打卡流程"
- "AblService: 钉钉打卡-步骤1完成开始步骤2"
- "AblService: 钉钉打卡-步骤2: 查找并点击打卡按钮"
- "AblService: 钉钉打卡-步骤3: 在打卡页面点击打卡"
- "AblService: 找到匹配节点: ..."
- "AblService: 成功点击节点: ..."
七、注意事项
-----------
1. 无障碍服务必须已开启,否则 AblService.getInstance() 会抛出异常
2. 钉钉应用的包名是 "com.alibaba.android.rimet"
3. 步骤3executeDingDingDakaStep3当前已注释需要根据实际打卡页面的UI实现
4. 所有延迟时间2秒可以根据实际情况调整
5. 如果找不到打卡按钮,会尝试多种可能的文本,如果都找不到则结束流程
八、待完善功能
-----------
1. executeDingDingDakaStep3() 方法当前已注释需要根据实际打卡页面的UI实现
2. 可以添加更多的错误处理和重试机制
3. 可以添加打卡结果的通知或回调
九、测试建议
-----------
1. 确保无障碍服务已开启
2. 确保钉钉应用已安装
3. 在 MainActivity 中点击"钉钉打卡"按钮
4. 观察日志输出,确认每个步骤是否正常执行
5. 如果某个步骤失败,检查日志中的节点信息,调整查找逻辑
十、相关文件
-----------
- app/src/main/java/com/fisherbone/fuzhu/abllib/AblService.java
- app/src/main/java/com/fisherbone/fuzhu/activity/MainActivity.java
- app/src/main/java/com/fisherbone/fuzhu/abllib/utils/AblViewUtil.java提供 performViewClick 方法)
- app/src/main/res/layout/activity_main.xml包含"钉钉打卡"UI元素
---
文档生成时间2026-01-06

View File

@@ -0,0 +1,580 @@
================================================================================
项目重构后应用领域分析报告
================================================================================
基于项目核心技术能力,分析重构后可以应用的领域和场景
================================================================================
一、核心技术能力分析
================================================================================
1.1 核心能力清单
---------------
✓ 无障碍服务AccessibilityService自动化操作
✓ UI元素识别和操作点击、滑动、输入等
✓ 任务调度和执行系统
✓ 组件化架构设计
✓ 设备管理和远程控制
✓ 数据采集和存储
✓ 多设备群控管理
✓ 实时状态监控
✓ 事件驱动架构
1.2 技术优势
------------
- 无需Root权限即可实现自动化
- 跨应用操作能力
- 实时UI状态监听
- 灵活的任务编排
- 可扩展的组件化架构
================================================================================
二、直接应用领域
================================================================================
2.1 移动应用自动化测试 ★★★★★
--------------------------------
应用场景:
- UI自动化测试
- 回归测试
- 兼容性测试
- 性能测试
- 压力测试
技术复用:
✓ 无障碍服务 → 自动化测试框架
✓ 任务调度系统 → 测试用例执行引擎
✓ UI元素识别 → 测试元素定位
✓ 步骤编排 → 测试步骤管理
优势:
- 无需修改被测应用
- 支持真实设备测试
- 可以模拟真实用户操作
- 支持多设备并行测试
产品方向:
- 移动端自动化测试平台
- 兼容性测试服务
- CI/CD集成测试工具
2.2 无障碍辅助工具 ★★★★★
--------------------------------
应用场景:
- 视障人士辅助工具
- 老年人智能助手
- 操作简化工具
- 语音控制应用
技术复用:
✓ 无障碍服务 → 辅助功能核心
✓ UI识别 → 内容读取和识别
✓ 自动化操作 → 简化操作流程
✓ 事件监听 → 智能响应
优势:
- 符合Android无障碍规范
- 可以读取屏幕内容
- 可以执行自动化操作
- 支持语音交互
产品方向:
- 智能语音助手
- 屏幕阅读器增强版
- 一键操作工具
- 智能家居控制
2.3 企业移动设备管理MDM/EMM ★★★★☆
--------------------------------
应用场景:
- 企业设备统一管理
- 远程设备控制
- 应用分发和管理
- 设备监控和统计
技术复用:
✓ 设备管理 → MDM核心功能
✓ 远程控制 → 远程协助
✓ 任务分发 → 策略执行
✓ 状态监控 → 设备健康监控
优势:
- 支持批量设备管理
- 可以实现远程操作
- 支持策略下发和执行
- 实时状态监控
产品方向:
- 企业移动设备管理平台
- 远程技术支持工具
- 设备监控系统
2.4 数据采集和分析平台 ★★★★☆
--------------------------------
应用场景:
- 应用使用行为分析
- 用户行为数据采集
- 竞品分析
- 市场调研
技术复用:
✓ UI识别 → 数据提取
✓ 事件监听 → 行为追踪
✓ 数据存储 → 数据仓库
✓ 任务调度 → 采集任务管理
优势:
- 可以采集真实用户行为
- 支持多维度数据采集
- 可以自动化采集流程
- 支持大规模数据采集
产品方向:
- 用户行为分析平台
- 竞品分析工具
- 市场调研系统
================================================================================
三、扩展应用领域
================================================================================
3.1 智能家居控制中心 ★★★★☆
--------------------------------
应用场景:
- 智能家居设备控制
- 场景自动化
- 语音控制
- 远程控制
技术复用:
✓ 自动化操作 → 设备控制
✓ 任务调度 → 场景编排
✓ 事件驱动 → 智能响应
✓ 组件化架构 → 设备插件化
优势:
- 可以控制各种智能设备
- 支持复杂场景编排
- 可以实现自动化联动
- 支持远程控制
产品方向:
- 智能家居控制中心
- 场景自动化平台
- 智能生活助手
3.2 移动办公自动化 ★★★★☆
--------------------------------
应用场景:
- 办公流程自动化
- 数据录入自动化
- 报表自动生成
- 审批流程自动化
技术复用:
✓ 自动化操作 → 流程自动化
✓ 数据采集 → 数据提取
✓ 任务调度 → 定时任务
✓ UI识别 → 表单识别和填写
优势:
- 可以自动化重复性工作
- 提高工作效率
- 减少人为错误
- 支持复杂流程
产品方向:
- 办公自动化平台
- RPA机器人流程自动化工具
- 智能办公助手
3.3 教育培训辅助工具 ★★★☆☆
--------------------------------
应用场景:
- 在线学习辅助
- 作业自动批改
- 学习进度跟踪
- 智能答疑
技术复用:
✓ UI识别 → 题目识别
✓ 自动化操作 → 答题辅助
✓ 数据采集 → 学习数据分析
✓ 任务调度 → 学习计划管理
优势:
- 可以辅助学习过程
- 可以跟踪学习进度
- 可以提供个性化建议
- 支持多种学习场景
产品方向:
- 智能学习助手
- 在线教育辅助工具
- 学习分析平台
3.4 游戏辅助工具 ★★★☆☆
--------------------------------
应用场景:
- 游戏自动化操作
- 游戏数据采集
- 游戏策略优化
- 游戏测试
技术复用:
✓ 自动化操作 → 游戏操作
✓ UI识别 → 游戏状态识别
✓ 任务调度 → 游戏任务管理
✓ 事件监听 → 游戏事件响应
优势:
- 可以实现复杂游戏操作
- 可以采集游戏数据
- 可以优化游戏策略
- 支持多账号管理
产品方向:
- 游戏辅助工具
- 游戏数据分析平台
- 游戏测试工具
3.5 健康医疗辅助 ★★★☆☆
--------------------------------
应用场景:
- 健康数据采集
- 用药提醒
- 健康监测
- 医疗辅助
技术复用:
✓ 数据采集 → 健康数据收集
✓ 任务调度 → 提醒系统
✓ 自动化操作 → 操作辅助
✓ 事件监听 → 健康事件响应
优势:
- 可以采集健康数据
- 可以提供智能提醒
- 可以辅助医疗操作
- 支持健康管理
产品方向:
- 健康管理应用
- 医疗辅助工具
- 健康数据分析平台
================================================================================
四、技术平台化方向
================================================================================
4.1 自动化操作平台 ★★★★★
--------------------------------
定位:通用的移动端自动化操作平台
核心能力:
- 可视化任务编排
- 多应用支持
- 插件化架构
- 云端任务分发
技术架构:
- 组件化架构 → 插件化平台
- 任务调度系统 → 任务编排引擎
- 无障碍服务 → 操作执行引擎
- 设备管理 → 设备资源池
应用场景:
- 自动化测试
- 数据采集
- 流程自动化
- 辅助工具开发
4.2 设备管理和控制平台 ★★★★☆
--------------------------------
定位:企业级设备管理和远程控制平台
核心能力:
- 批量设备管理
- 远程控制
- 策略下发
- 监控和统计
技术架构:
- 设备管理模块 → 设备管理平台
- 远程控制 → 远程协助系统
- 任务分发 → 策略执行引擎
- 状态监控 → 监控系统
应用场景:
- 企业MDM
- 远程技术支持
- 设备监控
- 批量操作
4.3 数据采集和分析平台 ★★★★☆
--------------------------------
定位:移动端数据采集和分析平台
核心能力:
- 多维度数据采集
- 实时数据处理
- 数据分析和可视化
- 数据导出
技术架构:
- UI识别 → 数据提取引擎
- 事件监听 → 行为追踪系统
- 数据存储 → 数据仓库
- 任务调度 → 采集任务管理
应用场景:
- 用户行为分析
- 竞品分析
- 市场调研
- 数据挖掘
================================================================================
五、商业化方向
================================================================================
5.1 SaaS服务模式
---------------
✓ 自动化测试云平台
✓ 设备管理云服务
✓ 数据采集云服务
✓ 辅助工具云服务
5.2 私有化部署
---------------
✓ 企业级自动化测试平台
✓ 企业设备管理平台
✓ 企业内部数据采集平台
5.3 技术授权
---------------
✓ 自动化操作SDK
✓ 设备管理SDK
✓ 数据采集SDK
5.4 定制开发
---------------
✓ 行业解决方案
✓ 定制化工具开发
✓ 技术咨询服务
================================================================================
六、技术迁移和适配
================================================================================
6.1 跨平台扩展
---------------
Android → iOS
- 需要适配iOS的辅助功能API
- 技术方案需要重新设计
- 但核心思路可以复用
Android → Web
- 可以使用浏览器自动化框架Selenium、Puppeteer
- 技术栈需要调整
- 但架构思路可以复用
6.2 技术升级
---------------
无障碍服务 → UI Automator
- 更专业的测试框架
- 更好的稳定性
- 需要适配
无障碍服务 → Appium
- 跨平台支持
- 更丰富的功能
- 需要学习成本
6.3 架构演进
---------------
组件化 → 微服务:
- 服务拆分
- 独立部署
- 需要基础设施支持
单体架构 → 云原生:
- 容器化部署
- 弹性伸缩
- 需要云平台支持
================================================================================
七、市场机会分析
================================================================================
7.1 高价值领域(优先级高)
---------------------------
1. 移动应用自动化测试
- 市场规模大
- 需求稳定
- 技术门槛适中
- 竞争激烈但有机会
2. 无障碍辅助工具
- 社会价值高
- 政策支持
- 用户需求明确
- 技术门槛较高
3. 企业设备管理
- 企业需求大
- 付费意愿强
- 技术门槛高
- 需要行业经验
7.2 潜力领域(优先级中)
---------------------------
1. 数据采集和分析
- 市场需求增长
- 技术门槛适中
- 需要合规考虑
- 竞争激烈
2. 智能家居控制
- 市场前景好
- 技术门槛适中
- 需要生态支持
- 竞争激烈
3. 办公自动化
- 企业需求大
- 技术门槛适中
- 需要行业理解
- 竞争激烈
7.3 新兴领域(优先级低)
---------------------------
1. 教育培训辅助
- 市场潜力大
- 技术门槛适中
- 需要教育行业经验
- 政策风险
2. 健康医疗辅助
- 社会价值高
- 技术门槛高
- 需要医疗资质
- 监管严格
================================================================================
八、实施建议
================================================================================
8.1 短期3-6个月
------------------
1. 选择1-2个核心领域深耕
- 建议:自动化测试 + 无障碍辅助
- 原因:技术复用度高,市场需求明确
2. 技术重构和优化
- 代码质量提升
- 架构优化
- 性能优化
3. 产品化改造
- 通用化改造
- 配置化设计
- 用户体验优化
8.2 中期6-12个月
------------------
1. 平台化建设
- 可视化配置
- 插件化架构
- 云端服务
2. 商业化探索
- 产品定价
- 销售渠道
- 客户验证
3. 生态建设
- 开发者社区
- 插件市场
- 合作伙伴
8.3 长期12个月以上
------------------
1. 规模化发展
- 市场拓展
- 产品矩阵
- 品牌建设
2. 技术领先
- 技术创新
- 专利布局
- 标准制定
3. 生态完善
- 平台生态
- 合作伙伴网络
- 行业影响力
================================================================================
九、风险提示
================================================================================
9.1 技术风险
------------
- 无障碍服务可能被系统限制
- 不同Android版本兼容性问题
- 应用更新导致功能失效
- 性能和安全问题
9.2 法律风险
------------
- 自动化操作可能违反服务条款
- 数据采集需要合规
- 需要遵守相关法律法规
- 知识产权风险
9.3 市场风险
------------
- 竞争激烈
- 市场需求变化
- 技术替代风险
- 商业模式风险
9.4 运营风险
------------
- 技术团队能力
- 产品迭代速度
- 客户服务能力
- 资金和资源
================================================================================
十、总结
================================================================================
项目重构后具有广泛的应用前景,核心优势在于:
1. 技术能力通用性强
- 无障碍服务可以应用于多个领域
- 组件化架构便于扩展
- 任务调度系统可以复用
2. 市场需求明确
- 自动化测试需求稳定
- 无障碍辅助社会价值高
- 企业服务市场空间大
3. 商业化路径清晰
- SaaS模式可行
- 私有化部署有市场
- 技术授权有需求
建议优先发展:
1. 移动应用自动化测试平台(技术复用度高,市场明确)
2. 无障碍辅助工具(社会价值高,政策支持)
3. 企业设备管理平台(付费意愿强,需求稳定)
通过技术重构、产品化改造和平台化建设,项目有望在多个领域实现商业化成功。
================================================================================
分析时间2026-01-05
================================================================================

378
项目评价报告.txt Normal file
View File

@@ -0,0 +1,378 @@
================================================================================
项目评价报告
================================================================================
项目名称fuzhu抖音自动化辅助工具
评价时间2026-01-05
评价维度:架构设计、代码质量、技术选型、功能完整性、可维护性、安全性等
================================================================================
一、总体评价
================================================================================
综合评分:★★★☆☆ (3.5/5.0)
这是一个功能相对完整的Android自动化工具项目采用了组件化架构设计
具备一定的技术深度和业务复杂度。项目在架构设计上有亮点,但在代码质量、
安全性、可维护性等方面还有较大改进空间。
================================================================================
二、各维度详细评价
================================================================================
2.1 架构设计 ★★★★☆ (4.0/5.0)
--------------------------------
优点:
✓ 采用组件化架构,支持独立运行和集成模式切换,设计思路先进
✓ 自定义ARouter路由框架实现模块间解耦
✓ 使用APT技术实现依赖注入技术选型合理
✓ 模块划分清晰app、common、order、personal、wechat等
✓ 支持组件化/集成化模式动态切换,灵活性好
不足:
✗ 组件化实现不够彻底部分模块order、personal更像是示例代码
✗ 缺少统一的架构文档和设计说明
✗ 模块间依赖关系不够清晰,存在循环依赖风险
建议:
- 完善组件化架构文档
- 明确模块职责边界
- 建立模块依赖关系图
2.2 代码质量 ★★☆☆☆ (2.5/5.0)
--------------------------------
优点:
✓ 基本功能实现完整
✓ 使用了部分设计模式(如策略模式)
✓ 部分工具类封装较好
不足:
✗ 代码规范性较差:
- 大量硬编码字符串和魔法数字
- 变量命名不规范如ChangLiang、qidong等
- 缺少必要的注释和文档
- 代码风格不统一
✗ 代码结构问题:
- MainActivity类过于庞大1400+行),职责不清
- 大量静态变量ChangLiang类不利于测试和维护
- 缺少抽象和接口设计
- 业务逻辑和UI逻辑耦合严重
✗ 异常处理不足:
- 缺少统一的异常处理机制
- 很多地方直接catch Exception处理过于粗糙
- 缺少错误日志和监控
建议:
- 重构MainActivity拆分职责
- 使用配置类替代静态常量
- 建立统一的异常处理机制
- 完善代码注释和文档
- 引入代码规范检查工具如CheckStyle、PMD
2.3 技术选型 ★★★☆☆ (3.0/5.0)
--------------------------------
优点:
✓ 使用了主流的技术栈:
- OkGo、Retrofit进行网络请求
- RxJava处理异步操作
- OrmLite进行数据持久化
- DataBinding简化UI代码
✓ 自定义路由框架,技术深度较好
✓ 使用无障碍服务实现自动化,技术方案合理
不足:
✗ 部分依赖版本较旧:
- OkGo 3.0.4(已停止维护)
- Retrofit 2.5.0建议升级到2.9+
- EventBus 2.4.0建议升级到3.x
✗ 技术栈混用:
- 同时使用EventBus和LiveEventBus增加学习成本
- 网络请求框架混用OkGo和Retrofit
✗ 缺少现代化技术:
- 未使用Kotlin虽然项目是Java但可以考虑迁移
- 未使用协程处理异步
- 未使用Jetpack组件ViewModel、LiveData等
建议:
- 统一网络请求框架
- 升级依赖版本
- 考虑引入Jetpack组件
- 评估Kotlin迁移的可行性
2.4 功能完整性 ★★★★☆ (4.0/5.0)
--------------------------------
优点:
✓ 核心功能完整:
- 账号管理
- 自动化任务执行
- 群控功能
- 设备管理
✓ 功能覆盖全面:
- 支持多种任务类型(关注、取关、点赞、评论等)
- 支持云端/本地控制
- 支持设备激活和注销
不足:
✗ 部分功能实现不够完善:
- 错误处理不够细致
- 缺少功能开关和配置
- 用户体验有待提升
✗ 缺少辅助功能:
- 缺少数据统计和分析
- 缺少操作日志查看
- 缺少任务历史记录
建议:
- 完善错误处理和用户提示
- 增加数据统计功能
- 优化用户体验
2.5 可维护性 ★★☆☆☆ (2.0/5.0)
--------------------------------
优点:
✓ 模块化设计,便于维护
✓ 部分工具类封装较好
不足:
✗ 代码可读性差:
- 大量中文拼音变量名
- 缺少必要的注释
- 代码逻辑复杂,难以理解
✗ 可测试性差:
- 大量静态依赖,难以进行单元测试
- 业务逻辑和UI耦合难以测试
- 缺少测试代码
✗ 可扩展性不足:
- 硬编码较多,扩展困难
- 缺少插件化机制
- 配置不够灵活
建议:
- 重构代码,提高可读性
- 编写单元测试和集成测试
- 建立配置管理系统
- 完善文档
2.6 安全性 ★★☆☆☆ (2.0/5.0)
--------------------------------
优点:
✓ 使用HTTPS进行网络通信
✓ 设备激活机制,防止未授权使用
不足:
✗ 安全风险较高:
- 签名文件密码硬编码123456
- 服务器地址硬编码
- 缺少数据加密
- 敏感信息可能泄露
✗ 权限使用不当:
- 申请了过多权限
- 部分权限使用说明不清晰
- 缺少权限使用说明
✗ 代码混淆不足:
- ProGuard配置可能不够完善
- 关键逻辑可能被反编译
建议:
- 移除硬编码的敏感信息
- 使用配置文件或环境变量
- 加强代码混淆
- 对敏感数据进行加密
- 完善权限使用说明
2.7 性能优化 ★★☆☆☆ (2.5/5.0)
--------------------------------
优点:
✓ 使用了异步处理RxJava
✓ 数据库操作使用了ORM框架
不足:
✗ 性能问题:
- 主线程可能执行耗时操作
- 内存泄漏风险(静态变量、未释放的资源)
- 频繁的网络请求可能影响性能
- 缺少性能监控
✗ 资源管理:
- 可能缺少资源释放
- 图片加载未优化
- 数据库操作可能未优化
建议:
- 进行性能测试和优化
- 使用LeakCanary检测内存泄漏
- 优化网络请求频率
- 添加性能监控
2.8 文档和注释 ★★☆☆☆ (2.0/5.0)
--------------------------------
优点:
✓ 有项目文档(虽然不够完善)
✓ 部分关键类有注释
不足:
✗ 文档不完善:
- 缺少API文档
- 缺少开发指南
- 缺少部署文档
- 缺少故障排查文档
✗ 代码注释不足:
- 大部分代码缺少注释
- 复杂逻辑缺少说明
- 参数和返回值缺少文档
建议:
- 完善项目文档
- 编写API文档
- 增加代码注释
- 建立文档维护机制
================================================================================
三、主要问题总结
================================================================================
3.1 代码质量问题
----------------
1. MainActivity类过于庞大需要拆分
2. 大量硬编码和魔法数字
3. 变量命名不规范
4. 缺少异常处理机制
5. 代码注释不足
3.2 架构设计问题
----------------
1. 组件化实现不够彻底
2. 模块职责不够清晰
3. 缺少统一的架构文档
3.3 安全性问题
----------------
1. 敏感信息硬编码
2. 代码混淆可能不足
3. 权限使用说明不清
3.4 可维护性问题
----------------
1. 代码可读性差
2. 可测试性差
3. 缺少测试代码
4. 文档不完善
================================================================================
四、改进建议
================================================================================
4.1 短期改进1-2个月
-----------------------
1. 代码规范:
- 统一代码风格
- 规范变量命名
- 增加代码注释
- 引入代码检查工具
2. 重构关键类:
- 拆分MainActivity
- 重构ChangLiang类
- 提取公共逻辑
3. 完善文档:
- 完善项目文档
- 增加API文档
- 编写开发指南
4.2 中期改进3-6个月
-----------------------
1. 架构优化:
- 完善组件化架构
- 明确模块职责
- 优化模块依赖
2. 技术升级:
- 升级依赖版本
- 统一技术栈
- 引入Jetpack组件
3. 测试完善:
- 编写单元测试
- 编写集成测试
- 建立测试流程
4.3 长期改进6-12个月
-----------------------
1. 架构重构:
- 考虑MVVM架构
- 引入依赖注入框架如Dagger
- 实现插件化机制
2. 技术迁移:
- 评估Kotlin迁移
- 使用协程替代RxJava
- 引入Jetpack Compose
3. 工程化:
- 建立CI/CD流程
- 完善监控体系
- 建立代码审查机制
================================================================================
五、亮点总结
================================================================================
1. 组件化架构设计思路先进,支持独立运行和集成模式
2. 自定义路由框架,技术深度较好
3. 功能相对完整,覆盖主要业务场景
4. 使用APT技术实现依赖注入技术选型合理
5. 无障碍服务封装较好,自动化实现方案可行
================================================================================
六、风险提示
================================================================================
1. 法律风险:
- 自动化操作可能违反平台服务条款
- 需要确保合规使用
2. 技术风险:
- 依赖无障碍服务,稳定性受系统版本影响
- 平台更新可能导致功能失效
3. 安全风险:
- 敏感信息硬编码
- 需要加强安全防护
4. 维护风险:
- 代码可维护性较差
- 需要投入大量时间进行重构
================================================================================
七、总结
================================================================================
这是一个功能相对完整、技术栈较为现代的项目,在架构设计上有一定亮点,
但在代码质量、安全性、可维护性等方面还有较大改进空间。
建议:
1. 优先解决代码质量和安全问题
2. 逐步重构关键模块
3. 完善文档和测试
4. 建立代码规范和审查机制
如果能够按照上述建议进行改进,项目的质量和可维护性将得到显著提升。
================================================================================
评价人AI Assistant
评价日期2026-01-05
================================================================================