1 Commits

Author SHA1 Message Date
ΞTΞRNAL
f3c794fc47 v1.6.4 2026-04-02 00:22:02 +05:30
12 changed files with 452 additions and 17 deletions

View File

@@ -33,8 +33,8 @@ tasks.register<GetVersionTask>("getVersion") {
}
// You can still set these for legacy use by submodules or scripts:
rootProject.ext.set("appVersionName", providers.gradleProperty("APP_VERSION_NAME").orElse("1.6.3").get())
rootProject.ext.set("appVersionCode", providers.gradleProperty("APP_VERSION_CODE").orElse("316").get().toInt())
rootProject.ext.set("appVersionName", providers.gradleProperty("APP_VERSION_NAME").orElse("1.6.4").get())
rootProject.ext.set("appVersionCode", providers.gradleProperty("APP_VERSION_CODE").orElse("318").get().toInt())
rootProject.ext.set("applicationId", "me.eternal.purrfectsnap")
// buildHash: when PurrfectSnap or Snapchat is updated, mappings become outdated and auto-regenerate.
// Include version code so each release has a different hash; use random for uniqueness within same version.

View File

@@ -1,3 +1,9 @@
## v1.6.4
- New: Performance mode feature!(Smooth & Max)
- Fix: Failed to init feature Device Spoofer for some devices
- Fix: Increase the default queue of auto open snaps
- Fix: user_conversation error spam
## v1.6.3
- Fix: Blank Global Settings page
- New: Backup & Restore option for Randomized Device Profile feature!

View File

