Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,10 @@ Each installation method is completely different and comes with its own challeng
<details>
<summary>I'm having issues on GrapheneOS!</summary>

- Uninstall Google Play "trio" (framework, services, store) in "apps" app and reinstall them again. They break all the time so it's a good idea to reload them. Don't worry, you won't have to login again to Google.
- Make sure to turn **ON** the exploit protection compatibility mode in "App Info" of Grindr, GrindrPlus and Google Play "trio". Just tap and hold onto the app icon to go there. When it comes to Google services you can do so from "apps" app.
- While doing the step above make sure to give Google Play services permissions to access location all the time and sensors.
- In Settings -> Apps -> Sandboxed Google Play, turn off the option "Reroute location requests to OS".
- Turn **ON** the **"Exploit Protection Compatibility"** mode on Grindr. To do so - tap and hold on the app icon, click "App info" and scroll down a little. In general - Grindr should work without a problem with that option turned off but if it gives you any issues then you can try to play around with those settings.
- Do the same for Google services. In contrary to other apps - you have to access those options through "App store" app provided by GOS team.
- Make sure to give Google Play services permissions to **All time location** and to **Sensors**.
- In Settings -> Apps -> Sandboxed Google Play, turn **OFF** the option **"Reroute location requests to OS"**. This option sometimes breaks the location features on Grindr.

</details>

Expand Down
6 changes: 3 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ plugins {
}

