[Xposed Framework Modules] RobbieTechie:
In this mod, the main files are Hooker.kt Hooks.kt and Obfuscation.kt. The Hooker.kt file is where the check is executed to see if you are running the mod for the version of grindr that is installed. Typically when a new version of grindr comes out I copy the already updated code to a new folder and name it for the version I'm working on, then the first thing I change is the value we are working on at the time of updating

companion object {
const val TARGET_PKG_VERSION_NAME = "9.15.0"

var pkgParam: LoadPackageParam by InitOnce()
var appContext: Context by InitOnce()
var pkgVersionName: String by InitOnce()
val sharedPref by lazy { appContext.getSharedPreferences("phrases", Context.MODE_PRIVATE) }
}

When 9.16.0 comes out I would change

const val TARGET_PKG_VERSION_NAME = "9.15.0"

to

const val TARGET_PKG_VERSION_NAME = "9.16.0"

then the app will load for 9.16.0 otherwise the rest of the mod would not load

You can run multiple copies of jadx so I typically load say 9.15.0 and 9.16.0 to see if you need to update a reference to code

The first thing that controls a large amount of functionality is the Storage section, which holds the features.

I typically disable each function call individually in Hooker.kt by commenting each one out except for the really core basic stuff in the beginning such as allowi mock location, feature granting, hooing the usersession

You can use:

?*

This is still considered a comment

so is this

*/

if you need to comment out an individual line

// you can use double forward slashes

// and you can do that for multiple lines if you wish

//I do this typically to disable a function

/*

but you can do it

this way if you prefer

depending on which way you like more

I do the the double forward slash when testing

because for me it's slightly faster

you can also end a block comment on the end of a line with text */

//sometimes I'm just lazy like this as I know it will be removed

//even though the other way looks slightly more professional

so I may do something like this, this is only a partial copy of Hooker.kt

// try {
// Hooks.allowScreenshotsHook()
// } catch (e: Exception) {
// e.message?.let { Logger.xLog(it) }
// }

try {
Hooks.allowMockProvider()
} catch (e: Exception) {
e.message?.let { Logger.xLog(it) }
}

// try {
// Hooks.hookUserSessionImpl()
// } catch (e: Exception) {
// e.message?.let { Logger.xLog(it) }
// }

then when I want to text the function for UserSessionImpl()

// try {
// Hooks.allowScreenshotsHook()
// } catch (e: Exception) {
// e.message?.let { Logger.xLog(it) }
// }

try {
Hooks.allowMockProvider()
} catch (e: Exception) {
e.message?.let { Logger.xLog(it) }
}

try {
Hooks.hookUserSessionImpl()
} catch (e: Exception) {
e.message?.let { Logger.xLog(it) }
}

I also use Logcat to view errors as I update the code, and you can search inside the logcat, so you can type in grindr and it will narrow down the logs for that and you can see if you have errors

Also, when you see something like Hooks.hookUserSessionImpl(), you go into the Hooks.kt file and look for functiontypically names fun hookUserSessionImpl()

fun hookUserSessionImpl() {

val class_Feature = findClass(

GApp.model.Feature,

Hooker.pkgParam.classLoader

)

listOf(

findClass(

GApp.storage.UserSession,

Hooker.pkgParam.classLoader

),

/*findClass(

GApp.storage.UserSession2,

Hooker.pkgParam.classLoader

)*/

).forEach { userSessionImpl ->

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.hasFeature_feature,

class_Feature,

RETURN_TRUE

)

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.isFree,

RETURN_FALSE

)

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.isNoXtraUpsell,

RETURN_TRUE

) //Not sure what is this for

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.isXtra,

RETURN_TRUE

)

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.isPlus,

RETURN_TRUE

)

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.isNoPlusUpsell,

RETURN_TRUE

)

findAndHookMethod(

userSessionImpl,

GApp.storage.IUserSession_.isUnlimited,

RETURN_TRUE

)

}

}