@@ -5,6 +5,7 @@ import android.content.Intent
55import android.os.Build
66import android.os.ext.SdkExtensions
77import android.provider.MediaStore
8+ import android.util.Log
89import de.robv.android.xposed.IXposedHookLoadPackage
910import de.robv.android.xposed.XC_MethodHook
1011import de.robv.android.xposed.XposedBridge
@@ -15,6 +16,7 @@ class MainHook : IXposedHookLoadPackage {
1516
1617 companion object {
1718 private const val TAG = " NoPhotoPicker"
19+ private const val FLAG = " x_handled_by_nophoto"
1820 }
1921
2022 fun XC_LoadPackage.LoadPackageParam.isSystemFramework (): Boolean {
@@ -27,14 +29,14 @@ class MainHook : IXposedHookLoadPackage {
2729 hookSystemServices(lpparam)
2830 }
2931 lpparam.packageName != null -> {
32+ hookInstrumentation(lpparam)
3033 hookActivity(lpparam)
3134 hookActivityResult(lpparam)
3235 }
3336 }
3437 }
3538
3639 private fun hookSystemServices (lpparam : XC_LoadPackage .LoadPackageParam ) {
37- // Try to find which service class exists
3840 val classLoader = lpparam.classLoader
3941 val serviceClasses = listOf (
4042 " com.android.server.wm.ActivityTaskManagerService" ,
@@ -50,11 +52,39 @@ class MainHook : IXposedHookLoadPackage {
5052 " startActivity" ,
5153 createIntentInterceptor(" System:$className " )
5254 )
53- XposedBridge .log(" $TAG : Hooked $className " )
55+
56+ Log .d(TAG , " Hooked $className " )
5457 return
5558 }
5659 }
5760 }
61+ private fun hookInstrumentation (lpparam : XC_LoadPackage .LoadPackageParam ) {
62+ try {
63+ XposedHelpers .findAndHookMethod(
64+ android.app.Instrumentation ::class .java,
65+ " execStartActivity" ,
66+ android.content.Context ::class .java,
67+ android.os.IBinder ::class .java,
68+ android.os.IBinder ::class .java,
69+ Activity ::class .java,
70+ Intent ::class .java,
71+ Int ::class .javaPrimitiveType,
72+ android.os.Bundle ::class .java,
73+ object : XC_MethodHook () {
74+ override fun beforeHookedMethod (param : MethodHookParam ) {
75+ val intent = param.args[4 ] as ? Intent ? : return
76+ if (isPhotoPickerIntent(intent)) {
77+ logIntentDetails(intent, " Instrumentation.execStartActivity" )
78+ param.args[4 ] = buildDocumentPickerIntent(intent)
79+ }
80+ }
81+ }
82+ )
83+ Log .d(TAG , " Hooked Instrumentation for ${lpparam.packageName} " )
84+ } catch (t: Throwable ) {
85+ XposedBridge .log(" $TAG : Failed to hook Instrumentation: ${t.message} " )
86+ }
87+ }
5888
5989 private fun createIntentInterceptor (source : String ): XC_MethodHook {
6090 return object : XC_MethodHook () {
@@ -63,9 +93,18 @@ class MainHook : IXposedHookLoadPackage {
6393 for (i in args.indices) {
6494 if (args[i] is Intent ) {
6595 val intent = args[i] as Intent
96+
6697 if (isPhotoPickerIntent(intent)) {
6798 logIntentDetails(intent, " $source .startActivity" )
68- args[i] = buildDocumentPickerIntent(intent)
99+
100+ val newIntent = buildDocumentPickerIntent(intent)
101+
102+ args[i] = newIntent
103+
104+ if (i + 1 < args.size && (args[i + 1 ] == null || args[i + 1 ] is String )) {
105+ val newType = newIntent.type ? : " */*"
106+ args[i + 1 ] = newType
107+ Log .d(TAG , " Updated resolvedType to $newType " ) }
69108 return
70109 }
71110 }
@@ -83,6 +122,9 @@ class MainHook : IXposedHookLoadPackage {
83122 object : XC_MethodHook () {
84123 override fun beforeHookedMethod (param : MethodHookParam ) {
85124 val intent = param.args[0 ] as ? Intent ? : return
125+
126+ if (intent.hasExtra(FLAG )) return
127+
86128 if (isPhotoPickerIntent(intent)) {
87129 logIntentDetails(intent, " App.Activity.startActivity" )
88130 param.args[0 ] = buildDocumentPickerIntent(intent)
@@ -107,7 +149,7 @@ class MainHook : IXposedHookLoadPackage {
107149 }
108150 )
109151
110- XposedBridge .log( " $ TAG: Successfully hooked Activity methods for ${lpparam.packageName} " )
152+ Log .d( TAG , " Successfully hooked Activity methods for ${lpparam.packageName} " )
111153 } catch (t: Throwable ) {
112154 XposedBridge .log(" $TAG : Failed to hook Activity: ${t.message} " )
113155 }
@@ -141,7 +183,7 @@ class MainHook : IXposedHookLoadPackage {
141183 }
142184
143185 if (! hasContent) {
144- XposedBridge .log( " $ TAG: Empty result detected for request $requestCode " )
186+ Log .d( TAG , " Empty result detected for request $requestCode " )
145187 param.args[1 ] = Activity .RESULT_CANCELED
146188 param.args[2 ] = null
147189 }
@@ -165,6 +207,7 @@ class MainHook : IXposedHookLoadPackage {
165207 }
166208
167209 private fun isPhotoPickerIntent (intent : Intent ): Boolean {
210+ if (intent.hasExtra(FLAG )) return false
168211 return when {
169212 Build .VERSION .SDK_INT >= Build .VERSION_CODES .TIRAMISU ->
170213 intent.action == MediaStore .ACTION_PICK_IMAGES
@@ -177,8 +220,8 @@ class MainHook : IXposedHookLoadPackage {
177220 }
178221
179222 private fun logIntentDetails (intent : Intent , source : String ) {
180- XposedBridge .log( " $ TAG: [$source ] Photo picker detected" )
181- XposedBridge .log( " Action: ${intent.action} " )
223+ Log .d( TAG , " [$source ] Photo picker detected" )
224+ Log .d( TAG , " Action: ${intent.action} " )
182225 }
183226
184227 private fun buildDocumentPickerIntent (original : Intent ): Intent {
@@ -208,11 +251,12 @@ class MainHook : IXposedHookLoadPackage {
208251
209252 if (shouldAllowMultiple) {
210253 putExtra(Intent .EXTRA_ALLOW_MULTIPLE , true )
211- XposedBridge .log( " $ TAG: Multi-select enabled" )
254+ Log .d( TAG , " Multi-select enabled" )
212255 }
213256
257+ putExtra(FLAG , true )
214258 addFlags(Intent .FLAG_GRANT_READ_URI_PERMISSION )
215- XposedBridge .log( " $ TAG: Created document picker intent" )
259+ Log .d( TAG , " Created document picker intent" )
216260 }
217261 }
218262}
0 commit comments