android {
val grindrVersionName = listOf("25.8.0")
val grindrVersionCode = listOf(138406)
val grindrVersionName = listOf("25.10.2")
val grindrVersionCode = listOf(140001)

namespace = "com.grindrplus"
compileSdk = 35
Expand All @@ -22,7 +22,7 @@ android {
minSdk = 26
targetSdk = 34
versionCode = 14
versionName = "4.2.1-${grindrVersionName.joinToString("_")}_$gitCommitHash"
versionName = "4.3.0-${grindrVersionName.joinToString("_")}_$gitCommitHash"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
16 changes: 10 additions & 6 deletions app/src/main/java/com/grindrplus/GrindrPlus.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ object GrindrPlus {
val currentActivity: Activity?
get() = currentActivityRef?.get()

private val userAgent = "u6.h" // search for 'grindr3/'
internal val userSession = "qc.V" // search for 'com.grindrapp.android.storage.UserSessionImpl$1'
private val userAgent = "W6.h" // search for 'grindr3/'
internal val userSession = "pd.W" // search for 'com.grindrapp.android.storage.UserSessionImpl$1'
private val deviceInfo =
"h4.B" // search for 'AdvertisingIdClient.Info("00000000-0000-0000-0000-000000000000", true)'
internal val grindrLocationProvider = "H8.d" // search for 'system settings insufficient for location request, attempting to resolve'
"y4.B" // search for 'AdvertisingIdClient.Info("00000000-0000-0000-0000-000000000000", true)'
internal val grindrLocationProvider = "u9.d" // search for 'system settings insufficient for location request, attempting to resolve'
internal val serverDrivenCascadeRepo = "com.grindrapp.android.persistence.repository.ServerDrivenCascadeRepo"

private val ioScope = CoroutineScope(Dispatchers.IO)
Expand Down Expand Up @@ -160,8 +160,12 @@ object GrindrPlus {
if (parts.size != 2 || parts.any { it.toDoubleOrNull() == null }) {
Logger.w("Invalid forced coordinates format: $forcedCoordinates", LogSource.MODULE)
} else {
Logger.i("Using forced coordinates: $forcedCoordinates", LogSource.MODULE)
Config.put("forced_coordinates", forcedCoordinates)
if (parts[0] == "0.0" && parts[1] == "0.0") {
Logger.w("Ignoring forced coordinates: $forcedCoordinates", LogSource.MODULE)
} else {
Logger.i("Using forced coordinates: $forcedCoordinates", LogSource.MODULE)
Config.put("forced_coordinates", forcedCoordinates)
}
}
}

Expand Down
5 changes: 2 additions & 3 deletions app/src/main/java/com/grindrplus/core/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object Utils {
val profileTypeClass =
GrindrPlus.loadClass("com.grindrapp.android.ui.profileV2.model.ProfileType")
val referrerTypeClass =
GrindrPlus.loadClass("com.grindrapp.android.base.model.profile.ReferrerType")
GrindrPlus.loadClass("com.grindrapp.android.profile.domain.ReferrerType")
val conversationMetadataClass =
GrindrPlus.loadClass("com.grindrapp.android.chat.model.DirectConversationMetaData")

Expand All @@ -55,7 +55,6 @@ object Utils {
"0xDEADBEEF", // str3
null,
null, // chatMediaDrawerArgs
null,
844
)

Expand Down Expand Up @@ -84,7 +83,7 @@ object Utils {

fun openProfile(id: String) {
val referrerTypeClass =
GrindrPlus.loadClass("com.grindrapp.android.base.model.profile.ReferrerType")
GrindrPlus.loadClass("com.grindrapp.android.profile.domain.ReferrerType")
val referrerType = referrerTypeClass.getField("NOTIFICATION").get(null)
val profilesActivityInnerClass =
GrindrPlus.loadClass("com.grindrapp.android.ui.profileV2.ProfilesActivity\$a")
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/grindrplus/core/http/Interceptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ class Interceptor(
private fun modifyRequest(originalRequest: Request): Request {
try {
// search for 'return value != null && value.length() > 0' in userSession
val isLoggedIn = invokeMethodSafe(userSession, "n") as Boolean
val isLoggedIn = invokeMethodSafe(userSession, "o") as Boolean

// search for 'return FlowKt.asStateFlow' in userSession (return type is String)
val authTokenFlow = invokeMethodSafe(userSession, "r")
val authTokenFlow = invokeMethodSafe(userSession, "s")
val authToken = invokeMethodSafe(authTokenFlow, "getValue") as String

// search for one line method returning an string in userSession
val roles = invokeMethodSafe(userSession, "z") as String
val roles = invokeMethodSafe(userSession, "A") as String

// search for 'getValue().getNameTitleCase()' in userAgent
val userAgent = invokeMethodSafe(userAgent, "a") as String
Expand Down
10 changes: 5 additions & 5 deletions app/src/main/java/com/grindrplus/hooks/AntiBlock.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@ class AntiBlock : Hook(
"Notifies you when someone blocks or unblocks you"
) {
private var myProfileId: Long = 0
private val chatDeleteConversationPlugin = "x5.c" // search for 'com.grindrapp.android.chat.ChatDeleteConversationPlugin'
private val inboxFragmentV2DeleteConversations = "b8.i" // search for '("chat_read_receipt", conversationId, null);'
private val individualUnblockActivityViewModel = "Wd.s" // search for '@DebugMetadata(c = "com.grindrapp.android.ui.block.IndividualUnblockActivityViewModel$unblockAllProfile$1", f = "IndividualUnblockActivityViewModel.kt",'
private val chatDeleteConversationPlugin = "O5.c" // search for 'com.grindrapp.android.chat.ChatDeleteConversationPlugin'
private val inboxFragmentV2DeleteConversations = "N8.i" // search for '("chat_read_receipt", conversationId, null);'
private val individualUnblockActivityViewModel = "Ve.s" // search for '@DebugMetadata(c = "com.grindrapp.android.ui.block.IndividualUnblockActivityViewModel$unblockAllProfile$1", f = "IndividualUnblockActivityViewModel.kt",'

override fun init() {
findClass(individualUnblockActivityViewModel).hook("F", HookStage.BEFORE) { param ->
findClass(individualUnblockActivityViewModel).hook("J", HookStage.BEFORE) { param ->
GrindrPlus.shouldTriggerAntiblock = false
}

findClass(individualUnblockActivityViewModel).hook("F", HookStage.AFTER) { param ->
findClass(individualUnblockActivityViewModel).hook("J", HookStage.AFTER) { param ->
Thread.sleep(700) // Wait for WS to unblock
GrindrPlus.shouldTriggerAntiblock = true
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/grindrplus/hooks/AntiDetection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class AntiDetection : Hook(
"Anti Detection",
"Hides root, emulator, and environment detections"
) {
private val grindrMiscClass = "Vd.m" // search for '"sdk_gphone", "emulator", "simulator", "google_sdk"'
private val grindrMiscClass = "Ue.r" // search for '"sdk_gphone", "emulator", "simulator", "google_sdk"'
private val devicePropertiesCollector = "siftscience.android.DevicePropertiesCollector"
private val commonUtils = "com.google.firebase.crashlytics.internal.common.CommonUtils"
private val osData = "com.google.firebase.crashlytics.internal.model.AutoValue_StaticSessionData_OsData"
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/grindrplus/hooks/BanManagement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ class BanManagement : Hook(
"Ban management",
"Provides comprehensive ban management tools (detailed ban info, etc.)"
) {
private val authServiceClass = "u4.h" // search for 'v3/users/password-validation'
private val authServiceClass = "L4.h" // search for 'v3/users/password-validation'
private val materialButton = "com.google.android.material.button.MaterialButton"
private val bannedFragment = "com.grindrapp.android.ui.account.banned.BannedFragment"
private val deviceUtility = "qc.l" // search for 'Settings.Secure.getString(context.getContentResolver(), "android_id")'
private val bannedArgs = "y4.a" // search for 'new StringBuilder("BannedArgs(bannedType=")'
private val deviceUtility = "pd.l" // search for 'Settings.Secure.getString(context.getContentResolver(), "android_id")'
private val bannedArgs = "P4.a" // search for 'new StringBuilder("BannedArgs(bannedType=")'
private var bannedInfo: JSONObject = JSONObject()

@SuppressLint("DiscouragedApi")
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/grindrplus/hooks/ChatTerminal.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class ChatTerminal : Hook(
"Chat terminal",
"Create a chat terminal to execute commands"
) {
private val chatMessageHandler = "D5.l" // search for '(chatMessageMetaData, "chatMessageMetaData")'
private val chatMessageHandler = "T5.l" // search for '(chatMessageMetaData, "chatMessageMetaData")'

override fun init() {
findClass(chatMessageHandler).hook("m", HookStage.BEFORE) { param ->
Expand Down
9 changes: 2 additions & 7 deletions app/src/main/java/com/grindrplus/hooks/DisableAnalytics.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.grindrplus.hooks

import com.grindrplus.core.logi
import com.grindrplus.utils.Hook
import com.grindrplus.utils.HookStage
import com.grindrplus.utils.RetrofitUtils.RETROFIT_NAME
Expand All @@ -10,7 +11,7 @@ class DisableAnalytics : Hook(
"Disable analytics",
"Disable Grindr analytics (data collection)"
) {
private val analyticsRestService = "X5.a" // search for 'v1/telemetry'
private val analyticsRestService = "u6.a" // search for 'v1/telemetry'

override fun init() {
val analyticsRestServiceClass = findClass(analyticsRestService)
Expand All @@ -30,12 +31,6 @@ class DisableAnalytics : Hook(
param.setResult(true)
}

// AppLovin
findClass("com.applovin.impl.d4") // ConnectionManager
.hook("a", HookStage.BEFORE) {
param -> param.setResult(null)
}

// Braze
findClass("com.braze.Braze\$Companion")
// See https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/outbound-network-requests-offline.html
Expand Down
10 changes: 5 additions & 5 deletions app/src/main/java/com/grindrplus/hooks/DisableBoosting.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class DisableBoosting : Hook(
"Disable boosting",
"Get rid of all upsells related to boosting"
) {
private val drawerProfileUiState = "ue.e\$a" // search for 'DrawerProfileUiState(showBoostMeButton='
private val radarUiModel = "tb.a\$a" // search for 'RadarUiModel(boostButton='
private val drawerProfileUiState = "tf.e\$a" // search for 'DrawerProfileUiState(showBoostMeButton='
private val radarUiModel = "jc.a\$a" // search for 'RadarUiModel(boostButton='
private val fabUiModel = "com.grindrapp.android.boost2.presentation.model.FabUiModel"
private val rightNowMicrosFabUiModel =
"com.grindrapp.android.rightnow.presentation.model.RightNowMicrosFabUiModel"
Expand All @@ -34,8 +34,8 @@ class DisableBoosting : Hook(
newInstance(findClass(boostStateClass))
) // roamButtonState
setObjectField(param.thisObject(), "c", false) // showRNBoostCard
setObjectField(param.thisObject(), "j", null) // showDayPassItem
setObjectField(param.thisObject(), "k", null) // unlimitedWeeklySubscriptionItem
setObjectField(param.thisObject(), "i", null) // showDayPassItem
setObjectField(param.thisObject(), "j", null) // unlimitedWeeklySubscriptionItem
setObjectField(param.thisObject(), "s", false) // isRightNowAvailable
}

Expand All @@ -61,7 +61,7 @@ class DisableBoosting : Hook(
// 'com.grindrapp.android.ui.home.HomeActivity.showTapsAndViewedMePopup.<anonymous> (HomeActivity.kt'
// 'com.grindrapp.android.ui.home.HomeActivity$subscribeForBoostRedeem$1'
// 'com.grindrapp.android.ui.home.HomeActivity.showTapsAndViewedMePopup.<anonymous>.<anonymous> (HomeActivity.kt'
listOf("Ee.B0", "Ee.D0", "Ee.E0", "Ee.C0").forEach {
listOf("Df.w0", "Df.y0", "Df.z0", "Df.x0").forEach {
findClass(it).hook("invoke", HookStage.BEFORE) { param ->
param.setResult(null)
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/grindrplus/hooks/DisableUpdates.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DisableUpdates : Hook(
"https://raw.githubusercontent.com/R0rt1z2/GrindrPlus/master/version.json"
private val appUpdateInfo = "com.google.android.play.core.appupdate.AppUpdateInfo"
private val appUpdateZzm = "com.google.android.play.core.appupdate.zzm" // search for 'requestUpdateInfo(%s)'
private val appUpgradeManager = "M8.r" // search for 'Uri.parse("market://details?id=com.grindrapp.android");'
private val appUpgradeManager = "z9.n" // search for 'Uri.parse("market://details?id=com.grindrapp.android");'
private val appConfiguration = "com.grindrapp.android.platform.config.AppConfiguration"
private var versionCode: Int = 0
private var versionName: String = ""
Expand Down
27 changes: 2 additions & 25 deletions app/src/main/java/com/grindrplus/hooks/EmptyCalls.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,12 @@ class EmptyCalls : Hook(
"Video calls",
"Allow video calls on empty chats"
) {
private val individualChatNavViewModel = "P5.f0" // search for 'com.grindrapp.android.chat.presentation.viewmodel.IndividualChatNavViewModel'
private val createVideoCallResponse = "com.grindrapp.android.chat.api.model.CreateVideoCallResponse"
private val videoCallInfoResponse = "com.grindrapp.android.chat.api.model.VideoCallInfoResponse"
private val individualChatNavViewModel = "f6.d0" // search for 'com.grindrapp.android.chat.presentation.viewmodel.IndividualChatNavViewModel'

override fun init() {
findClass(individualChatNavViewModel) // isTalkBefore()
.hook("A", HookStage.BEFORE) { param ->
.hook("e", HookStage.BEFORE) { param ->
param.setResult(true)
}

findClass(createVideoCallResponse)
.hook("getRemainingSeconds", HookStage.BEFORE) { param ->
param.setResult(Long.MAX_VALUE)
}

findClass(createVideoCallResponse)
.hookConstructor(HookStage.BEFORE) { param ->
param.setArg(1, Long.MAX_VALUE) // maxSeconds
param.setArg(3, Long.MAX_VALUE) // remainingSeconds
}

findClass(createVideoCallResponse)
.hook("fetchRemainingSeconds", HookStage.BEFORE) { param ->
param.setResult(Long.MAX_VALUE)
}

findClass(videoCallInfoResponse)
.hook("getRemainingSeconds", HookStage.BEFORE) { param ->
param.setResult(Int.MAX_VALUE)
}
}
}
14 changes: 7 additions & 7 deletions app/src/main/java/com/grindrplus/hooks/EnableUnlimited.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ class EnableUnlimited : Hook(
"Enable unlimited",
"Enable Grindr Unlimited features"
) {
private val paywallUtils = "Qb.d" // search for 'app_restart_required'
private val persistentAdBannerContainer = "m6.o3" // search for 'GrindrAdContainer grindrAdContainer = (GrindrAdContainer) ViewBindings.findChildViewById(view, R.id.persistent_banner_ad_container);'
private val userSession = "qc.V" // search for 'com.grindrapp.android.storage.UserSessionImpl$1'
private val paywallUtils = "Gd.d" // search for 'app_restart_required'
private val persistentAdBannerContainer = "O6.L3" // search for 'GrindrAdContainer grindrAdContainer = (GrindrAdContainer) ViewBindings.findChildViewById(view, R.id.persistent_banner_ad_container);'
private val userSession = "pd.W" // search for 'com.grindrapp.android.storage.UserSessionImpl$1'
private val subscribeToInterstitialsList = listOf(
"J5.E\$a" // search for 'com.grindrapp.android.chat.presentation.ui.ChatActivityV2$subscribeToInterstitialAds$1$1$1'
"Z5.G\$a" // search for 'com.grindrapp.android.chat.presentation.ui.ChatActivityV2$subscribeToInterstitialAds$1$1$1'
)
private val viewsToHide = mapOf(
"com.grindrapp.android.ui.tagsearch.ProfileTagCascadeFragment\$c" to listOf("upsell_bottom_bar"), // search for 'bind(Landroid/view/View;)Lcom/grindrapp/android/databinding/ProfileTagCascadeFragmentBinding;'
Expand All @@ -45,13 +45,13 @@ class EnableUnlimited : Hook(
}

userSessionClass.hook( // isFree()
"x", HookStage.BEFORE // search for '.isEmpty();' in userSession
"y", HookStage.BEFORE // search for '.isEmpty();' in userSession
) { param ->
param.setResult(false)
}

userSessionClass.hook( // isFreeXtra()
"t", HookStage.BEFORE // search for 'Role.XTRA, Role.FREE_XTRA' in userSession
"u", HookStage.BEFORE // search for 'Role.XTRA, Role.FREE_XTRA' in userSession
) { param ->
param.setResult(false)
}
Expand Down Expand Up @@ -118,7 +118,7 @@ class EnableUnlimited : Hook(
}

// search for 'variantName, "treatment_exact_count") ?'
findClass("W1.a").hook("b", HookStage.BEFORE) { param ->
findClass("Q1.a").hook("b", HookStage.BEFORE) { param ->
param.setResult(false)
}
}
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/grindrplus/hooks/FeatureGranting.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ class FeatureGranting : Hook(
"Feature granting",
"Grant all Grindr features"
) {
private val featureFlagManager = "J7.d" // search for 'experiments, @NotNull String featureFlagName,'
private val isFeatureFlagEnabled = "ya.b" // search for 'implements IsFeatureFlagEnabled {'
private val featureFlagManager = "x8.a" // search for 'experiments, @NotNull String featureFlagName,'
private val isFeatureFlagEnabled = "nb.d" // search for 'implements IsFeatureFlagEnabled {'
private val upsellsV8Model = "com.grindrapp.android.model.UpsellsV8"
private val insertsModel = "com.grindrapp.android.model.Inserts"
private val favoritesExperiment = "com.grindrapp.android.favoritesv2.domain.experiment.FavoritesV2Experiment" // search for 'public final class FavoritesV2Experiment'
private val albumSpankBankExperiment = "b4.b" // search for 'spankBankExperiment'
private val albumSpankBankExperiment = "s4.b" // search for 'spankBankExperiment'
private val settingDistanceVisibilityViewModel =
"com.grindrapp.android.ui.settings.distance.a\$e" // search for 'UiState(distanceVisibility='
private val featureModel = "com.grindrapp.android.usersession.model.Feature"
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/grindrplus/hooks/LocalSavedPhrases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ class LocalSavedPhrases : Hook(
"Local saved phrases",
"Save unlimited phrases locally"
) {
private val phrasesRestService = "u4.k" // search for 'v3/me/prefs'
private val createSuccessResult = "A9.a\$b" // search for 'Success(successValue='
private val phrasesRestService = "L4.k" // search for 'v3/me/prefs'
private val createSuccessResult = "na.a\$b" // search for 'Success(successValue='
private val chatRestService = "com.grindrapp.android.chat.data.datasource.api.service.ChatRestService"
private val addSavedPhraseResponse =
"com.grindrapp.android.chat.api.model.AddSavedPhraseResponse"
Expand Down
Loading
Loading