Skip to content
This repository has been archived by the owner on Sep 15, 2024. It is now read-only.

Commit

Permalink
feat: better SaveImages
Browse files Browse the repository at this point in the history
  • Loading branch information
mkx173 committed Mar 9, 2024
1 parent 3b34c91 commit 0b78b44
Showing 1 changed file with 70 additions and 38 deletions.
108 changes: 70 additions & 38 deletions app/src/main/java/gm/tieba/tabswitch/hooker/add/SaveImages.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import androidx.annotation.NonNull;

Expand All @@ -19,19 +21,26 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import gm.tieba.tabswitch.XposedContext;
import gm.tieba.tabswitch.dao.AcRules;
import gm.tieba.tabswitch.hooker.IHooker;
import gm.tieba.tabswitch.hooker.Obfuscated;
import gm.tieba.tabswitch.hooker.deobfuscation.Matcher;
import gm.tieba.tabswitch.hooker.deobfuscation.MatcherProperties;
import gm.tieba.tabswitch.hooker.deobfuscation.ReturnTypeMatcher;
import gm.tieba.tabswitch.util.ClassMatcherUtils;
import gm.tieba.tabswitch.util.FileUtils;
import gm.tieba.tabswitch.util.ReflectUtils;
import gm.tieba.tabswitch.widget.TbToast;
import kotlin.text.StringsKt;

public class SaveImages extends XposedContext implements IHooker {
public class SaveImages extends XposedContext implements IHooker, Obfuscated {
private ArrayList<String> mList;
private Field mDownloadImageViewField;

Expand All @@ -41,18 +50,38 @@ public String key() {
return "save_images";
}

public void hook() throws Throwable {
final var method = ReflectUtils.findFirstMethodByExactType(
"com.baidu.tbadk.coreExtra.view.ImagePagerAdapter", ArrayList.class
@Override
public List<? extends Matcher> matchers() {
return List.of(
new ReturnTypeMatcher<>(LinearLayout.class, MatcherProperties.create().useClassMatcher(ClassMatcherUtils.usingString("分享弹窗触发分享:分享成功")))
);
XposedBridge.hookMethod(method, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(final MethodHookParam param) throws Throwable {
mList = new ArrayList<>((ArrayList<String>) param.args[0]);
mList.removeIf(o -> o.startsWith("####mLiveRoomPageProvider"));
}

public void hook() throws Throwable {
AcRules.findRule(matchers(), (matcher, clazz, method) -> {
switch (matcher) {
case "分享弹窗触发分享:分享成功/LinearLayout":
XposedHelpers.findAndHookMethod(clazz, sClassLoader, method, int.class, int.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
LinearLayout downloadIconView = (LinearLayout) param.getResult();
downloadIconView.setOnLongClickListener(saveImageListener);
}
});
break;
}
});

XposedBridge.hookMethod(
ReflectUtils.findFirstMethodByExactType("com.baidu.tbadk.coreExtra.view.ImagePagerAdapter", ArrayList.class),
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(final MethodHookParam param) throws Throwable {
mList = new ArrayList<>((ArrayList<String>) param.args[0]);
mList.removeIf(o -> o.startsWith("####mLiveRoomPageProvider"));
}
});

Class<?> imageViewerBottomLayoutClass = XposedHelpers.findClass("com.baidu.tbadk.coreExtra.view.ImageViewerBottomLayout", sClassLoader);
ArrayList<Field> declaredFields = new ArrayList<>(Arrays.asList(imageViewerBottomLayoutClass.getDeclaredFields()));
declaredFields.removeIf(o -> o.getType() != ImageView.class);
Expand All @@ -63,41 +92,44 @@ protected void beforeHookedMethod(final MethodHookParam param) throws Throwable
sClassLoader, Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(final XC_MethodHook.MethodHookParam param) throws Throwable {
final var context = ((Context) param.args[0]).getApplicationContext();
// R.id.download_icon
final var imageView = (ImageView) mDownloadImageViewField.get(param.thisObject);
imageView.setOnLongClickListener(v -> {
TbToast.showTbToast(String.format(Locale.CHINA,
"开始下载%d张图片", mList.size()), TbToast.LENGTH_SHORT);

final long baseTime = System.currentTimeMillis();
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA);
final String formattedTime = dateFormat.format(new Date(baseTime));

new Thread(() -> {
try {
final var list = new ArrayList<>(mList);
for (var i = 0; i < list.size(); i++) {
var url = list.get(i);
url = StringsKt.substringBeforeLast(url, "*", url);
saveImage(url, formattedTime + "_" + String.format(Locale.CHINA, "%02d", i), context);
}
new Handler(Looper.getMainLooper()).post(() ->
TbToast.showTbToast(String.format(Locale.CHINA,
"已保存%d张图片至手机相册", list.size()),
TbToast.LENGTH_SHORT));
} catch (final IOException | NullPointerException e) {
new Handler(Looper.getMainLooper()).post(() ->
TbToast.showTbToast("保存失败", TbToast.LENGTH_SHORT));
}
}).start();
return true;
});
imageView.setOnLongClickListener(saveImageListener);
}
});
}
}

final private View.OnLongClickListener saveImageListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
TbToast.showTbToast(String.format(Locale.CHINA,
"开始下载%d张图片", mList.size()), TbToast.LENGTH_SHORT);

final long baseTime = System.currentTimeMillis();
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA);
final String formattedTime = dateFormat.format(new Date(baseTime));

new Thread(() -> {
try {
final var list = new ArrayList<>(mList);
for (var i = 0; i < list.size(); i++) {
var url = list.get(i);
url = StringsKt.substringBeforeLast(url, "*", url);
saveImage(url, formattedTime + "_" + String.format(Locale.CHINA, "%02d", i), getContext());
}
new Handler(Looper.getMainLooper()).post(() ->
TbToast.showTbToast(String.format(Locale.CHINA,
"已保存%d张图片至手机相册", list.size()),
TbToast.LENGTH_SHORT));
} catch (final IOException | NullPointerException e) {
new Handler(Looper.getMainLooper()).post(() ->
TbToast.showTbToast("保存失败", TbToast.LENGTH_SHORT));
}
}).start();
return true;
}
};

private static void saveImage(final String url, final String filename, final Context context) throws IOException {
try (final var is = new URL(url).openStream()) {
final var bb = FileUtils.toByteBuffer(is);
Expand Down

0 comments on commit 0b78b44

Please sign in to comment.