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
Binary file modified app/libs/snapmod.jar
Binary file not shown.
37 changes: 37 additions & 0 deletions app/src/main/java/xyz/rodit/snapmod/SettingsActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package xyz.rodit.snapmod
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.ImageFormat
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.net.Uri
import android.os.Bundle
import android.text.InputType
Expand All @@ -14,6 +17,7 @@ import android.view.inputmethod.EditorInfo
import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import xyz.rodit.xposed.SettingsActivity
Expand Down Expand Up @@ -76,6 +80,8 @@ class SettingsActivity : SettingsActivity(R.xml.root_preferences) {
setNumericInput(fragment, "location_share_lat")
setNumericInput(fragment, "location_share_long")
setNumericInput(fragment, "audio_playback_speed")
setNumericInput(fragment, "custom_video_fps")
setNumericInput(fragment, "custom_video_bitrate")

(fragment.findPreference<Preference>("hidden_friends") as EditTextPreference?)?.apply {
setOnBindEditTextListener {
Expand All @@ -85,6 +91,30 @@ class SettingsActivity : SettingsActivity(R.xml.root_preferences) {
it.setSelection(it.text.length)
}
}

val camera = getSystemService(CAMERA_SERVICE) as CameraManager
val characteristics = camera.getCameraCharacteristics(camera.cameraIdList[0])
val config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
config?.let { c ->
val sizes = c.getOutputSizes(ImageFormat.JPEG)
val strings = sizes.map { s -> "${s.width}x${s.height}" }.toTypedArray()
sequenceOf(
"custom_image_resolution",
"custom_video_resolution"
).map { fragment.findPreference<ListPreference>(it) }
.filterNotNull()
.forEach {
it.entries = strings + "Default"
it.entryValues = strings + "0"
}
}

fragment.findPreference<Preference>("camera_readme")?.apply {
onPreferenceClickListener = Preference.OnPreferenceClickListener {
showCameraReadme()
true
}
}
}

private fun setNumericInput(fragment: SettingsFragment, name: String) {
Expand All @@ -102,6 +132,13 @@ class SettingsActivity : SettingsActivity(R.xml.root_preferences) {
.show()
}

private fun showCameraReadme() {
AlertDialog.Builder(this)
.setTitle(R.string.camera_title)
.setMessage(R.string.camera_dialog_description)
.show()
}

private fun getInstallationSummary(detailed: Boolean): Spannable {
val builder = SpannableStringBuilder()
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class FeatureManager(context: FeatureContext) : Contextual(context) {
// Tweaks
add(::BypassVideoLength)
add(::BypassVideoLengthGlobal)
add(::CameraResolution)
add(::ConfigurationTweaks)
add(::ConfigurationTweaks)
add(::DisableBitmojis)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ class AdditionalFriendInfo(context: FeatureContext) : Feature(context) {
override fun performHooks() {
// Show more info in friend profile footer.
FriendProfileTransformer.apply.after(context, "more_profile_info") {
if (!FriendProfilePageData.isInstance(it.args[0]) || it.result !is List<*>) return@after
val transformer = FriendProfileTransformer.wrap(it.thisObject)
if (!FriendProfilePageData.isInstance(transformer.data) || it.result !is List<*>) return@after

val viewModelList = it.result as List<*>
if (viewModelList.isEmpty()) return@after

val viewModel = viewModelList[0]!!
if (!FooterInfoItem.isInstance(viewModel)) return@after

val data = FriendProfilePageData.wrap(it.args[0])
val data = FriendProfilePageData.wrap(transformer.data)
val friendDate = Date(max(data.addedTimestamp, data.reverseAddedTimestamp))
val birthday = if (data.birthday.isNull) CalendarDate(13, -1) else data.birthday

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package xyz.rodit.snapmod.features.tweaks

import xyz.rodit.snapmod.features.Feature
import xyz.rodit.snapmod.features.FeatureContext
import xyz.rodit.snapmod.mappings.MediaQualityLevel
import xyz.rodit.snapmod.mappings.RecordingCodecConfiguration
import xyz.rodit.snapmod.mappings.ScCameraSettings
import xyz.rodit.snapmod.mappings.TranscodingRequest
import xyz.rodit.snapmod.util.after
import xyz.rodit.snapmod.util.getNonDefault
import xyz.rodit.snapmod.util.getResolution

class CameraResolution(context: FeatureContext) : Feature(context) {

override fun performHooks() {
// Override preview and picture resolution
ScCameraSettings.constructors.after {
val settings = ScCameraSettings.wrap(it.thisObject)
context.config.getResolution("custom_video_resolution")?.let { r ->
val previewResolution = settings.previewResolution
if (previewResolution.isNotNull) {
previewResolution.width = r.width
previewResolution.height = r.height
}
}

context.config.getResolution("custom_image_resolution")?.let { r ->
val pictureResolution = settings.pictureResolution
if (pictureResolution.isNotNull) {
pictureResolution.width = r.width
pictureResolution.height = r.height
}
}
}

// Override actual recording resolution
RecordingCodecConfiguration.constructors.after {
val config = RecordingCodecConfiguration.wrap(it.thisObject)
context.config.getResolution("custom_video_resolution")?.let { r ->
val res = config.resolution
res.width = r.width
res.height = r.height
}

context.config.getNonDefault("custom_video_bitrate")?.let { bitrate ->
config.bitrate = bitrate
}
}

// Override save/send quality level
TranscodingRequest.constructors.after(context, "force_source_encoding") {
TranscodingRequest.wrap(it.thisObject).qualityLevel = MediaQualityLevel.LEVEL_MAX()
}
}
}
11 changes: 11 additions & 0 deletions app/src/main/java/xyz/rodit/snapmod/logging/XLogUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package xyz.rodit.snapmod.logging

import android.util.Log
import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import java.util.*

private val logMap = WeakHashMap<Any, XLog>()
Expand Down Expand Up @@ -44,4 +46,13 @@ fun XLog.dumpMethodCall(param: XC_MethodHook.MethodHookParam) {
"Arguments:\n" +
param.args.mapIndexed { i, o -> "$i: $o" }.joinToString("\n")
)
}

fun XLog.dumpConstruction(className: String, classLoader: ClassLoader) {
val cls = XposedHelpers.findClass(className, classLoader)
XposedBridge.hookAllConstructors(cls, object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
debug("${param.thisObject}")
}
})
}
20 changes: 19 additions & 1 deletion app/src/main/java/xyz/rodit/snapmod/util/ConfigExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,22 @@ fun ConfigurationClient.getList(key: String): List<String> {
.split(',')
.filter(String::isNotBlank)
.map(String::trim)
}
}

fun ConfigurationClient.getNonDefault(key: String, default: Int = 0): Int? {
val value = this.getInt(key, default)
return if (value == default) null else value
}

fun ConfigurationClient.getResolution(key: String): Resolution? {
val parts = this.getString(key, "0").split('x')
if (parts.size != 2) return null
return try {
val dimens = parts.map { it.toInt() }
Resolution(dimens[0], dimens[1])
} catch (ex: Exception) {
null
}
}

data class Resolution(val width: Int, val height: Int)
8 changes: 8 additions & 0 deletions app/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,14 @@
<item>NOTE</item>
</string-array>

<string-array name="default_resolutions">
<item>Default</item>
</string-array>

<string-array name="default_resolutions_values">
<item>0</item>
</string-array>

<string-array name="sc_scope">
<item>com.snapchat.android</item>
</string-array>
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<string name="title_activity_settings">Settings</string>
<string name="privacy_header">Privacy</string>
<string name="tweaks_header">Tweaks</string>
<string name="camera_header">🧪 Camera</string>
<string name="snaps_header">Snaps</string>
<string name="downloads_header">Downloads</string>
<string name="notifications_header">Notifications</string>
Expand Down Expand Up @@ -158,6 +159,23 @@

<string name="enable_new_chat_menu_title">Enable New Chat Menu</string>
<string name="enable_new_chat_menu_description">Enables the new chat context menu (recommended).</string>

<string name="enable_story_list_title">Enable Story List</string>
<string name="enable_story_list_description">Replaces the default story carousel with a list.</string>

<string name="camera_title">Notice</string>
<string name="camera_description">Tap to read.</string>
<string name="camera_dialog_description">Note, increasing video resolution, FPS and bitrate all have an effect on the media size. This means high quality videos will take up a lot of storage space and will take a long time to upload and download for the recipient.</string>

<string name="custom_image_resolution_title">Image Resolution</string>
<string name="custom_video_resolution_title">Video Resolution</string>

<string name="custom_video_fps_title">Video FPS</string>
<string name="custom_video_fps_default">Set to 0 for default FPS.</string>

<string name="custom_video_bitrate_title">Video Bitrate (bps)</string>
<string name="custom_video_bitrate_default">Set to 0 for default bitrate. Higher bitrate leads to higher quality but also larger file sizes. For example, 4k videos typically have a bitrate of 60000000 to 100000000 bps.</string>

<string name="force_source_encoding_title">Force Source Encoding</string>
<string name="force_source_encoding_description">Forces Snapchat to use the source resolution and bitrate when saving/sending videos and images. This must be enabled for custom video/image resolution to be noticeable.</string>
</resources>
46 changes: 46 additions & 0 deletions app/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,52 @@
app:iconSpaceReserved="false" />
</PreferenceCategory>

<PreferenceCategory app:title="@string/camera_header"
app:iconSpaceReserved="false">

<Preference
app:key="camera_readme"
app:title="@string/camera_title"
app:summary="@string/camera_description"
app:iconSpaceReserved="false" />

<ListPreference
app:key="custom_image_resolution"
app:title="@string/custom_image_resolution_title"
app:entries="@array/default_resolutions"
app:entryValues="@array/default_resolutions_values"
app:defaultValue="0"
app:iconSpaceReserved="false" />

<ListPreference
app:key="custom_video_resolution"
app:title="@string/custom_video_resolution_title"
app:entries="@array/default_resolutions"
app:entryValues="@array/default_resolutions_values"
app:defaultValue="0"
app:iconSpaceReserved="false" />

<EditTextPreference
app:key="custom_video_fps"
app:title="@string/custom_video_fps_title"
app:dialogMessage="@string/custom_video_fps_default"
app:defaultValue="0"
app:iconSpaceReserved="false" />

<EditTextPreference
app:key="custom_video_bitrate"
app:title="@string/custom_video_bitrate_title"
app:dialogMessage="@string/custom_video_bitrate_default"
app:defaultValue="0"
app:iconSpaceReserved="false" />

<SwitchPreferenceCompat
app:key="force_source_encoding"
app:title="@string/force_source_encoding_title"
app:summary="@string/force_source_encoding_description"
app:iconSpaceReserved="false" />
</PreferenceCategory>

<PreferenceCategory app:title="@string/snaps_header"
app:iconSpaceReserved="false">

Expand Down
Loading