@@ -1909,6 +1909,16 @@
"name": "Disable Metrics",
"description": "Blocks sending specific analytic data to Snapchat"
},
"performance_mode": {
"name": "Performance Mode",
"description": "Applies an app-wide speed profile for navigation, preview loading, background work, and camera responsiveness",
"properties": {
"performance_profile": {
"name": "Performance Profile",
"description": "Select how aggressively PurrfectSnap pushes app-wide performance tuning"
}
}
},
"disable_story_sections": {
"name": "Disable Story Sections",
"description": "Removes sections from the Stories page\nMay require a refresh to work properly"
@@ -3065,6 +3075,11 @@
"custom_image_upload_format": {
"null": "Automatic"
},
"performance_profile": {
"smooth": "Smooth",
"max": "Max",
"null": "Disabled"
},
"update_check_frequency": {
"daily": "Daily",
"weekly": "Weekly",

View File

@@ -38,9 +38,16 @@ class Global : ConfigContainer() {
val customUploadImageFormat = unique("custom_image_upload_format", "jpeg", "png", "webp") { requireRestart(); addFlags(ConfigFlag.NO_TRANSLATE) }
}
inner class PerformanceModeConfig : ConfigContainer() {
val profile = unique("performance_profile", "smooth", "max") {
requireRestart()
}
}
val betterLocation = container("better_location", BetterLocationConfig())
val snapchatPlus = unique("snapchat_plus", "not_subscribed", "basic", "ad_free") { requireRestart() }
val mediaUploadQualityConfig = container("media_upload_quality", MediaUploadQualityConfig())
val performanceMode = container("performance_mode", PerformanceModeConfig()) { requireRestart() }
val disableConfirmationDialogs = multiple("disable_confirmation_dialogs", "erase_message", "remove_friend", "block_friend", "ignore_friend", "hide_friend", "hide_conversation", "clear_conversation") { requireRestart() }
val disableMetrics = boolean("disable_metrics") { requireRestart() }
val disableStorySections = multiple("disable_story_sections", "friends", "suggested_stories", "following", "discover") { requireRestart(); requireCleanCache() }

View File

@@ -166,7 +166,7 @@ class MessagingTweaks : ConfigContainer() {
val maxDelayMs = integer("max_delay_ms", defaultValue = 100) {
inputCheck = { it.toIntOrNull()?.coerceAtLeast(0) != null && it.toInt() > minDelay.get() }
}
val queueSize = integer("queue_size", defaultValue = 10) {
val queueSize = integer("queue_size", defaultValue = 1000) {
inputCheck = { it.toIntOrNull()?.coerceAtLeast(1) != null }
}
val retryAttempts = integer("retry_attempts", defaultValue = 5) {

View File

@@ -47,6 +47,14 @@ class DatabaseAccess(
} == true
}
private val hasArroyoUserConversationTable by lazy {
useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'user_conversation'")?.use { query ->
query.moveToFirst() && query.getStringOrNull("name") == "user_conversation"
}
} == true
}
private fun useDatabase(database: DatabaseType, writeMode: Boolean = false): SQLiteDatabase? {
// only cache read-only databases
if (!writeMode && openedDatabases.containsKey(database) && openedDatabases[database]?.isOpen == true) {
@@ -148,6 +156,10 @@ class DatabaseAccess(
}?.toMutableMap() ?: mutableMapOf()
}
if (!hasArroyoUserConversationTable) {
return@lazy mutableMapOf()
}
(useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT client_conversation_id, conversation_type, user_id FROM user_conversation WHERE user_id != ?",
@@ -354,7 +366,7 @@ class DatabaseAccess(
}
fun getConversationType(conversationId: String): Int? {
if (hasArroyoConversationTable) {
if (hasArroyoConversationTable || !hasArroyoUserConversationTable) {
return getFeedEntryByConversationId(conversationId)?.conversationType
}
@@ -372,7 +384,9 @@ class DatabaseAccess(
}
fun getDMConversationId(userId: String): String? {
if (hasArroyoConversationTable) {
friendDMsCache[userId]?.let { return it }
if (hasArroyoConversationTable || !hasArroyoUserConversationTable) {
return friendDMsCache[userId]
}
@@ -408,6 +422,10 @@ class DatabaseAccess(
}
}
if (!hasArroyoUserConversationTable) {
return getFeedEntryByConversationId(conversationId)?.participants
}
return useDatabase(DatabaseType.ARROYO)?.performOperation {
safeRawQuery(
"SELECT user_id FROM user_conversation WHERE client_conversation_id = ?",

View File

@@ -99,6 +99,7 @@ class FeatureManager(
MeoPasscodeBypass(),
AppLock(),
CameraTweaks(),
PerformanceMode(),
InfiniteStoryBoost(),
PinConversations(),
DeviceSpooferHook(),

View File

@@ -37,11 +37,32 @@ class ConfigurationOverride : Feature("Configuration Override") {
}.getOrNull()
val propertyOverrides = mutableMapOf<String, ConfigFilter>()
val loggedOverrides = mutableSetOf<String>()
fun overrideProperty(key: String, filter: (ConfigKeyInfo) -> Boolean, value: (ConfigKeyInfo) -> Any?, isAppExperiment: Boolean = false) {
propertyOverrides[key] = ConfigFilter(filter, value, isAppExperiment)
}
fun logPerformanceOverride(key: String, value: Any?) {
if (!key.contains("PRELOAD") &&
!key.contains("PERFORMANCE") &&
!key.contains("WARM") &&
!key.contains("PREFETCH") &&
!key.contains("LATENCY") &&
!key.contains("ANALYTICS") &&
!key.contains("THREAD_PRIORITY") &&
!key.contains("HD_MODE") &&
!key.contains("LENS") &&
!key.contains("THUMBNAIL")
) {
return
}
synchronized(loggedOverrides) {
if (!loggedOverrides.add(key)) return
}
context.log.info("Performance override applied: $key=$value", "PerformanceMode")
}
overrideProperty("STREAK_EXPIRATION_INFO", { context.config.userInterface.streakExpirationInfo.get() },
{ true })
overrideProperty("TRANSCODING_MAX_QUALITY", { context.config.global.mediaUploadQualityConfig.forceVideoUploadSourceQuality.get() },
@@ -84,6 +105,44 @@ class ConfigurationOverride : Feature("Configuration Override") {
{ true })
overrideProperty("MEDIA_RECORDER_MAX_QUALITY_LEVEL", { context.config.camera.forceCameraSourceEncoding.get() },
{ true })
overrideProperty("PREVIEW_PRELOAD_ACTIVATOR", { context.config.global.performanceMode.profile.getNullable() != null },
{ true })
overrideProperty("BUFFERED_VIDEO_RECORDING_ACTIVATOR", { context.config.global.performanceMode.profile.getNullable() != null },
{ true })
overrideProperty("CAMERA_THREAD_PRIORITY", { context.config.global.performanceMode.profile.getNullable() != null },
{ true })
overrideProperty("HD_MODE_ACTIVATOR", { context.config.global.performanceMode.profile.getNullable() == "max" },
{ true })
arrayOf(
"FEATURE_PRELOADER",
"USER_STORY_PRELOAD",
"STARTUP_LENS_ACTIVATOR",
"LENSES_PREVIEW_ACTIVATOR",
"THUMBNAIL_PRESENTER_ACTIVATOR",
"SINGLE_SEGMENT_THUMBNAIL_ACTIVATOR",
"SERVER_PREFETCH",
"SERVER_PREFETCH_WITH_COF",
"DISCOVER_FEED_PERFORMANCE",
"DISCOVER_FEED_STORY_PREFETCH",
"DISCOVER_FEED_THUMBNAILS",
"LOGIN_PRELOAD",
"PREFETCH_REPO_SUBSCRIBE_ON_CPU",
"COMPUTE_FEED_CACHE_WITH_TTL",
"COMPUTE_FEED_NETWORK_WITH_CACHE",
"OPERA_WARMUP",
"REFACTORED_WITH_WARMUP_LENS",
"SHOW_PREFETCH",
).forEach { key ->
overrideProperty(key, { context.config.global.performanceMode.profile.getNullable() != null }, { true })
}
overrideProperty("LOAD_LATENCY_TRACKER_ACTIVATOR", { context.config.global.performanceMode.profile.getNullable() != null },
{ false })
overrideProperty("ANALYTICS_ACTIVATOR", { context.config.global.performanceMode.profile.getNullable() != null },
{ false })
overrideProperty("LOCK_SCREEN_ANALYTICS_ACTIVATOR", { context.config.global.performanceMode.profile.getNullable() != null },
{ false })
overrideProperty("REDUCE_MY_PROFILE_UI_COMPLEXITY", { context.config.userInterface.mapFriendNameTags.get() },
{ true })
@@ -115,7 +174,10 @@ class ConfigurationOverride : Feature("Configuration Override") {
propertyOverrides[propertyKey.name]?.let { (filter, value) ->
if (!filter(propertyKey)) return@let
param.setResult(value(propertyKey))
value(propertyKey).also {
logPerformanceOverride(propertyKey.name ?: return@also, it)
param.setResult(it)
}
}
}
@@ -135,7 +197,10 @@ class ConfigurationOverride : Feature("Configuration Override") {
propertyOverrides[key]?.let { (filter, value) ->
val keyInfo = getConfigKeyInfo(enumData) ?: return@let
if (!filter(keyInfo)) return@let
setValue(value(keyInfo))
value(keyInfo).also {
logPerformanceOverride(key, it)
setValue(it)
}
}
}
@@ -151,7 +216,10 @@ class ConfigurationOverride : Feature("Configuration Override") {
}
propertyOverrides[keyInfo.name]?.let { (filter, value, isAppExperiment) ->
if (isAppExperiment != true || !filter(keyInfo)) return@let
param.setResult(value(keyInfo))
value(keyInfo).also {
logPerformanceOverride(keyInfo.name ?: return@also, it)
param.setResult(it)
}
}
}
@@ -174,7 +242,10 @@ class ConfigurationOverride : Feature("Configuration Override") {
}
val propertyOverride = propertyOverrides[keyInfo.name] ?: return@hook
propertyOverride.isAppExperiment.takeIf { propertyOverride.filter(keyInfo) }?.let { param.setResult(it) }
propertyOverride.isAppExperiment.takeIf { propertyOverride.filter(keyInfo) }?.let {
logPerformanceOverride(keyInfo.name ?: return@let, it)
param.setResult(it)
}
}
}
}.onFailure {

View File

@@ -385,7 +385,7 @@ object RandomizedDeviceProfileStore {
bluetoothMacAddress = bluetoothMac,
ipAddress = ipAddress,
wifiSsid = region.randomWifiSsid(),
wifiRssi = random.nextInt(-72, -36),
wifiRssi = randomInt(-72, -36),
localeTag = locale.toLanguageTag(),
countryIso = region.countryIso,
timeZoneId = region.timeZoneId,
@@ -450,7 +450,7 @@ object RandomizedDeviceProfileStore {
private fun randomPublicIpv4(prefixes: List<Int>? = null): String {
val firstOctet = prefixes?.takeIf { it.isNotEmpty() }?.let { pick(it) } ?: run {
generateSequence { random.nextInt(1, 224) }
generateSequence { randomInt(1, 224) }
.first { candidate ->
candidate != 10 &&
candidate != 127 &&
@@ -459,13 +459,18 @@ object RandomizedDeviceProfileStore {
candidate != 192
}
}
val secondOctet = random.nextInt(1, 255)
val thirdOctet = random.nextInt(1, 255)
val fourthOctet = random.nextInt(2, 255)
val secondOctet = randomInt(1, 255)
val thirdOctet = randomInt(1, 255)
val fourthOctet = randomInt(2, 255)
val candidate = "$firstOctet.$secondOctet.$thirdOctet.$fourthOctet"
return runCatching { InetAddress.getByName(candidate).hostAddress }.getOrDefault(candidate)
}
private fun randomInt(minInclusive: Int, maxExclusive: Int): Int {
require(maxExclusive > minInclusive)
return minInclusive + random.nextInt(maxExclusive - minInclusive)
}
private fun randomLong(minInclusive: Long, maxExclusive: Long): Long {
require(maxExclusive > minInclusive)
val bound = maxExclusive - minInclusive

View File

@@ -7,7 +7,7 @@ import me.eternal.purrfectsnap.core.features.Feature
class DisableMetrics : Feature("DisableMetrics") {
override fun init() {
if (!context.config.global.disableMetrics.get()) return
if (!context.config.global.disableMetrics.get() && context.config.global.performanceMode.profile.getNullable() == null) return
context.event.subscribe(NetworkApiRequestEvent::class) { param ->
val url = param.url

View File

@@ -0,0 +1,312 @@
package me.eternal.purrfectsnap.core.features.impl.tweaks
import android.animation.ValueAnimator
import android.app.Activity
import android.database.sqlite.SQLiteDatabase
import android.hardware.camera2.CaptureRequest
import android.media.MediaRecorder
import android.os.Build
import android.transition.Transition
import android.os.HandlerThread
import android.os.Process
import android.util.Range
import android.view.View
import android.view.ViewPropertyAnimator
import android.view.animation.Animation
import android.widget.OverScroller
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import java.lang.Thread
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.ThreadPoolExecutor
import me.eternal.purrfectsnap.core.features.Feature
import me.eternal.purrfectsnap.core.util.hook.HookStage
import me.eternal.purrfectsnap.core.util.hook.hook
import me.eternal.purrfectsnap.core.util.hook.hookConstructor
import okhttp3.Dispatcher
class PerformanceMode : Feature("Performance Mode") {
override fun init() {
val profile = context.config.global.performanceMode.profile.getNullable() ?: return
val isMaxProfile = profile == "max"
val threadPriority = if (isMaxProfile) {
Process.THREAD_PRIORITY_DISPLAY
} else {
Process.THREAD_PRIORITY_MORE_FAVORABLE
}
val minimumFrameRate = if (isMaxProfile) 60 else 45
val durationScale = if (isMaxProfile) 0.35f else 0.55f
val recyclerViewCacheSize = if (isMaxProfile) 64 else 32
val maxRequests = if (isMaxProfile) 192 else 96
val maxRequestsPerHost = if (isMaxProfile) 32 else 16
val minimumCoreThreads = if (isMaxProfile) 16 else 8
val prefetchItemCount = if (isMaxProfile) 24 else 12
val maxAnimationDurationMs = if (isMaxProfile) 90L else 140L
val maxScrollDurationMs = if (isMaxProfile) 120 else 180
val preferredRefreshRate = if (isMaxProfile) 120f else 90f
context.log.info(
"Performance mode enabled: profile=$profile, threadPriority=$threadPriority, minFps=$minimumFrameRate, durationScale=$durationScale, rvCache=$recyclerViewCacheSize, maxRequests=$maxRequests/$maxRequestsPerHost, minCoreThreads=$minimumCoreThreads, prefetch=$prefetchItemCount, maxAnimMs=$maxAnimationDurationMs, maxScrollMs=$maxScrollDurationMs, preferredRefreshRate=$preferredRefreshRate",
"PerformanceMode"
)
runCatching {
ValueAnimator.setFrameDelay(0L)
context.log.info("Applied ValueAnimator frame delay override: 0ms", "PerformanceMode")
}
fun firstHitLogger(name: String): (String) -> Unit {
val didLog = AtomicBoolean(false)
return { details ->
if (didLog.compareAndSet(false, true)) {
context.log.info("First hit: $name | $details", "PerformanceMode")
}
}
}
val handlerThreadConstructorLog = firstHitLogger("HandlerThread.constructor")
val handlerThreadStartLog = firstHitLogger("HandlerThread.start")
val threadStartLog = firstHitLogger("Thread.start")
val executorLog = firstHitLogger("ThreadPoolExecutor.constructor")
val dispatcherLog = firstHitLogger("OkHttp.Dispatcher.constructor")
val animatorLog = firstHitLogger("ValueAnimator.getDurationScale")
val animatorDurationLog = firstHitLogger("ValueAnimator.setDuration")
val viewAnimatorDurationLog = firstHitLogger("ViewPropertyAnimator.setDuration")
val transitionDurationLog = firstHitLogger("Transition.setDuration")
val animationDurationLog = firstHitLogger("Animation.setDuration")
val recyclerCtorLog = firstHitLogger("RecyclerView.constructor")
val recyclerAdapterLog = firstHitLogger("RecyclerView.setAdapter")
val recyclerLayoutManagerLog = firstHitLogger("RecyclerView.setLayoutManager")
val sqliteOpenLog = firstHitLogger("SQLiteDatabase.openDatabase")
val sqliteCreateLog = firstHitLogger("SQLiteDatabase.openOrCreateDatabase")
val mediaRecorderLog = firstHitLogger("MediaRecorder.setVideoFrameRate")
val captureRequestLog = firstHitLogger("CaptureRequest.Builder.set")
val sustainedModeLog = firstHitLogger("Window.setSustainedPerformanceMode")
val refreshRateLog = firstHitLogger("Activity.preferredRefreshRate")
val overScrollerLog = firstHitLogger("OverScroller.startScroll")
fun isPerformanceSensitiveThread(name: String?): Boolean {
val normalizedName = name?.lowercase() ?: return false
return listOf("camera", "preview", "codec", "render", "gl", "transcod", "lens", "feed", "story", "opera", "messag", "network", "db", "disk").any {
normalizedName.contains(it)
}
}
HandlerThread::class.java.hookConstructor(HookStage.BEFORE) { param ->
if (param.args().size < 2) return@hookConstructor
val threadName = param.argNullable<String>(0)
if (!isPerformanceSensitiveThread(threadName)) return@hookConstructor
param.setArg(1, threadPriority)
handlerThreadConstructorLog("name=$threadName priority=$threadPriority")
}
HandlerThread::class.java.hook("start", HookStage.AFTER) { param ->
val thread = param.nullableThisObject<Any>() as? HandlerThread ?: return@hook
if (!isPerformanceSensitiveThread(thread.name)) return@hook
runCatching {
val tid = thread.threadId
if (tid > 0) {
Process.setThreadPriority(tid, threadPriority)
}
}
handlerThreadStartLog("name=${thread.name} tid=${thread.threadId} priority=$threadPriority")
}
Thread::class.java.hook("start", HookStage.AFTER) { param ->
val thread = param.thisObject<Thread>()
if (!isPerformanceSensitiveThread(thread.name)) return@hook
runCatching {
thread.priority = Thread.MAX_PRIORITY
}
threadStartLog("name=${thread.name} priority=${thread.priority}")
}
ThreadPoolExecutor::class.java.hookConstructor(HookStage.AFTER) { param ->
val executor = param.thisObject<ThreadPoolExecutor>()
runCatching {
val targetCorePoolSize = executor.maximumPoolSize.coerceAtLeast(1).coerceAtMost(minimumCoreThreads.coerceAtLeast(executor.corePoolSize))
if (executor.corePoolSize < targetCorePoolSize) {
executor.corePoolSize = targetCorePoolSize
}
executor.allowCoreThreadTimeOut(false)
executor.prestartAllCoreThreads()
executorLog("core=${executor.corePoolSize} max=${executor.maximumPoolSize} active=${executor.activeCount}")
}
}
Dispatcher::class.java.hookConstructor(HookStage.AFTER) { param ->
val dispatcher = param.thisObject<Dispatcher>()
runCatching {
dispatcher.maxRequests = maxRequests
dispatcher.maxRequestsPerHost = maxRequestsPerHost
dispatcherLog("maxRequests=${dispatcher.maxRequests} maxRequestsPerHost=${dispatcher.maxRequestsPerHost}")
}
}
ValueAnimator::class.java.hook("getDurationScale", HookStage.AFTER) { param ->
param.setResult(durationScale)
animatorLog("durationScale=$durationScale")
}
ValueAnimator::class.java.hook("setDuration", HookStage.BEFORE) { param ->
val original = param.arg<Long>(0)
val updated = original.coerceAtMost(maxAnimationDurationMs)
if (updated != original) {
param.setArg(0, updated)
}
animatorDurationLog("requested=$original applied=${param.arg<Long>(0)}")
}
ViewPropertyAnimator::class.java.hook("setDuration", HookStage.BEFORE) { param ->
val original = param.arg<Long>(0)
val updated = original.coerceAtMost(maxAnimationDurationMs)
if (updated != original) {
param.setArg(0, updated)
}
viewAnimatorDurationLog("requested=$original applied=${param.arg<Long>(0)}")
}
Transition::class.java.hook("setDuration", HookStage.BEFORE) { param ->
val original = param.arg<Long>(0)
val updated = original.coerceAtMost(maxAnimationDurationMs)
if (updated != original) {
param.setArg(0, updated)
}
transitionDurationLog("requested=$original applied=${param.arg<Long>(0)}")
}
Animation::class.java.hook("setDuration", HookStage.BEFORE) { param ->
val original = param.arg<Long>(0)
val updated = original.coerceAtMost(maxAnimationDurationMs)
if (updated != original) {
param.setArg(0, updated)
}
animationDurationLog("requested=$original applied=${param.arg<Long>(0)}")
}
RecyclerView::class.java.hookConstructor(HookStage.AFTER) { param ->
val recyclerView = param.thisObject<RecyclerView>()
recyclerView.setItemViewCacheSize(recyclerViewCacheSize)
recyclerView.overScrollMode = View.OVER_SCROLL_NEVER
recyclerView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
if (isMaxProfile) {
recyclerView.itemAnimator = null
}
recyclerCtorLog("cache=$recyclerViewCacheSize max=$isMaxProfile class=${recyclerView::class.java.name}")
}
RecyclerView::class.java.hook("setAdapter", HookStage.AFTER) { param ->
val recyclerView = param.thisObject<RecyclerView>()
recyclerView.setItemViewCacheSize(recyclerViewCacheSize)
recyclerView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
if (isMaxProfile) {
recyclerView.itemAnimator = null
}
recyclerAdapterLog("cache=$recyclerViewCacheSize adapter=${param.argNullable<Any>(0)?.javaClass?.name}")
}
RecyclerView::class.java.hook("setLayoutManager", HookStage.AFTER) { param ->
val recyclerView = param.thisObject<RecyclerView>()
val layoutManager = param.argNullable<Any>(0)
when (layoutManager) {
is LinearLayoutManager -> {
layoutManager.isItemPrefetchEnabled = true
layoutManager.initialPrefetchItemCount = prefetchItemCount
}
is StaggeredGridLayoutManager -> {
layoutManager.isItemPrefetchEnabled = true
layoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
}
}
recyclerView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
recyclerLayoutManagerLog("layoutManager=${layoutManager?.javaClass?.name} prefetch=$prefetchItemCount")
}
fun SQLiteDatabase.applyPerformancePragmas() {
runCatching { execSQL("PRAGMA synchronous = NORMAL") }
runCatching { execSQL("PRAGMA temp_store = MEMORY") }
runCatching { execSQL("PRAGMA cache_size = -32768") }
runCatching { execSQL("PRAGMA mmap_size = 268435456") }
runCatching { execSQL("PRAGMA journal_size_limit = 1048576") }
runCatching { execSQL("PRAGMA optimize") }
}
SQLiteDatabase::class.java.hook("openDatabase", HookStage.AFTER) { param ->
(param.getResult() as? SQLiteDatabase)?.also {
it.applyPerformancePragmas()
sqliteOpenLog("path=${param.argNullable<Any>(0)}")
}
}
SQLiteDatabase::class.java.hook("openOrCreateDatabase", HookStage.AFTER) { param ->
(param.getResult() as? SQLiteDatabase)?.also {
it.applyPerformancePragmas()
sqliteCreateLog("path=${param.argNullable<Any>(0)}")
}
}
MediaRecorder::class.java.hook("setVideoFrameRate", HookStage.BEFORE) { param ->
val currentRate = param.arg<Int>(0)
if (currentRate < minimumFrameRate) {
param.setArg(0, minimumFrameRate)
}
mediaRecorderLog("requested=$currentRate applied=${param.arg<Int>(0)}")
}
OverScroller::class.java.hook("startScroll", HookStage.BEFORE) { param ->
if (param.args().size >= 5) {
val original = param.arg<Int>(4)
val updated = original.coerceAtMost(maxScrollDurationMs)
if (updated != original) {
param.setArg(4, updated)
}
overScrollerLog("requested=$original applied=${param.arg<Int>(4)}")
}
}
CaptureRequest.Builder::class.java.hook("set", HookStage.BEFORE) { param ->
val key = param.arg<CaptureRequest.Key<*>>(0)
when (key) {
CaptureRequest.EDGE_MODE -> param.setArg(1, CaptureRequest.EDGE_MODE_FAST)
CaptureRequest.NOISE_REDUCTION_MODE -> param.setArg(1, CaptureRequest.NOISE_REDUCTION_MODE_FAST)
CaptureRequest.HOT_PIXEL_MODE -> param.setArg(1, CaptureRequest.HOT_PIXEL_MODE_FAST)
CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE -> param.setArg(1, CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE_FAST)
CaptureRequest.CONTROL_AF_MODE -> param.setArg(1, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE -> param.setArg(1, CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_OFF)
CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE -> {
val currentRange = param.argNullable<Any>(1) as? Range<*>
val lower = (currentRange?.lower as? Int) ?: minimumFrameRate
val upper = (currentRange?.upper as? Int) ?: minimumFrameRate
if (upper < minimumFrameRate) {
param.setArg(1, Range(lower.coerceAtMost(minimumFrameRate), minimumFrameRate))
}
}
}
captureRequestLog("key=${key.name} value=${param.argNullable<Any>(1)}")
}
fun applyActivityPerformanceTuning(activity: Activity) {
runCatching {
activity.window.decorView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
val display = activity.display
val targetRefreshRate = display?.supportedModes?.maxByOrNull { it.refreshRate }?.refreshRate
?.coerceAtLeast(preferredRefreshRate) ?: preferredRefreshRate
activity.window.attributes = activity.window.attributes.apply {
this.preferredRefreshRate = targetRefreshRate
}
refreshRateLog("activity=${activity::class.java.name} refreshRate=$targetRefreshRate")
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isMaxProfile) {
runCatching {
activity.window.setSustainedPerformanceMode(true)
sustainedModeLog("activity=${activity::class.java.name}")
}
}
}
onNextActivityCreate {
applyActivityPerformanceTuning(it)
}
}
}

View File

@@ -7,8 +7,8 @@ org.gradle.configuration-cache=true
org.gradle.configuration-cache.problems=warn
nativeAbis=arm64-v8a
APP_VERSION_NAME=1.6.3
APP_VERSION_CODE=316
APP_VERSION_NAME=1.6.4
APP_VERSION_CODE=318
debug_build_hash=18fe2a814d0e2eb5
psIntegrityPinnedSha256=
EXPECTED_CERT_SHA256=0f188cb17d8ea4902ee15ce98f4928ba4226a4790fa3c52f62d776b08e46ca1c