Skip to content

Commit

Permalink
fix: Clean Recent Chat - DeleteMode : including group chat, other cha…
Browse files Browse the repository at this point in the history
…t, all chat, topChat
  • Loading branch information
suzhelan committed Dec 19, 2023
1 parent d391acc commit 794ab8e
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 8 deletions.
68 changes: 62 additions & 6 deletions app/src/main/java/top/linl/hook/FixCleanRecentChat.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@

package top.linl.hook;

import android.app.Activity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import cc.ioctl.util.HookUtils;
import io.github.qauxv.util.Toasts;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
Expand All @@ -50,14 +50,17 @@
*/
public class FixCleanRecentChat {

private static final HashMap<Object, Integer> viewHolderList = new LinkedHashMap<>();
private static final ConcurrentHashMap<Object, Integer> viewHolderList = new ConcurrentHashMap<>();
private static int deleteTextViewId;
private final CleanRecentChat cleanRecentChat;

private Activity activity;

public FixCleanRecentChat(CleanRecentChat cleanRecentChat) {
this.cleanRecentChat = cleanRecentChat;
}


private void hookGetDeleteViewId() {
Class<?> superClass = ClassUtils.getClass("com.tencent.qqnt.chats.biz.guild.GuildDiscoveryItemBuilder").getSuperclass();
Class<?> findClass = null;
Expand Down Expand Up @@ -100,10 +103,11 @@ public void loadHook() throws Exception {
Method onCreateMethod = MethodTool.find("com.tencent.mobileqq.activity.home.Conversation").name("onResume").params(boolean.class).get();
HookUtils.hookAfterIfEnabled(cleanRecentChat, onCreateMethod, param -> {
ImageView imageView = FieIdUtils.getFirstField(param.thisObject, ImageView.class);
activity = (Activity) imageView.getContext();
imageView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
new Thread(new DeleteAllItemTask()).start();
cleanRecentChat.showDialog(activity);
return true;
}
});
Expand Down Expand Up @@ -134,14 +138,28 @@ private void hookOnHolder() {
viewHolderList.put(param.thisObject, adapterIndex);
});

Method onCreate = MethodTool.find("com.tencent.qqnt.chats.core.adapter.ChatsListAdapter")
.name("onCreateViewHolder")
.params(android.view.ViewGroup.class, int.class)
.get();
HookUtils.hookAfterIfEnabled(cleanRecentChat, onCreate, param -> {
viewHolderList.put(param.getResult(), (int) param.args[1]);
});
}

