Skip to content

Commit

Permalink
fix: SharePicExtHook on NT 4288
Browse files Browse the repository at this point in the history
  • Loading branch information
cinit committed Jul 24, 2023
1 parent e6a4e6b commit f6f9045
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 39 deletions.
152 changes: 113 additions & 39 deletions app/src/main/java/cc/ioctl/hook/msg/SharePicExtHook.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

package cc.ioctl.hook.msg;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
Expand All @@ -31,6 +32,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
import cc.hicore.QApp.QAppUtils;
import cc.ioctl.util.HookUtils;
import cc.ioctl.util.HostInfo;
import cc.ioctl.util.Reflex;
Expand All @@ -46,6 +48,7 @@
import io.github.qauxv.lifecycle.Parasitics;
import io.github.qauxv.ui.ResUtils;
import io.github.qauxv.util.Initiator;
import io.github.qauxv.util.IoUtils;
import io.github.qauxv.util.Log;
import io.github.qauxv.util.SyncUtils;
import io.github.qauxv.util.Toasts;
Expand All @@ -56,8 +59,12 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import kotlin.collections.ArraysKt;
import kotlin.jvm.functions.Function3;

@FunctionHookEntry
@UiItemAgentEntry
Expand Down Expand Up @@ -140,6 +147,90 @@ protected boolean initOnce() throws Exception {
Class<?> kAIOPictureData = Initiator.loadClass("com.tencent.mobileqq.richmediabrowser.model.AIOPictureData");
Constructor<?> ctorAIOPictureModel = kAIOPictureModel.getConstructor();
Method getPictureFile = Reflex.findMethodByTypes_1(kAIOPictureModel, File.class, kAIOPictureData, int.class);
Function3<Object, Context, File, Void> fnInjectItemToShareSheet = (shareSheet, ctx, file) -> {
try {
Object actionSheet = shareSheet;
if (actionSheet != null) {
if (kShareActionSheetProxy != null && kShareActionSheetProxy.isInstance(actionSheet)) {
actionSheet = fProxyImpl.get(actionSheet);
}
assert actionSheet != null;
if (kShareActionSheetImplV2.isInstance(actionSheet)) {
actionSheet = fImplV2Impl.get(actionSheet);
}
assert actionSheet != null;
// just make sure impl...
kShareActionSheetV2.cast(actionSheet);
if (!mAioPictureViewV2ListenerHooked) {
Object listener = fieldV2Listener.get(actionSheet);
assert listener != null;
Class<?> clazz = listener.getClass();
Method m = clazz.getDeclaredMethod(miv2OnItemClick.getName(), miv2OnItemClick.getParameterTypes());
XposedBridge.hookMethod(m, mItemClickHandler);
mAioPictureViewV2ListenerHooked = true;
}
List<Object>[] itemListArray = (List<Object>[]) fV2ItemListArray.get(actionSheet);
assert itemListArray != null;
List<Object> row2 = itemListArray[1];
assert row2 != null;
Object item = ctorActionSheetItem.newInstance();
Parasitics.injectModuleResources(ctx.getResources());
int drawableId = ResUtils.isInNightMode() ? R.drawable.ic_launch_28dp_night : R.drawable.ic_launch_28dp_light;
Reflex.setInstanceObject(item, "id", int.class, R.id.ShareActionSheet_sharePictureWithExtApp);
Reflex.setInstanceObject(item, "icon", int.class, drawableId);
Reflex.setInstanceObject(item, "label", String.class, "其他应用");
Reflex.setInstanceObject(item, "argus", String.class, file.getAbsolutePath());
row2.add(item);
}
return null;
} catch (ReflectiveOperationException e) {
IoUtils.unsafeThrow(e);
return null;
}
};
if (QAppUtils.isQQnt()) {
Class<?> kNTShareActionManager = Initiator.loadClass("com.tencent.qqnt.aio.gallery.share.NTShareActionManager");
Field itemsField = Reflex.findSingleField(kNTShareActionManager, ArrayList.class, false);
itemsField.setAccessible(true);
Method maybeShow = ArraysKt.single(kNTShareActionManager.getDeclaredMethods(), m -> {
if (m.getReturnType() != void.class) {
return false;
}
Class<?>[] argt = m.getParameterTypes();
if (argt.length != 1) {
return false;
}
Class<?> maybeNTShareContext = argt[0];
return !maybeNTShareContext.isInterface() && Modifier.isFinal(maybeNTShareContext.getModifiers());
});
Class<?> kNTShareContext = maybeShow.getParameterTypes()[0];
Class<?> kRFWLayerItemMediaInfo = Initiator.loadClass("com.tencent.richframework.gallery.bean.RFWLayerItemMediaInfo");
Field layerItemInfoField = Reflex.findSingleField(kNTShareContext, kRFWLayerItemMediaInfo, false);
layerItemInfoField.setAccessible(true);
Field activityField = Reflex.findSingleField(kNTShareContext, Activity.class, false);
activityField.setAccessible(true);
Field actionSheetField = Reflex.findSingleField(kNTShareActionManager, kiShareActionSheet, false);
actionSheetField.setAccessible(true);
Method getExistSaveOrEditPath = kRFWLayerItemMediaInfo.getDeclaredMethod("getExistSaveOrEditPath");
HookUtils.hookAfterIfEnabled(this, maybeShow, param -> {
List<Object> secondLine = ((ArrayList<Object>) itemsField.get(param.thisObject));
Object shareContext = param.args[0];
Object layerItemInfo = layerItemInfoField.get(shareContext);
Activity ctx = (Activity) activityField.get(shareContext);
String path = (String) getExistSaveOrEditPath.invoke(layerItemInfo);
if (TextUtils.isEmpty(path)) {
Toasts.error(ctx, "getExistSaveOrEditPath is empty");
return;
}
File file = new File(path);
if (!file.exists()) {
Toasts.error(ctx, "file not exists");
return;
}
Object actionSheet = actionSheetField.get(param.thisObject);
fnInjectItemToShareSheet.invoke(actionSheet, ctx, file);
});
}
HookUtils.hookAfterIfEnabled(this, showActionSheetForPic, param -> {
Object actionSheet = fieldActionSheet.get(param.thisObject);
Context ctx = (Context) contextOfAIOBrowserBaseView.get(param.thisObject);
Expand All @@ -154,38 +245,7 @@ protected boolean initOnce() throws Exception {
return;
}
assert ctx != null;
if (actionSheet != null) {
if (kShareActionSheetProxy != null && kShareActionSheetProxy.isInstance(actionSheet)) {
actionSheet = fProxyImpl.get(actionSheet);
}
assert actionSheet != null;
if (kShareActionSheetImplV2.isInstance(actionSheet)) {
actionSheet = fImplV2Impl.get(actionSheet);
}
assert actionSheet != null;
// just make sure impl...
kShareActionSheetV2.cast(actionSheet);
if (!mAioPictureViewV2ListenerHooked) {
Object listener = fieldV2Listener.get(actionSheet);
assert listener != null;
Class<?> clazz = listener.getClass();
Method m = clazz.getDeclaredMethod(miv2OnItemClick.getName(), miv2OnItemClick.getParameterTypes());
XposedBridge.hookMethod(m, mItemClickHandler);
mAioPictureViewV2ListenerHooked = true;
}
List<Object>[] itemListArray = (List<Object>[]) fV2ItemListArray.get(actionSheet);
assert itemListArray != null;
List<Object> row2 = itemListArray[1];
assert row2 != null;
Object item = ctorActionSheetItem.newInstance();
Parasitics.injectModuleResources(ctx.getResources());
int drawableId = ResUtils.isInNightMode() ? R.drawable.ic_launch_28dp_night : R.drawable.ic_launch_28dp_light;
Reflex.setInstanceObject(item, "id", int.class, R.id.ShareActionSheet_sharePictureWithExtApp);
Reflex.setInstanceObject(item, "icon", int.class, drawableId);
Reflex.setInstanceObject(item, "label", String.class, "其他应用");
Reflex.setInstanceObject(item, "argus", String.class, picFile.getAbsolutePath());
row2.add(item);
}
fnInjectItemToShareSheet.invoke(actionSheet, ctx, picFile);
});
return true;
}
Expand All @@ -194,14 +254,28 @@ protected boolean initOnce() throws Exception {
Object item = param.args[0];
int id = (int) Reflex.getInstanceObject(item, "id", int.class);
if (id == R.id.ShareActionSheet_sharePictureWithExtApp) {
Class<?> kAIOPictureView = Initiator.loadClass("com.tencent.mobileqq.richmediabrowser.view.AIOPictureView");
Class<?> kAIOBrowserBaseView = Initiator.load("com.tencent.mobileqq.richmediabrowser.view.AIOBrowserBaseView");
if (kAIOBrowserBaseView == null) {
kAIOBrowserBaseView = kAIOPictureView.getSuperclass();
assert kAIOBrowserBaseView != null;
Context ctx = null;
Class<?> kNTAIOLayerMorePart = Initiator.load("com.tencent.qqnt.aio.gallery.part.NTAIOLayerMorePart");
if (kNTAIOLayerMorePart != null && kNTAIOLayerMorePart.isInstance(param.thisObject)) {
// NT
Field activityField = Reflex.findSingleField(kNTAIOLayerMorePart, Activity.class, true);
activityField.setAccessible(true);
ctx = (Context) activityField.get(param.thisObject);
} else {
// older
Class<?> kAIOPictureView = Initiator.loadClass("com.tencent.mobileqq.richmediabrowser.view.AIOPictureView");
Class<?> kAIOBrowserBaseView = Initiator.load("com.tencent.mobileqq.richmediabrowser.view.AIOBrowserBaseView");
if (kAIOBrowserBaseView == null) {
kAIOBrowserBaseView = kAIOPictureView.getSuperclass();
assert kAIOBrowserBaseView != null;
}
Field contextOfAIOBrowserBaseView = Reflex.findFirstDeclaredInstanceFieldByType(kAIOBrowserBaseView, Context.class);
ctx = (Context) contextOfAIOBrowserBaseView.get(Reflex.getFirstByType(param.thisObject, kAIOPictureView));
}
if (ctx == null) {
Toasts.error(null, "unable to get activity");
return;
}
Field contextOfAIOBrowserBaseView = Reflex.findFirstDeclaredInstanceFieldByType(kAIOBrowserBaseView, Context.class);
Context ctx = (Context) contextOfAIOBrowserBaseView.get(Reflex.getFirstByType(param.thisObject, kAIOPictureView));
assert ctx != null;
String picPath = (String) Reflex.getInstanceObject(item, "argus", String.class);
if (TextUtils.isEmpty(picPath)) {
Expand Down
23 changes: 23 additions & 0 deletions app/src/main/java/cc/ioctl/util/Reflex.java
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,29 @@ public static Method findSingleMethod(@NonNull Class<?> clazz, @Nullable Class<?
return candidateMethod;
}

@NonNull
public static Field findSingleField(@NonNull Class<?> clazz, @NonNull Class<?> type, boolean withSuper) throws NoSuchFieldException {
Objects.requireNonNull(clazz, "clazz == null");
Objects.requireNonNull(type, "type == null");
Class<?> clz = clazz;
Field candidateField = null;
do {
for (Field field : clz.getDeclaredFields()) {
if (field.getType() == type) {
if (candidateField != null) {
throw new NoSuchFieldException("Multiple fields of type " + type.getName() + " found in " + clazz.getName());
} else {
candidateField = field;
}
}
}
} while ((candidateField == null && withSuper) && (clz = clz.getSuperclass()) != null);
if (candidateField == null) {
throw new NoSuchFieldException("No field of type " + type.getName() + " found in " + clazz.getName());
}
return candidateField;
}

/**
* Finds a method with the given return type and parameter types.
*
Expand Down

0 comments on commit f6f9045

Please sign in to comment.