feat: media quality level override
This commit is contained in:
@@ -12,7 +12,8 @@
|
|||||||
|
|
||||||
"action": {
|
"action": {
|
||||||
"clean_cache": "Clean Cache",
|
"clean_cache": "Clean Cache",
|
||||||
"clear_message_logger": "Clear Message Logger"
|
"clear_message_logger": "Clear Message Logger",
|
||||||
|
"refresh_mappings": "Refresh Mappings"
|
||||||
},
|
},
|
||||||
|
|
||||||
"property": {
|
"property": {
|
||||||
@@ -41,11 +42,14 @@
|
|||||||
"auto_save": "Auto Save",
|
"auto_save": "Auto Save",
|
||||||
"anti_auto_save": "Anti Auto Save Button",
|
"anti_auto_save": "Anti Auto Save Button",
|
||||||
"snapchat_plus": "Snapchat Plus",
|
"snapchat_plus": "Snapchat Plus",
|
||||||
|
"disable_snap_splitting": "Disable Snap Splitting",
|
||||||
|
"disable_video_length_restriction": "Disable Video Length Restriction",
|
||||||
|
"override_media_quality": "Override Media Quality",
|
||||||
|
"media_quality_level": "Media Quality Level",
|
||||||
"remove_voice_record_button": "Remove Voice Record Button",
|
"remove_voice_record_button": "Remove Voice Record Button",
|
||||||
"remove_stickers_button": "Remove Stickers Button",
|
"remove_stickers_button": "Remove Stickers Button",
|
||||||
"remove_cognac_button": "Remove Cognac Button",
|
"remove_cognac_button": "Remove Cognac Button",
|
||||||
"remove_call_buttons": "Remove Call Buttons",
|
"remove_call_buttons": "Remove Call Buttons",
|
||||||
"disable_snap_splitting": "Disable Snap Splitting",
|
|
||||||
"block_ads": "Block Ads",
|
"block_ads": "Block Ads",
|
||||||
"streak_expiration_info": "Show Streak Expiration Info",
|
"streak_expiration_info": "Show Streak Expiration Info",
|
||||||
"new_map_ui": "New Map UI",
|
"new_map_ui": "New Map UI",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package me.rhunk.snapenhance.config
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import me.rhunk.snapenhance.config.impl.ConfigIntegerValue
|
import me.rhunk.snapenhance.config.impl.ConfigIntegerValue
|
||||||
import me.rhunk.snapenhance.config.impl.ConfigStateListValue
|
import me.rhunk.snapenhance.config.impl.ConfigStateListValue
|
||||||
|
import me.rhunk.snapenhance.config.impl.ConfigStateSelection
|
||||||
import me.rhunk.snapenhance.config.impl.ConfigStateValue
|
import me.rhunk.snapenhance.config.impl.ConfigStateValue
|
||||||
import me.rhunk.snapenhance.config.impl.ConfigStringValue
|
import me.rhunk.snapenhance.config.impl.ConfigStringValue
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -143,6 +144,33 @@ enum class ConfigProperty(
|
|||||||
ANTI_AUTO_SAVE("property.anti_auto_save", "description.anti_auto_save", ConfigCategory.EXTRAS, ConfigStateValue(false)),
|
ANTI_AUTO_SAVE("property.anti_auto_save", "description.anti_auto_save", ConfigCategory.EXTRAS, ConfigStateValue(false)),
|
||||||
SNAPCHAT_PLUS("property.snapchat_plus", "description.snapchat_plus", ConfigCategory.EXTRAS, ConfigStateValue(false)),
|
SNAPCHAT_PLUS("property.snapchat_plus", "description.snapchat_plus", ConfigCategory.EXTRAS, ConfigStateValue(false)),
|
||||||
|
|
||||||
|
DISABLE_SNAP_SPLITTING(
|
||||||
|
"property.disable_snap_splitting",
|
||||||
|
"description.disable_snap_splitting",
|
||||||
|
ConfigCategory.TWEAKS,
|
||||||
|
ConfigStateValue(false)
|
||||||
|
),
|
||||||
|
DISABLE_VIDEO_LENGTH_RESTRICTION(
|
||||||
|
"property.disable_video_length_restriction",
|
||||||
|
"description.disable_video_length_restriction",
|
||||||
|
ConfigCategory.TWEAKS,
|
||||||
|
ConfigStateValue(false)
|
||||||
|
),
|
||||||
|
OVERRIDE_MEDIA_QUALITY(
|
||||||
|
"property.override_media_quality",
|
||||||
|
"description.override_media_quality",
|
||||||
|
ConfigCategory.TWEAKS,
|
||||||
|
ConfigStateValue(false)
|
||||||
|
),
|
||||||
|
MEDIA_QUALITY_LEVEL(
|
||||||
|
"property.media_quality_level",
|
||||||
|
"description.media_quality_level",
|
||||||
|
ConfigCategory.TWEAKS,
|
||||||
|
ConfigStateSelection(
|
||||||
|
listOf("LEVEL_NONE", "LEVEL_1", "LEVEL_2", "LEVEL_3", "LEVEL_4", "LEVEL_5", "LEVEL_6", "LEVEL_7", "LEVEL_MAX"),
|
||||||
|
"LEVEL_NONE"
|
||||||
|
)
|
||||||
|
),
|
||||||
REMOVE_VOICE_RECORD_BUTTON(
|
REMOVE_VOICE_RECORD_BUTTON(
|
||||||
"property.remove_voice_record_button",
|
"property.remove_voice_record_button",
|
||||||
"description.remove_voice_record_button",
|
"description.remove_voice_record_button",
|
||||||
@@ -167,18 +195,6 @@ enum class ConfigProperty(
|
|||||||
ConfigCategory.TWEAKS,
|
ConfigCategory.TWEAKS,
|
||||||
ConfigStateValue(false)
|
ConfigStateValue(false)
|
||||||
),
|
),
|
||||||
DISABLE_SNAP_SPLITTING(
|
|
||||||
"property.disable_snap_splitting",
|
|
||||||
"description.disable_snap_splitting",
|
|
||||||
ConfigCategory.TWEAKS,
|
|
||||||
ConfigStateValue(false)
|
|
||||||
),
|
|
||||||
DISABLE_VIDEO_LENGTH_RESTRICTION(
|
|
||||||
"property.disable_video_length_restriction",
|
|
||||||
"description.disable_video_length_restriction",
|
|
||||||
ConfigCategory.TWEAKS,
|
|
||||||
ConfigStateValue(false)
|
|
||||||
),
|
|
||||||
BLOCK_ADS("property.block_ads", "description.block_ads", ConfigCategory.TWEAKS, ConfigStateValue(false)),
|
BLOCK_ADS("property.block_ads", "description.block_ads", ConfigCategory.TWEAKS, ConfigStateValue(false)),
|
||||||
STREAK_EXPIRATION_INFO(
|
STREAK_EXPIRATION_INFO(
|
||||||
"property.streak_expiration_info",
|
"property.streak_expiration_info",
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package me.rhunk.snapenhance.features.impl.extras
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.Logger
|
||||||
|
import me.rhunk.snapenhance.config.ConfigProperty
|
||||||
|
import me.rhunk.snapenhance.features.Feature
|
||||||
|
import me.rhunk.snapenhance.features.FeatureLoadParams
|
||||||
|
import me.rhunk.snapenhance.hook.HookStage
|
||||||
|
import me.rhunk.snapenhance.hook.Hooker
|
||||||
|
|
||||||
|
class MediaQualityLevelOverride : Feature("MediaQualityLevelOverride", loadParams = FeatureLoadParams.INIT_SYNC) {
|
||||||
|
override fun init() {
|
||||||
|
val enumQualityLevel = context.mappings.getMappedClass("enums", "QualityLevel")
|
||||||
|
|
||||||
|
Hooker.hookConstructor(context.androidContext.classLoader.loadClass("hfj"), HookStage.AFTER) {
|
||||||
|
Logger.log(it.thisObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
Hooker.hook(context.mappings.getMappedClass("MediaQualityLevelProvider"),
|
||||||
|
context.mappings.getMappedValue("MediaQualityLevelProviderMethod"),
|
||||||
|
HookStage.BEFORE,
|
||||||
|
{context.config.bool(ConfigProperty.OVERRIDE_MEDIA_QUALITY)}
|
||||||
|
) { param ->
|
||||||
|
val currentCompressionLevel = enumQualityLevel.enumConstants
|
||||||
|
.firstOrNull { it.toString() == context.config.state(ConfigProperty.MEDIA_QUALITY_LEVEL)} ?: run {
|
||||||
|
context.longToast("Invalid media quality level: ${context.config.state(ConfigProperty.MEDIA_QUALITY_LEVEL)}")
|
||||||
|
return@hook
|
||||||
|
}
|
||||||
|
Logger.debug("set media compression level to $currentCompressionLevel")
|
||||||
|
param.setResult(currentCompressionLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import me.rhunk.snapenhance.features.impl.downloader.AntiAutoDownload
|
|||||||
import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader
|
import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader
|
||||||
import me.rhunk.snapenhance.features.impl.extras.AntiAutoSave
|
import me.rhunk.snapenhance.features.impl.extras.AntiAutoSave
|
||||||
import me.rhunk.snapenhance.features.impl.extras.AutoSave
|
import me.rhunk.snapenhance.features.impl.extras.AutoSave
|
||||||
|
import me.rhunk.snapenhance.features.impl.extras.MediaQualityLevelOverride
|
||||||
import me.rhunk.snapenhance.features.impl.extras.DisableVideoLengthRestriction
|
import me.rhunk.snapenhance.features.impl.extras.DisableVideoLengthRestriction
|
||||||
import me.rhunk.snapenhance.features.impl.extras.ExternalMediaAsSnap
|
import me.rhunk.snapenhance.features.impl.extras.ExternalMediaAsSnap
|
||||||
import me.rhunk.snapenhance.features.impl.extras.Notifications
|
import me.rhunk.snapenhance.features.impl.extras.Notifications
|
||||||
@@ -68,6 +69,7 @@ class FeatureManager(private val context: ModContext) : Manager {
|
|||||||
register(AntiAutoSave::class)
|
register(AntiAutoSave::class)
|
||||||
register(UnlimitedSnapViewTime::class)
|
register(UnlimitedSnapViewTime::class)
|
||||||
register(DisableVideoLengthRestriction::class)
|
register(DisableVideoLengthRestriction::class)
|
||||||
|
register(MediaQualityLevelOverride::class)
|
||||||
|
|
||||||
initializeFeatures()
|
initializeFeatures()
|
||||||
}
|
}
|
||||||
@@ -80,6 +82,7 @@ class FeatureManager(private val context: ModContext) : Manager {
|
|||||||
action(feature)
|
action(feature)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
Logger.xposedLog("Failed to init feature ${feature.nameKey}", it)
|
Logger.xposedLog("Failed to init feature ${feature.nameKey}", it)
|
||||||
|
context.longToast("Failed to init feature ${feature.nameKey}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isAsync) {
|
if (!isAsync) {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package me.rhunk.snapenhance.mapping.impl
|
package me.rhunk.snapenhance.mapping.impl
|
||||||
|
|
||||||
|
import me.rhunk.snapenhance.Logger
|
||||||
import me.rhunk.snapenhance.Logger.debug
|
import me.rhunk.snapenhance.Logger.debug
|
||||||
import me.rhunk.snapenhance.mapping.Mapper
|
import me.rhunk.snapenhance.mapping.Mapper
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
|
import java.lang.reflect.Modifier
|
||||||
import java.util.Objects
|
import java.util.Objects
|
||||||
|
|
||||||
|
|
||||||
@@ -13,11 +15,22 @@ class EnumMapper : Mapper() {
|
|||||||
mappings: MutableMap<String, Any>
|
mappings: MutableMap<String, Any>
|
||||||
) {
|
) {
|
||||||
val enumMappings = HashMap<String, String>()
|
val enumMappings = HashMap<String, String>()
|
||||||
|
var enumQualityLevel: Class<*>? = null
|
||||||
|
|
||||||
//settings classes have an interface that extends Serializable and contains the getName method
|
//settings classes have an interface that extends Serializable and contains the getName method
|
||||||
//this enum classes are used to store the settings values
|
//this enum classes are used to store the settings values
|
||||||
//Setting enum class -> implements an interface -> getName method
|
//Setting enum class -> implements an interface -> getName method
|
||||||
classes.forEach { clazz ->
|
classes.forEach { clazz ->
|
||||||
if (!clazz.isEnum) return@forEach
|
if (!clazz.isEnum) return@forEach
|
||||||
|
|
||||||
|
//quality level enum
|
||||||
|
if (enumQualityLevel == null) {
|
||||||
|
if (clazz.enumConstants.any { it.toString().startsWith("LEVEL_NONE") }) {
|
||||||
|
enumMappings["QualityLevel"] = clazz.name
|
||||||
|
enumQualityLevel = clazz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (clazz.interfaces.isEmpty()) return@forEach
|
if (clazz.interfaces.isEmpty()) return@forEach
|
||||||
val serializableInterfaceClass = clazz.interfaces[0]
|
val serializableInterfaceClass = clazz.interfaces[0]
|
||||||
if (serializableInterfaceClass.methods
|
if (serializableInterfaceClass.methods
|
||||||
@@ -35,6 +48,18 @@ class EnumMapper : Mapper() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//find the media quality level provider
|
||||||
|
for (clazz in classes) {
|
||||||
|
if (!Modifier.isAbstract(clazz.modifiers)) continue
|
||||||
|
if (clazz.fields.none { Modifier.isTransient(it.modifiers) }) continue
|
||||||
|
clazz.methods.firstOrNull { it.returnType == enumQualityLevel }?.let {
|
||||||
|
mappings["MediaQualityLevelProvider"] = clazz.name
|
||||||
|
mappings["MediaQualityLevelProviderMethod"] = it.name
|
||||||
|
Logger.debug("found MediaQualityLevelProvider: ${clazz.name}.${it.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debug("found " + enumMappings.size + " enums")
|
debug("found " + enumMappings.size + " enums")
|
||||||
mappings["enums"] = enumMappings
|
mappings["enums"] = enumMappings
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user