这个bug最早是在国外的交流群发现的,然后好像本坛的用户也有说过?就是在应用内删除文件的时候没有被永久删除,而是放到了回收站当中,然而当前版本并没有回收站的入口,所以文件并没有消失,而是重命名后修改了后缀(添加.trash隐藏)而已。
ad版的回收站功能是后面才出的,很可能是三哥搬代码没搬完,这种事在印度好像都已经见怪不怪了。
昨天晚上我试了一下pro版,界面还是做得比较像单纯的播放器的,没有其它乱七八糟的功能,试了一下删除,也确实有这个bug存在。

1 (2).jpg (11.83 KB, 下载次数: 0)
下载附件
2025-7-15 11:07 上传
我收集了各路大神的修改版,交流群里面已经很多人反馈了,没人愿意修复这个问题,当然我潜水在里面肯定是不会说话的
像这种bug,可以根据弹出的吐司、文件后缀入手。因为APP对文件有重命名动作,所以我感觉从这里入手会比较简单。
反编译apk,dex内直接搜索字符串.trash,有几个结果,点进a类。

1.png (76.92 KB, 下载次数: 0)
下载附件
2025-7-15 09:56 上传
public static boolean a(File file, Lw lw) {
long j;
try {
File b2 = b(file); //调用下面的b方法
if (file.renameTo(b2)) {
RecycleBinItem a2 = RecycleBinItem.a.a(file, b2, lw);
SW.p().getClass();
try {
j = SW.n.d.insertWithOnConflict("RecycleBin", null, a2.c(), 5);
} catch (Exception unused) {
j = -1;
}
if (j != -1) {
of0.a(of0.e(file.getPath()), b2.getPath());
return true;
}
b2.renameTo(file);
}
} catch (Exception unused2) {
}
return false;
}
public static File b(File file) {
String D = qp.D(Files.o(file.getPath()));
if (D == null || D.length() == 0) {
String replaceAll = Pattern.compile("_+").matcher(Pattern.compile("[^a-zA-Z0-9._-]").matcher(Pattern.compile("[\\s\\[\\](){}]").matcher(Files.r(file.getPath())).replaceAll("_")).replaceAll("_")).replaceAll("_");
char[] cArr = {'_'};
int length = replaceAll.length() - 1;
int i = 0;
boolean z = false;
while (i = 1) {
i2 = -1;
break;
}
if (charAt == cArr[i2]) {
break;
}
i2++;
}
boolean z2 = i2 >= 0;
if (z) {
if (!z2) {
break;
}
length--;
} else if (z2) {
i++;
} else {
z = true;
}
}
D = replaceAll.subSequence(i, length + 1).toString();
}
Hu0 hu0 = a;
File file2 = new File((String) hu0.getValue(), o0.f(D, ".trash"));
int i3 = 1;
while (file2.exists()) {
file2 = new File((String) hu0.getValue(), D + '_' + i3 + ".trash");
i3++;
}
return file2;
}
a和b方法的代码如上,可以发现他在重命名文件后,将文件移动到回收站里,并返回a状态。
那就接着往上找,查找a方法调用,看看他跑去哪里了。

2.png (32.91 KB, 下载次数: 0)
下载附件
2025-7-15 10:19 上传
有几个结果,进入public static final a(LSW;Ljava/io/File;LLw;Lcom/mxtech/media/MediaExtensions;ZLvm;)Ljava/lang/Object;,这个是删除逻辑,用于决定该如何处理文件。
public static final Object a(SW sw, File file, Lw lw, MediaExtensions mediaExtensions, boolean z, vm vmVar) {
if ((vmVar instanceof Zr) == false) goto L45;
Zr zr = (Zr) vmVar;
int i = zr.e;
if ((i & Integer.MIN_VALUE) == 0) goto L45;
zr.e = i - Integer.MIN_VALUE;
L46:
Object obj = zr.d;
mn mnVar = mn.d;
int i2 = zr.e;
if (i2 == 0) goto L52;
if (i2 != 1) goto L51;
Bk0.a(obj);
L74:
return Boolean.TRUE;
L51:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
L52:
Bk0.a(obj);
SystemClock.uptimeMillis();
if (z == false) goto L55;
boolean a2 = Files.b(e.K, file);
L56:
if (a2 == false) goto L70; //根据a2的返回值决定该如何处理文件
int i3 = uH0.a;
mediaExtensions.getClass();
if (mediaExtensions.i(file.getPath()) == false) goto L66;
AsyncTask.SERIAL_EXECUTOR.execute(new M1(4, file));
sw.i(file, sw.o(sw.n(file.getParent()), file.getName()));
L62:
e = move-exception;
new Integer(Log.e("MX.DeleteUtil", "", e));
L66:
Object obj2 = Zt.a;
dn b2 = a.b();
as asVar = new as(file, null);
zr.e = 1;
if (sN.r(b2, asVar, zr) != mnVar) goto L74;
return mnVar;
L70:
int i4 = uH0.a;
return Boolean.FALSE;
L55:
a2 = a.a(file, lw);
L45:
zr = new Zr(vmVar);
goto L46
}
a2用于决定该如何处理文件,直接删还是丢回收站。在a2之上又有个if (z == false) goto L55;,z来自该方法参数中的boolean,看看他是从哪里进来的。
位于cs类中的public final invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;,如下图:
可以看到这个布尔值来自上面的v5,那看一下v16在哪里。

3.png (53.16 KB, 下载次数: 0)
下载附件
2025-7-15 10:34 上传
[Java] 纯文本查看 复制代码
if-nez v10, :cond_c5
.line 191
.line 192
if-eqz v5, :cond_c2
.line 193
.line 194
goto :goto_c5
.line 195
:cond_c2
const/16 v16, 0x0
.line 196
.line 197
goto :goto_c7
.line 198
:cond_c5
:goto_c5
const/16 v16, 0x1
往上翻一翻,这里很清楚的看见v16来自这个判断。但是上面又有v10和v5两个判断。先看第一个v10,看看v10从哪里来的。
找了一下其实就来自方法代码开头。
.method public final invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
.registers 20
.line 1
move-object/from16 v7, p0
.line 2
.line 3
const/4 v0, 0x1
.line 4
sget-object v8, Lmn;->d:Lmn;
.line 5
.line 6
iget v1, v7, Lcs;->r:I
.line 7
.line 8
const-string v9, "MX.List.Builder.Local"
.line 9
.line 10
iget-boolean v10, v7, Lcs;->t:Z //v10从这里取值
.line 11
.line 12
const/4 v11, 0x0
.line 13
if-eqz v1, :cond_35
.line 14
.line 15
if-ne v1, v0, :cond_2d
这里是iget-boolean,那再找一下iput-boolean,看看在哪里。
搜索结果显示就在该类的
.method public constructor (Z[LLw;Ljava/util/ArrayList;Ltm;)V
.registers 5
.line 1
iput-boolean p1, p0, Lcs;->t:Z
.line 2
.line 3
iput-object p2, p0, Lcs;->x:[LLw;
.line 4
.line 5
iput-object p3, p0, Lcs;->y:Ljava/util/ArrayList;
.line 6
.line 7
const/4 p1, 0x2
.line 8
invoke-direct {p0, p1, p4}, Lzu0;->(ILtm;)V
.line 9
.line 10
.line 11
return-void
.end method
所以iput-boolean p1, p0, Lcs;->t:Z,对p1赋值1即可直接永久删除文件。

2 (2).jpg (11.2 KB, 下载次数: 0)
下载附件
2025-7-15 11:07 上传
测试了一下,提示已经变了,再检查使用空间已经减少,确认文件已经被永久删除。
在使用中我还发现一个问题,不知道是谁改的。就是启动的时候好像会黑一下,对比了一下原版,原来是启动界面的logo没了。但是改得一言难尽……
现在的高安卓版本,特别是原生系统,是强制冷启动显示logo的,和activity启动顺序无关。如果删掉icon,就会变成一个安卓机器人,很丑。我把这个破解版放到皮鞋8里测试,果然不出所料,启动背景是黑的,然后中间是一个大icon,更丑了。
如果要屏蔽启动logo,应该将图片替换为透明或者白色。
[XML] 纯文本查看 复制代码
true
@style/activityAnimation_res_0x7f1304a4
@color/transparent
@color/transparent
@color/splash_window_background
@drawable/mxplayer_logo_12
false
true
true
在上面的xml文件中,
android:windowSplashScreenAnimatedIcon
应该设置
[color=]@color/transparent
属性,屏蔽启动logo。
像小米手机是魔改系统,已经从系统底层干掉这个属性了,如果不设置windowSplashScreenAnimatedIcon,就不会显示任何logo。如果是魔改系统,才可以直接删掉这个item屏蔽logo。