commit bbfc0a835074a44f770ca85a9a3857e8b5a1f4c7
parent fd3e7e416e216cb41f0870c85a8d492523cba192
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun, 11 Jun 2023 01:04:35 +0200

feat: immersive camera preview

Diffstat:
Mapp/src/main/assets/lang/en_US.json | 1+
Mapp/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt | 6++++++
Mapp/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt | 28++++++++++++++++++----------
Mapp/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt | 4++++
Mapp/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt | 19++++++++++++++++++-
5 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/app/src/main/assets/lang/en_US.json b/app/src/main/assets/lang/en_US.json @@ -61,6 +61,7 @@ "hide_ui_elements": "Hide UI Elements", "auto_updater": "Auto Updater", "disable_camera": "Disable Camera", + "immersive_camera_preview": "Immersive Camera Preview", "infinite_story_boost": "Infinite Story Boost", "enable_app_appearance": "Enable App Appearance Settings", "disable_spotlight": "Disable Spotlight", diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt b/app/src/main/kotlin/me/rhunk/snapenhance/config/ConfigProperty.kt @@ -183,6 +183,12 @@ enum class ConfigProperty( ConfigCategory.UI_TWEAKS, ConfigStateValue(false) ), + IMMERSIVE_CAMERA_PREVIEW( + "property.immersive_camera_preview", + "description.immersive_camera_preview", + ConfigCategory.UI_TWEAKS, + ConfigStateValue(false) + ), HIDE_UI_ELEMENTS( "property.hide_ui_elements", "description.hide_ui_elements", diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt b/app/src/main/kotlin/me/rhunk/snapenhance/features/impl/ui/UITweaks.kt @@ -15,10 +15,15 @@ import me.rhunk.snapenhance.hook.hook class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CREATE_SYNC) { @SuppressLint("DiscouragedApi") override fun onActivityCreate() { + val hiddenElements = context.config.options(ConfigProperty.HIDE_UI_ELEMENTS) + val isImmersiveCamera = context.config.bool(ConfigProperty.IMMERSIVE_CAMERA_PREVIEW) val resources = context.resources - val capriViewfinderDefaultCornerRadius = context.resources.getIdentifier("capri_viewfinder_default_corner_radius", "dimen", Constants.SNAPCHAT_PACKAGE_NAME) - val ngsHovaNavLargerCameraButtonSize = context.resources.getIdentifier("ngs_hova_nav_larger_camera_button_size", "dimen", Constants.SNAPCHAT_PACKAGE_NAME) + val displayMetrics = context.resources.displayMetrics + + val capriViewfinderDefaultCornerRadius = resources.getIdentifier("capri_viewfinder_default_corner_radius", "dimen", Constants.SNAPCHAT_PACKAGE_NAME) + val ngsHovaNavLargerCameraButtonSize = resources.getIdentifier("ngs_hova_nav_larger_camera_button_size", "dimen", Constants.SNAPCHAT_PACKAGE_NAME) + val fullScreenSurfaceView = resources.getIdentifier("full_screen_surface_view", "id", Constants.SNAPCHAT_PACKAGE_NAME) val callButtonsStub = resources.getIdentifier("call_buttons_stub", "id", Constants.SNAPCHAT_PACKAGE_NAME) val callButton1 = resources.getIdentifier("friend_action_button3", "id", Constants.SNAPCHAT_PACKAGE_NAME) @@ -27,11 +32,10 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE val chatNoteRecordButton = resources.getIdentifier("chat_note_record_button", "id", Constants.SNAPCHAT_PACKAGE_NAME) val chatInputBarSticker = resources.getIdentifier("chat_input_bar_sticker", "id", Constants.SNAPCHAT_PACKAGE_NAME) val chatInputBarCognac = resources.getIdentifier("chat_input_bar_cognac", "id", Constants.SNAPCHAT_PACKAGE_NAME) - val hiddenElements = context.config.options(ConfigProperty.HIDE_UI_ELEMENTS) - Resources::class.java.methods.first { it.name == "getDimensionPixelSize"}.hook(HookStage.AFTER, { - hiddenElements["remove_camera_borders"] == true - }) { param -> + Resources::class.java.methods.first { it.name == "getDimensionPixelSize"}.hook(HookStage.AFTER, + { isImmersiveCamera } + ) { param -> val id = param.arg<Int>(0) if (id == capriViewfinderDefaultCornerRadius || id == ngsHovaNavLargerCameraButtonSize) { param.setResult(0) @@ -49,17 +53,21 @@ class UITweaks : Feature("UITweaks", loadParams = FeatureLoadParams.ACTIVITY_CRE } } - //TODO: use the event bus to dispatch a addView event - val addViewMethod = ViewGroup::class.java.getMethod( + ViewGroup::class.java.getMethod( "addView", View::class.java, Int::class.javaPrimitiveType, ViewGroup.LayoutParams::class.java - ) - Hooker.hook(addViewMethod, HookStage.BEFORE) { param -> + ).hook(HookStage.BEFORE) { param -> val view: View = param.arg(0) val viewId = view.id + if (isImmersiveCamera && view.id == fullScreenSurfaceView) { + Hooker.hookObjectMethod(View::class.java, view, "layout", HookStage.BEFORE) { param -> + param.setArg(3, displayMetrics.heightPixels) + } + } + if (viewId == chatNoteRecordButton && hiddenElements["remove_voice_record_button"] == true) { view.isEnabled = false view.setWillNotDraw(true) diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt b/app/src/main/kotlin/me/rhunk/snapenhance/hook/HookAdapter.kt @@ -13,6 +13,10 @@ class HookAdapter( return methodHookParam.thisObject as T } + fun <T : Any> nullableThisObject(): T? { + return methodHookParam.thisObject as T? + } + fun method(): Member { return methodHookParam.method } diff --git a/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt b/app/src/main/kotlin/me/rhunk/snapenhance/hook/Hooker.kt @@ -75,6 +75,23 @@ object Hooker { XposedBridge.hookAllConstructors(clazz, newMethodHook(stage, consumer, filter)) } + fun hookObjectMethod( + clazz: Class<*>, + instance: Any, + methodName: String, + stage: HookStage, + hookConsumer: (HookAdapter) -> Unit + ) { + val unhooks: MutableSet<XC_MethodHook.Unhook> = HashSet() + hook(clazz, methodName, stage) { param-> + if (param.nullableThisObject<Any>().let { + if (it == null) unhooks.forEach { u -> u.unhook() } + it != instance + }) return@hook + hookConsumer(param) + }.also { unhooks.addAll(it) } + } + fun ephemeralHookObjectMethod( clazz: Class<*>, instance: Any, @@ -84,7 +101,7 @@ object Hooker { ) { val unhooks: MutableSet<XC_MethodHook.Unhook> = HashSet() hook(clazz, methodName, stage) { param-> - if (param.thisObject<Any>() != instance) return@hook + if (param.nullableThisObject<Any>() != instance) return@hook hookConsumer(param) unhooks.forEach{ it.unhook() } }.also { unhooks.addAll(it) }