private static class DeleteAllItemTask implements Runnable {
public static class DeleteAllItemTask implements Runnable {

private static final AtomicReference<Method> deleteMethod = new AtomicReference<>();
private static Class<?> utilType;
private static Field itemField;

public boolean isDeleteTopMsg = false;
public String deleteMode;

public DeleteAllItemTask(String deleteMode) {
this.deleteMode = deleteMode;
}

private Object findItemField(Object viewHolder) throws IllegalAccessException {
if (itemField != null) {
return itemField.get(viewHolder);
Expand Down Expand Up @@ -185,6 +203,9 @@ private Class<?> findUtilClassType(Object viewHolder) {
throw new RuntimeException(e);
}
}
if (utilType == null) {
throw new RuntimeException("not find Class , viewHolder ClassName is " + viewHolder.getClass().getName());
}
return utilType;
}

Expand Down Expand Up @@ -226,6 +247,11 @@ public void run() {
if (size == 0) {
try {
//停一下等待ItemHolder重新bind到屏幕上 然后继续删除
/*
* 假设一次能清理屏幕中的 8 个item
* 2000 / 100 * 8 = 400 (个item)
* 清掉所有聊天项应该戳戳有余 有问题调高延迟和时长应该可以解决
*/
TimeUnit.MILLISECONDS.sleep(100);
continue;
} catch (InterruptedException e) {
Expand All @@ -237,21 +263,51 @@ public void run() {
Map.Entry<Object, Integer> viewHolderEntry = iterator.next();
try {
Object recentContactItemHolder = viewHolderEntry.getKey();
if (recentContactItemHolder == null) {
continue;
}
//delete util
Object util = FieIdUtils.getFirstField(recentContactItemHolder, findUtilClassType(recentContactItemHolder));//util run time obj
int adapterIndex = viewHolderEntry.getValue();//call param 1
/*
* { uid=0000,
* title=name,
* contactType=2,
* unreadCount=UnreadInfo(type=2, count=437).count ,
* showTime=晚上8:03,
* summary=八 ,
* isTop=true,
* isDraft=false}
* */
Object itemInfo = findItemField(recentContactItemHolder);//call param 2
String itemToString = String.valueOf(itemInfo);
//面向字符串编程
if (deleteMode.equals("清理群消息")) {
if (!itemToString.contains("contactType=2")) {
continue;
}
} else if (deleteMode.equals("清理其他消息")) {
if (itemToString.contains("contactType=2") || itemToString.contains("contactType=1")) {
continue;
}
}
if (!isDeleteTopMsg) {
if (itemToString.contains("isTop=true")) {
continue;
}
}
Object itemBinder = FieIdUtils.getFirstField(recentContactItemHolder,
ClassUtils.getClass("com.tencent.qqnt.chats.core.adapter.holder.RecentContactItemBinding"));//call param 3
int viewId = deleteTextViewId;//call param 4
getDeleteMethod(recentContactItemHolder).invoke(util, adapterIndex, itemInfo, itemBinder, viewId);
deleteQuantity++;
} catch (Exception e) {
throw new RuntimeException(e);

}
iterator.remove();
}
}
System.gc();//调用gc 防止viewHolder还没被回收
Toasts.show("已清理结束 数量" + deleteQuantity + "个");
}
}
Expand Down
29 changes: 27 additions & 2 deletions app/src/main/java/xyz/nextalone/hook/CleanRecentChat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

package xyz.nextalone.hook

import android.annotation.SuppressLint
import android.content.Context
import android.widget.ImageView
import android.widget.RelativeLayout
Expand Down Expand Up @@ -67,10 +68,32 @@ object CleanRecentChat : CommonSwitchFunctionHook(arrayOf(NFriendsStatusUtil_isC
private const val INCLUDE_TOPPED = "CleanRecentChat_include_topped"
private var includeTopped = getDefaultConfig().getBooleanOrDefault(INCLUDE_TOPPED, false)

@SuppressLint("StaticFieldLeak")
private var fixCleanRecentChat: FixCleanRecentChat? = null
fun showDialog(context: Context) {
val contextWrapper = CommonContextWrapper.createMaterialDesignContext(context)
val list = listOf("清理群消息", "清理其他消息", "清理所有消息")
MaterialDialog(contextWrapper).show {
var containsTops = false
title(text = "消息清理")

checkBoxPrompt(text = "包含置顶消息", isCheckedDefault = includeTopped) { checked ->
containsTops = checked
}
listItems(items = list) { dialog, _, text ->
Toasts.showToast(dialog.context, Toasts.TYPE_INFO, text, Toasts.LENGTH_SHORT)
val deleteAllItemTask = FixCleanRecentChat.DeleteAllItemTask(text as String?)
deleteAllItemTask.isDeleteTopMsg = containsTops
Thread(deleteAllItemTask).start()

}.ignoreResult()
}
}

override fun initOnce(): Boolean = throwOrTrue {
if (QAppUtils.isQQnt()) {
val fix = FixCleanRecentChat(this)
fix.loadHook()
fixCleanRecentChat = FixCleanRecentChat(this)
fixCleanRecentChat!!.loadHook()
} else {
DexKit.requireMethodFromCache(NConversation_onCreate)
.hookAfter(this) {
Expand Down Expand Up @@ -118,6 +141,8 @@ object CleanRecentChat : CommonSwitchFunctionHook(arrayOf(NFriendsStatusUtil_isC
== 1 联系人
== 103 公众号
== 2 群聊
== 7 群助手
== 134 我的电脑
== 131 我的关联QQ账号 uid=9992
class: com.tencent.qqnt.chats.core.adapter.holder.deleteMsg extend View.OnClickListener
*/
Expand Down

0 comments on commit 794ab8e

Please sign in to comment.