mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2025-05-10 12:45:41 +02:00
fix(Spotify - Unlock Spotify Premium): Remove pop up premium ads (#4842)
This commit is contained in:
parent
52af71f68a
commit
00aa2000ba
6 changed files with 137 additions and 43 deletions
|
@ -130,6 +130,7 @@ public final class UnlockPremiumPatch {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injection point. Remove ads sections from home.
|
* Injection point. Remove ads sections from home.
|
||||||
|
* Depends on patching protobuffer list remove method.
|
||||||
*/
|
*/
|
||||||
public static void removeHomeSections(List<Section> sections) {
|
public static void removeHomeSections(List<Section> sections) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -2,8 +2,12 @@ package app.revanced.patches.spotify.misc
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||||
|
import app.revanced.util.getReference
|
||||||
|
import app.revanced.util.indexOfFirstInstruction
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||||
|
|
||||||
internal val accountAttributeFingerprint = fingerprint {
|
internal val accountAttributeFingerprint = fingerprint {
|
||||||
custom { _, classDef ->
|
custom { _, classDef ->
|
||||||
|
@ -15,7 +19,7 @@ internal val accountAttributeFingerprint = fingerprint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val productStateProtoFingerprint = fingerprint {
|
internal val productStateProtoGetMapFingerprint = fingerprint {
|
||||||
returns("Ljava/util/Map;")
|
returns("Ljava/util/Map;")
|
||||||
custom { _, classDef ->
|
custom { _, classDef ->
|
||||||
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||||
|
@ -56,16 +60,41 @@ internal val readPlayerOptionOverridesFingerprint = fingerprint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val homeSectionFingerprint = fingerprint {
|
|
||||||
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val protobufListsFingerprint = fingerprint {
|
internal val protobufListsFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
|
||||||
custom { method, _ -> method.name == "emptyProtobufList" }
|
custom { method, _ -> method.name == "emptyProtobufList" }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val homeStructureFingerprint = fingerprint {
|
internal val protobufListRemoveFingerprint = fingerprint {
|
||||||
opcodes(Opcode.IGET_OBJECT, Opcode.RETURN_OBJECT)
|
custom { method, _ -> method.name == "remove" }
|
||||||
custom { _, classDef -> classDef.endsWith("homeapi/proto/HomeStructure;") }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val homeSectionFingerprint = fingerprint {
|
||||||
|
custom { _, classDef -> classDef.endsWith("homeapi/proto/Section;") }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val homeStructureGetSectionsFingerprint = fingerprint {
|
||||||
|
custom { method, classDef ->
|
||||||
|
classDef.endsWith("homeapi/proto/HomeStructure;") && method.indexOfFirstInstruction {
|
||||||
|
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "sections_"
|
||||||
|
} >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun reactivexFunctionApplyWithClassInitFingerprint(className: String) = fingerprint {
|
||||||
|
accessFlags(AccessFlags.PUBLIC)
|
||||||
|
returns("Ljava/lang/Object;")
|
||||||
|
parameters("Ljava/lang/Object;")
|
||||||
|
custom { method, _ -> method.name == "apply" && method.indexOfFirstInstruction {
|
||||||
|
opcode == Opcode.NEW_INSTANCE && getReference<TypeReference>()?.type?.endsWith(className) == true
|
||||||
|
} >= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal const val PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME = "FetchMessageRequest;"
|
||||||
|
internal val pendragonJsonFetchMessageRequestFingerprint =
|
||||||
|
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME)
|
||||||
|
|
||||||
|
internal const val PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME = "FetchMessageListRequest;"
|
||||||
|
internal val pendragonProtoFetchMessageListRequestFingerprint =
|
||||||
|
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME)
|
||||||
|
|
|
@ -4,21 +4,25 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.patch.PatchException
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import app.revanced.util.toPublicAccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
|
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
|
||||||
|
@ -41,14 +45,18 @@ val unlockPremiumPatch = bytecodePatch(
|
||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Make _value accessible so that it can be overridden in the extension.
|
fun MutableClass.publicizeField(fieldName: String) {
|
||||||
accountAttributeFingerprint.classDef.fields.first { it.name == "value_" }.apply {
|
fields.first { it.name == fieldName }.apply {
|
||||||
// Add public flag and remove private.
|
// Add public and remove private flag.
|
||||||
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
|
accessFlags = accessFlags.toPublicAccessFlags()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make _value accessible so that it can be overridden in the extension.
|
||||||
|
accountAttributeFingerprint.classDef.publicizeField("value_")
|
||||||
|
|
||||||
// Override the attributes map in the getter method.
|
// Override the attributes map in the getter method.
|
||||||
productStateProtoFingerprint.method.apply {
|
productStateProtoGetMapFingerprint.method.apply {
|
||||||
val getAttributesMapIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
val getAttributesMapIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
||||||
val attributesMapRegister = getInstruction<TwoRegisterInstruction>(getAttributesMapIndex).registerA
|
val attributesMapRegister = getInstruction<TwoRegisterInstruction>(getAttributesMapIndex).registerA
|
||||||
|
|
||||||
|
@ -61,12 +69,12 @@ val unlockPremiumPatch = bytecodePatch(
|
||||||
|
|
||||||
|
|
||||||
// Add the query parameter trackRows to show popular tracks in the artist page.
|
// Add the query parameter trackRows to show popular tracks in the artist page.
|
||||||
buildQueryParametersFingerprint.apply {
|
buildQueryParametersFingerprint.method.apply {
|
||||||
val addQueryParameterConditionIndex = method.indexOfFirstInstructionReversedOrThrow(
|
val addQueryParameterConditionIndex = indexOfFirstInstructionReversedOrThrow(
|
||||||
stringMatches!!.first().index, Opcode.IF_EQZ
|
buildQueryParametersFingerprint.stringMatches!!.first().index, Opcode.IF_EQZ
|
||||||
)
|
)
|
||||||
|
|
||||||
method.replaceInstruction(addQueryParameterConditionIndex, "nop")
|
replaceInstruction(addQueryParameterConditionIndex, "nop")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -105,48 +113,39 @@ val unlockPremiumPatch = bytecodePatch(
|
||||||
val shufflingContextCallIndex = indexOfFirstInstructionOrThrow {
|
val shufflingContextCallIndex = indexOfFirstInstructionOrThrow {
|
||||||
getReference<MethodReference>()?.name == "shufflingContext"
|
getReference<MethodReference>()?.name == "shufflingContext"
|
||||||
}
|
}
|
||||||
|
val boolRegister = getInstruction<FiveRegisterInstruction>(shufflingContextCallIndex).registerD
|
||||||
|
|
||||||
val registerBool = getInstruction<FiveRegisterInstruction>(shufflingContextCallIndex).registerD
|
|
||||||
addInstruction(
|
addInstruction(
|
||||||
shufflingContextCallIndex,
|
shufflingContextCallIndex,
|
||||||
"sget-object v$registerBool, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;"
|
"sget-object v$boolRegister, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Disable the "Spotify Premium" upsell experiment in context menus.
|
// Disable the "Spotify Premium" upsell experiment in context menus.
|
||||||
contextMenuExperimentsFingerprint.apply {
|
contextMenuExperimentsFingerprint.method.apply {
|
||||||
val moveIsEnabledIndex = method.indexOfFirstInstructionOrThrow(
|
val moveIsEnabledIndex = indexOfFirstInstructionOrThrow(
|
||||||
stringMatches!!.first().index, Opcode.MOVE_RESULT
|
contextMenuExperimentsFingerprint.stringMatches!!.first().index, Opcode.MOVE_RESULT
|
||||||
)
|
)
|
||||||
val isUpsellEnabledRegister = method.getInstruction<OneRegisterInstruction>(moveIsEnabledIndex).registerA
|
val isUpsellEnabledRegister = getInstruction<OneRegisterInstruction>(moveIsEnabledIndex).registerA
|
||||||
|
|
||||||
method.replaceInstruction(moveIsEnabledIndex, "const/4 v$isUpsellEnabledRegister, 0")
|
replaceInstruction(moveIsEnabledIndex, "const/4 v$isUpsellEnabledRegister, 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Make featureTypeCase_ accessible so we can check the home section type in the extension.
|
val protobufListClassDef = with(protobufListsFingerprint.originalMethod) {
|
||||||
homeSectionFingerprint.classDef.fields.first { it.name == "featureTypeCase_" }.apply {
|
|
||||||
// Add public flag and remove private.
|
|
||||||
accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv())
|
|
||||||
}
|
|
||||||
|
|
||||||
val protobufListClassName = with(protobufListsFingerprint.originalMethod) {
|
|
||||||
val emptyProtobufListGetIndex = indexOfFirstInstructionOrThrow(Opcode.SGET_OBJECT)
|
val emptyProtobufListGetIndex = indexOfFirstInstructionOrThrow(Opcode.SGET_OBJECT)
|
||||||
getInstruction(emptyProtobufListGetIndex).getReference<FieldReference>()!!.definingClass
|
// Find the protobuffer list class using the definingClass which contains the empty list static value.
|
||||||
}
|
val classType = getInstruction(emptyProtobufListGetIndex).getReference<FieldReference>()!!.definingClass
|
||||||
|
|
||||||
val protobufListRemoveFingerprint = fingerprint {
|
classes.find { it.type == classType } ?: throw PatchException("Could not find protobuffer list class.")
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "remove" && classDef.type == protobufListClassName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to allow mutation of the list so the home ads sections can be removed.
|
// Need to allow mutation of the list so the home ads sections can be removed.
|
||||||
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability.
|
// Protobuffer list has an 'isMutable' boolean parameter that sets the mutability.
|
||||||
// Forcing that always on breaks unrelated code in strange ways.
|
// Forcing that always on breaks unrelated code in strange ways.
|
||||||
// Instead, remove the method call that checks if the list is unmodifiable.
|
// Instead, remove the method call that checks if the list is unmodifiable.
|
||||||
protobufListRemoveFingerprint.method.apply {
|
protobufListRemoveFingerprint.match(protobufListClassDef).method.apply {
|
||||||
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow {
|
val invokeThrowUnmodifiableIndex = indexOfFirstInstructionOrThrow {
|
||||||
val reference = getReference<MethodReference>()
|
val reference = getReference<MethodReference>()
|
||||||
opcode == Opcode.INVOKE_VIRTUAL &&
|
opcode == Opcode.INVOKE_VIRTUAL &&
|
||||||
|
@ -157,8 +156,12 @@ val unlockPremiumPatch = bytecodePatch(
|
||||||
removeInstruction(invokeThrowUnmodifiableIndex)
|
removeInstruction(invokeThrowUnmodifiableIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Make featureTypeCase_ accessible so we can check the home section type in the extension.
|
||||||
|
homeSectionFingerprint.classDef.publicizeField("featureTypeCase_")
|
||||||
|
|
||||||
// Remove ads sections from home.
|
// Remove ads sections from home.
|
||||||
homeStructureFingerprint.method.apply {
|
homeStructureGetSectionsFingerprint.method.apply {
|
||||||
val getSectionsIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
val getSectionsIndex = indexOfFirstInstructionOrThrow(Opcode.IGET_OBJECT)
|
||||||
val sectionsRegister = getInstruction<TwoRegisterInstruction>(getSectionsIndex).registerA
|
val sectionsRegister = getInstruction<TwoRegisterInstruction>(getSectionsIndex).registerA
|
||||||
|
|
||||||
|
@ -168,5 +171,56 @@ val unlockPremiumPatch = bytecodePatch(
|
||||||
"$EXTENSION_CLASS_DESCRIPTOR->removeHomeSections(Ljava/util/List;)V"
|
"$EXTENSION_CLASS_DESCRIPTOR->removeHomeSections(Ljava/util/List;)V"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Replace a fetch request that returns and maps Singles with their static onErrorReturn value.
|
||||||
|
fun MutableMethod.replaceFetchRequestSingleWithError(requestClassName: String) {
|
||||||
|
// The index of where the request class is being instantiated.
|
||||||
|
val requestInstantiationIndex = indexOfFirstInstructionOrThrow {
|
||||||
|
getReference<TypeReference>()?.type?.endsWith(requestClassName) == true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The index of where the onErrorReturn method is called with the error static value.
|
||||||
|
val onErrorReturnCallIndex = indexOfFirstInstructionOrThrow(requestInstantiationIndex) {
|
||||||
|
getReference<MethodReference>()?.name == "onErrorReturn"
|
||||||
|
}
|
||||||
|
val onErrorReturnCallInstruction = getInstruction<FiveRegisterInstruction>(onErrorReturnCallIndex)
|
||||||
|
|
||||||
|
// The error static value register.
|
||||||
|
val onErrorReturnValueRegister = onErrorReturnCallInstruction.registerD
|
||||||
|
|
||||||
|
// The index where the error static value starts being constructed.
|
||||||
|
// Because the Singles are mapped, the error static value starts being constructed right after the first
|
||||||
|
// move-result-object of the map call, before the onErrorReturn method call.
|
||||||
|
val onErrorReturnValueConstructionIndex =
|
||||||
|
indexOfFirstInstructionReversedOrThrow(onErrorReturnCallIndex, Opcode.MOVE_RESULT_OBJECT) + 1
|
||||||
|
|
||||||
|
val singleClassName = onErrorReturnCallInstruction.getReference<MethodReference>()!!.definingClass
|
||||||
|
// The index where the request is firstly called, before its result is mapped to other values.
|
||||||
|
val requestCallIndex = indexOfFirstInstructionOrThrow(requestInstantiationIndex) {
|
||||||
|
getReference<MethodReference>()?.returnType == singleClassName
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct a new single with the error static value and return it.
|
||||||
|
addInstructions(
|
||||||
|
onErrorReturnCallIndex,
|
||||||
|
"invoke-static { v$onErrorReturnValueRegister }, " +
|
||||||
|
"$singleClassName->just(Ljava/lang/Object;)$singleClassName\n" +
|
||||||
|
"move-result-object v$onErrorReturnValueRegister\n" +
|
||||||
|
"return-object v$onErrorReturnValueRegister"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Remove every instruction from the request call to right before the error static value construction.
|
||||||
|
val removeCount = onErrorReturnValueConstructionIndex - requestCallIndex
|
||||||
|
removeInstructions(requestCallIndex, removeCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove pendragon (pop up ads) requests and return the errors instead.
|
||||||
|
pendragonJsonFetchMessageRequestFingerprint.method.replaceFetchRequestSingleWithError(
|
||||||
|
PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME
|
||||||
|
)
|
||||||
|
pendragonProtoFetchMessageListRequestFingerprint.method.replaceFetchRequestSingleWithError(
|
||||||
|
PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,4 +60,4 @@ val spoofPackageInfoPatch = bytecodePatch(
|
||||||
// endregion
|
// endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,4 +14,4 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
|
||||||
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|
||||||
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
|
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||||
import app.revanced.util.InstructionUtils.Companion.branchOpcodes
|
import app.revanced.util.InstructionUtils.Companion.branchOpcodes
|
||||||
import app.revanced.util.InstructionUtils.Companion.returnOpcodes
|
import app.revanced.util.InstructionUtils.Companion.returnOpcodes
|
||||||
import app.revanced.util.InstructionUtils.Companion.writeOpcodes
|
import app.revanced.util.InstructionUtils.Companion.writeOpcodes
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.Opcode.*
|
import com.android.tools.smali.dexlib2.Opcode.*
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
|
@ -169,6 +170,15 @@ internal val Instruction.isBranchInstruction: Boolean
|
||||||
internal val Instruction.isReturnInstruction: Boolean
|
internal val Instruction.isReturnInstruction: Boolean
|
||||||
get() = this.opcode in returnOpcodes
|
get() = this.opcode in returnOpcodes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds public [AccessFlags] and removes private and protected flags (if present).
|
||||||
|
*/
|
||||||
|
internal fun Int.toPublicAccessFlags() : Int {
|
||||||
|
return this.or(AccessFlags.PUBLIC.value)
|
||||||
|
.and(AccessFlags.PROTECTED.value.inv())
|
||||||
|
.and(AccessFlags.PRIVATE.value.inv())
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
|
* Find the [MutableMethod] from a given [Method] in a [MutableClass].
|
||||||
*
|
*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue