feat: ff menu bar (#59)
fix: amoled dark mode Co-authored-by: rhunk <101876869+rhunk@users.noreply.github.com>
This commit is contained in:
@@ -69,7 +69,10 @@
|
||||
"picture_resolution": "Override Picture Resolution",
|
||||
"force_highest_frame_rate": "Force Highest Frame Rate",
|
||||
"force_camera_source_encoding": "Force Camera Source Encoding",
|
||||
"amoled_dark_mode": "AMOLED Dark Mode"
|
||||
"amoled_dark_mode": "AMOLED Dark Mode",
|
||||
"enable_friend_feed_menu_bar": "Enable New Friend Feed Menu Bar",
|
||||
"friend_feed_menu_buttons": "Friend Feed Menu Buttons",
|
||||
"friend_feed_menu_buttons_position": "Friend Feed Buttons Position Index"
|
||||
},
|
||||
|
||||
"option": {
|
||||
@@ -79,6 +82,12 @@
|
||||
"snap": "Show medias",
|
||||
"reply_button": "Add reply button"
|
||||
},
|
||||
"friend_feed_menu_buttons": {
|
||||
"auto_download_blacklist": "\u2B07\uFE0F Auto Download Blacklist",
|
||||
"anti_auto_save": "\uD83D\uDCAC Anti Auto Save Messages",
|
||||
"stealth_mode": "\uD83D\uDC7B Stealth Mode",
|
||||
"conversation_info": "\uD83D\uDC64 Conversation Info"
|
||||
},
|
||||
"download_options": {
|
||||
"format_user_folder": "Create folder for each user",
|
||||
"format_hash": "Add a unique hash to the file path",
|
||||
@@ -140,7 +149,7 @@
|
||||
"friend_menu_option": {
|
||||
"preview": "Preview",
|
||||
"stealth_mode": "Stealth Mode",
|
||||
"anti_auto_download": "Anti Auto Download",
|
||||
"auto_download_blacklist": "Auto Download Blacklist",
|
||||
"anti_auto_save": "Anti Auto Save"
|
||||
},
|
||||
|
||||
|
||||
@@ -141,12 +141,6 @@ enum class ConfigProperty(
|
||||
ConfigCategory.MEDIA_MANAGEMENT,
|
||||
ConfigStateValue(false)
|
||||
),
|
||||
DOWNLOAD_BLACKLIST(
|
||||
"property.auto_download_blacklist",
|
||||
"description.auto_download_blacklist",
|
||||
ConfigCategory.MEDIA_MANAGEMENT,
|
||||
ConfigStateValue(false)
|
||||
),
|
||||
GALLERY_MEDIA_SEND_OVERRIDE(
|
||||
"property.gallery_media_send_override",
|
||||
"description.gallery_media_send_override",
|
||||
@@ -163,11 +157,6 @@ enum class ConfigProperty(
|
||||
listOf("CHAT", "SNAP", "NOTE", "EXTERNAL_MEDIA", "STICKER")
|
||||
)
|
||||
),
|
||||
ANTI_AUTO_SAVE("property.anti_auto_save",
|
||||
"description.anti_auto_save",
|
||||
ConfigCategory.MEDIA_MANAGEMENT,
|
||||
ConfigStateValue(false)
|
||||
),
|
||||
|
||||
FORCE_MEDIA_SOURCE_QUALITY(
|
||||
"property.force_media_source_quality",
|
||||
@@ -177,6 +166,31 @@ enum class ConfigProperty(
|
||||
),
|
||||
|
||||
//UI AND TWEAKS
|
||||
ENABLE_FRIEND_FEED_MENU_BAR(
|
||||
"property.enable_friend_feed_menu_bar",
|
||||
"description.enable_friend_feed_menu_bar",
|
||||
ConfigCategory.UI_TWEAKS,
|
||||
ConfigStateValue(false)
|
||||
),
|
||||
FRIEND_FEED_MENU_BUTTONS(
|
||||
"property.friend_feed_menu_buttons",
|
||||
"description.friend_feed_menu_buttons",
|
||||
ConfigCategory.UI_TWEAKS,
|
||||
ConfigStateListValue(
|
||||
listOf("auto_download_blacklist", "anti_auto_save", "stealth_mode", "conversation_info"),
|
||||
mutableMapOf(
|
||||
"auto_download_blacklist" to false,
|
||||
"anti_auto_save" to false,
|
||||
"stealth_mode" to true,
|
||||
"conversation_info" to true
|
||||
)
|
||||
)
|
||||
),
|
||||
FRIEND_FEED_MENU_POSITION("property.friend_feed_menu_buttons_position",
|
||||
"description.friend_feed_menu_buttons_position",
|
||||
ConfigCategory.UI_TWEAKS,
|
||||
ConfigIntegerValue(1)
|
||||
),
|
||||
CAMERA_DISABLE(
|
||||
"property.disable_camera",
|
||||
"description.disable_camera",
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.rhunk.snapenhance.data.wrapper.impl
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import me.rhunk.snapenhance.SnapEnhance
|
||||
import me.rhunk.snapenhance.data.wrapper.AbstractWrapper
|
||||
|
||||
class FriendActionButton(
|
||||
obj: View
|
||||
) : AbstractWrapper(obj) {
|
||||
private val iconDrawableContainer by lazy {
|
||||
instanceNonNull().javaClass.declaredFields.first { it.type != Int::class.javaPrimitiveType }[instanceNonNull()]
|
||||
}
|
||||
|
||||
private val setIconDrawableMethod by lazy {
|
||||
iconDrawableContainer.javaClass.declaredMethods.first {
|
||||
it.parameterTypes.size == 1 &&
|
||||
it.parameterTypes[0] == Drawable::class.java &&
|
||||
it.name != "invalidateDrawable" &&
|
||||
it.returnType == Void::class.javaPrimitiveType
|
||||
}
|
||||
}
|
||||
|
||||
fun setIconDrawable(drawable: Drawable) {
|
||||
setIconDrawableMethod.invoke(iconDrawableContainer, drawable)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun new(context: Context): FriendActionButton {
|
||||
val instance = SnapEnhance.classLoader.loadClass("com.snap.profile.shared.view.FriendActionButton")
|
||||
.getConstructor(Context::class.java, AttributeSet::class.java)
|
||||
.newInstance(context, null) as View
|
||||
return FriendActionButton(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,8 @@ class AmoledDarkMode : Feature("Amoled Dark Mode", loadParams = FeatureLoadParam
|
||||
getAttribute("sigColorTextPrimary") -> {
|
||||
ephemeralHook("getColor", 0xFFFFFFFF.toInt())
|
||||
}
|
||||
getAttribute("sigColorBackgroundMain") -> {
|
||||
getAttribute("sigColorBackgroundMain"),
|
||||
getAttribute("sigColorBackgroundSurface") -> {
|
||||
ephemeralHook("getColor", 0xFF000000.toInt())
|
||||
}
|
||||
getAttribute("actionSheetBackgroundDrawable"),
|
||||
|
||||
@@ -3,8 +3,11 @@ package me.rhunk.snapenhance.features.impl.ui.menus
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.StateListDrawable
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.Switch
|
||||
import android.widget.TextView
|
||||
@@ -14,65 +17,77 @@ object ViewAppearanceHelper {
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode", "RtlHardcoded", "DiscouragedApi",
|
||||
"ClickableViewAccessibility"
|
||||
)
|
||||
fun applyTheme(viewModel: View, view: TextView) {
|
||||
val sigColorTextPrimary = viewModel.context.theme.obtainStyledAttributes(
|
||||
intArrayOf(viewModel.resources.getIdentifier("sigColorTextPrimary", "attr", Constants.SNAPCHAT_PACKAGE_NAME))
|
||||
).getColor(0, 0)
|
||||
private var sigColorTextPrimary: Int = 0
|
||||
private var sigColorBackgroundSurface: Int = 0
|
||||
|
||||
val sigColorBackgroundMain = viewModel.context.theme.obtainStyledAttributes(
|
||||
intArrayOf(viewModel.resources.getIdentifier("sigColorBackgroundMain", "attr", Constants.SNAPCHAT_PACKAGE_NAME))
|
||||
).getColor(0, 0)
|
||||
|
||||
val snapchatFontResId = view.context.resources.getIdentifier("avenir_next_medium", "font", "com.snapchat.android")
|
||||
//remove the shadow
|
||||
view.setBackgroundColor(sigColorBackgroundMain)
|
||||
view.setTextColor(sigColorTextPrimary)
|
||||
view.setShadowLayer(0F, 0F, 0F, 0)
|
||||
view.outlineProvider = null
|
||||
view.gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
|
||||
view.width = viewModel.width
|
||||
|
||||
//DPI Calculator
|
||||
val scalingFactor = view.context.resources.displayMetrics.densityDpi.toDouble() / 400
|
||||
view.height = (150 * scalingFactor).toInt()
|
||||
view.setPadding((40 * scalingFactor).toInt(), 0, (40 * scalingFactor).toInt(), 0)
|
||||
view.isAllCaps = false
|
||||
view.textSize = 16f
|
||||
view.typeface = view.context.resources.getFont(snapchatFontResId)
|
||||
|
||||
//FIXME: wrong color, shouldn't be that much noticeable though
|
||||
view.setOnTouchListener { _, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
view.setBackgroundColor(0x5395026)
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
view.setBackgroundColor(sigColorBackgroundMain)
|
||||
}
|
||||
}
|
||||
false
|
||||
private fun createRoundedBackground(color: Int, hasRadius: Boolean): Drawable {
|
||||
if (!hasRadius) return ColorDrawable(color)
|
||||
//FIXME: hardcoded radius
|
||||
return ShapeDrawable().apply {
|
||||
paint.color = color
|
||||
shape = android.graphics.drawable.shapes.RoundRectShape(
|
||||
floatArrayOf(20f, 20f, 20f, 20f, 20f, 20f, 20f, 20f),
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
if (view is Switch) {
|
||||
with(viewModel.resources) {
|
||||
view.switchMinWidth = getDimension(getIdentifier("v11_switch_min_width", "dimen", Constants.SNAPCHAT_PACKAGE_NAME)).toInt()
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi")
|
||||
fun applyTheme(component: View, componentWidth: Int? = null, hasRadius: Boolean = false) {
|
||||
val resources = component.context.resources
|
||||
if (sigColorBackgroundSurface == 0 || sigColorTextPrimary == 0) {
|
||||
with(component.context.theme) {
|
||||
sigColorTextPrimary = obtainStyledAttributes(
|
||||
intArrayOf(resources.getIdentifier("sigColorTextPrimary", "attr", Constants.SNAPCHAT_PACKAGE_NAME))
|
||||
).getColor(0, 0)
|
||||
|
||||
sigColorBackgroundSurface = obtainStyledAttributes(
|
||||
intArrayOf(resources.getIdentifier("sigColorBackgroundSurface", "attr", Constants.SNAPCHAT_PACKAGE_NAME))
|
||||
).getColor(0, 0)
|
||||
}
|
||||
val colorStateList = ColorStateList(
|
||||
}
|
||||
|
||||
val snapchatFontResId = resources.getIdentifier("avenir_next_medium", "font", "com.snapchat.android")
|
||||
val scalingFactor = resources.displayMetrics.densityDpi.toDouble() / 400
|
||||
|
||||
with(component) {
|
||||
if (this is TextView) {
|
||||
setTextColor(sigColorTextPrimary)
|
||||
setShadowLayer(0F, 0F, 0F, 0)
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
componentWidth?.let { width = it}
|
||||
height = (150 * scalingFactor).toInt()
|
||||
isAllCaps = false
|
||||
textSize = 16f
|
||||
typeface = resources.getFont(snapchatFontResId)
|
||||
outlineProvider = null
|
||||
setPadding((40 * scalingFactor).toInt(), 0, (40 * scalingFactor).toInt(), 0)
|
||||
}
|
||||
background = StateListDrawable().apply {
|
||||
addState(intArrayOf(), createRoundedBackground(color = sigColorBackgroundSurface, hasRadius))
|
||||
addState(intArrayOf(android.R.attr.state_pressed), createRoundedBackground(color = 0x5395026, hasRadius))
|
||||
}
|
||||
}
|
||||
|
||||
if (component is Switch) {
|
||||
with(resources) {
|
||||
component.switchMinWidth = getDimension(getIdentifier("v11_switch_min_width", "dimen", Constants.SNAPCHAT_PACKAGE_NAME)).toInt()
|
||||
}
|
||||
component.trackTintList = ColorStateList(
|
||||
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)
|
||||
), intArrayOf(
|
||||
Color.parseColor("#1d1d1d"),
|
||||
Color.parseColor("#26bd49")
|
||||
)
|
||||
)
|
||||
val thumbStateList = ColorStateList(
|
||||
component.thumbTintList = ColorStateList(
|
||||
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)
|
||||
), intArrayOf(
|
||||
Color.parseColor("#F5F5F5"),
|
||||
Color.parseColor("#26bd49")
|
||||
)
|
||||
)
|
||||
view.trackTintList = colorStateList
|
||||
view.thumbTintList = thumbStateList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package me.rhunk.snapenhance.features.impl.ui.menus.impl
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.SystemClock
|
||||
import android.util.TypedValue
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -15,8 +11,8 @@ import me.rhunk.snapenhance.Constants.VIEW_INJECTED_CODE
|
||||
import me.rhunk.snapenhance.config.ConfigProperty
|
||||
import me.rhunk.snapenhance.features.impl.Messaging
|
||||
import me.rhunk.snapenhance.features.impl.downloader.MediaDownloader
|
||||
import me.rhunk.snapenhance.features.impl.spying.MessageLogger
|
||||
import me.rhunk.snapenhance.features.impl.ui.menus.AbstractMenu
|
||||
import me.rhunk.snapenhance.features.impl.ui.menus.ViewAppearanceHelper
|
||||
|
||||
|
||||
class ChatActionMenu : AbstractMenu() {
|
||||
@@ -26,27 +22,6 @@ class ChatActionMenu : AbstractMenu() {
|
||||
return false
|
||||
}
|
||||
|
||||
private fun applyButtonTheme(parent: View, button: Button) {
|
||||
button.background = ColorDrawable(Color.WHITE)
|
||||
button.setTextColor(Color.BLACK)
|
||||
button.transformationMethod = null
|
||||
val margin = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
20f,
|
||||
Resources.getSystem().displayMetrics
|
||||
).toInt()
|
||||
val params = MarginLayoutParams(parent.layoutParams)
|
||||
params.setMargins(margin, 5, margin, 5)
|
||||
params.marginEnd = margin
|
||||
params.marginStart = margin
|
||||
button.layoutParams = params
|
||||
button.height = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
50f,
|
||||
Resources.getSystem().displayMetrics
|
||||
).toInt()
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun inject(viewGroup: ViewGroup) {
|
||||
val parent = viewGroup.parent.parent as ViewGroup
|
||||
@@ -64,9 +39,23 @@ class ChatActionMenu : AbstractMenu() {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val injectButton = { button: Button ->
|
||||
ViewAppearanceHelper.applyTheme(button, parent.width, hasRadius = true)
|
||||
|
||||
with(button) {
|
||||
layoutParams = MarginLayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
).apply {
|
||||
setMargins(40, 0, 40, 15)
|
||||
}
|
||||
parent.addView(this)
|
||||
}
|
||||
}
|
||||
|
||||
if (context.config.bool(ConfigProperty.CHAT_DOWNLOAD_CONTEXT_MENU)) {
|
||||
parent.addView(Button(viewGroup.context).apply {
|
||||
applyButtonTheme(parent, this)
|
||||
injectButton(Button(viewGroup.context).apply {
|
||||
text = this@ChatActionMenu.context.translation.get("chat_action_menu.preview_button")
|
||||
setOnClickListener {
|
||||
closeActionMenu()
|
||||
@@ -74,8 +63,7 @@ class ChatActionMenu : AbstractMenu() {
|
||||
}
|
||||
})
|
||||
|
||||
parent.addView(Button(viewGroup.context).apply {
|
||||
applyButtonTheme(parent, this)
|
||||
injectButton(Button(viewGroup.context).apply {
|
||||
text = this@ChatActionMenu.context.translation.get("chat_action_menu.download_button")
|
||||
setOnClickListener {
|
||||
closeActionMenu()
|
||||
@@ -90,18 +78,17 @@ class ChatActionMenu : AbstractMenu() {
|
||||
|
||||
//delete logged message button
|
||||
if (context.config.bool(ConfigProperty.MESSAGE_LOGGER)) {
|
||||
val downloadButton = Button(viewGroup.context)
|
||||
applyButtonTheme(parent, downloadButton)
|
||||
downloadButton.text = context.translation.get("chat_action_menu.delete_logged_message_button")
|
||||
downloadButton.setOnClickListener {
|
||||
closeActionMenu()
|
||||
context.executeAsync {
|
||||
with(context.feature(Messaging::class)) {
|
||||
context.feature(MessageLogger::class).deleteMessage(lastOpenedConversationUUID.toString(), lastFocusedMessageId)
|
||||
injectButton(Button(viewGroup.context).apply {
|
||||
text = this@ChatActionMenu.context.translation.get("chat_action_menu.delete_logged_message_button")
|
||||
setOnClickListener {
|
||||
closeActionMenu()
|
||||
this@ChatActionMenu.context.executeAsync {
|
||||
with(this@ChatActionMenu.context.feature(Messaging::class)) {
|
||||
context.feature(me.rhunk.snapenhance.features.impl.spying.MessageLogger::class).deleteMessage(lastOpenedConversationUUID.toString(), lastFocusedMessageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
parent.addView(downloadButton)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,26 +5,35 @@ import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Switch
|
||||
import android.widget.Toast
|
||||
import me.rhunk.snapenhance.Logger
|
||||
import me.rhunk.snapenhance.config.ConfigProperty
|
||||
import me.rhunk.snapenhance.data.ContentType
|
||||
import me.rhunk.snapenhance.data.wrapper.impl.FriendActionButton
|
||||
import me.rhunk.snapenhance.database.objects.ConversationMessage
|
||||
import me.rhunk.snapenhance.database.objects.FriendInfo
|
||||
import me.rhunk.snapenhance.database.objects.UserConversationLink
|
||||
import me.rhunk.snapenhance.features.impl.Messaging
|
||||
import me.rhunk.snapenhance.features.impl.downloader.AntiAutoDownload
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.AntiAutoSave
|
||||
import me.rhunk.snapenhance.features.impl.spying.StealthMode
|
||||
import me.rhunk.snapenhance.features.impl.tweaks.AntiAutoSave
|
||||
import me.rhunk.snapenhance.features.impl.ui.menus.AbstractMenu
|
||||
import me.rhunk.snapenhance.features.impl.ui.menus.ViewAppearanceHelper.applyTheme
|
||||
import me.rhunk.snapenhance.features.impl.ui.menus.ViewAppearanceHelper
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.text.DateFormat
|
||||
@@ -164,77 +173,203 @@ class FriendFeedInfoMenu : AbstractMenu() {
|
||||
builder.show()
|
||||
}
|
||||
|
||||
private fun createToggleFeature(viewModel: View, viewConsumer: ((View) -> Unit), text: String, isChecked: () -> Boolean, toggle: (Boolean) -> Unit) {
|
||||
val switch = Switch(viewModel.context)
|
||||
private fun createEmojiDrawable(text: String, width: Int, height: Int, textSize: Float, disabled: Boolean = false): Drawable {
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
val paint = Paint()
|
||||
paint.textSize = textSize
|
||||
paint.color = Color.BLACK
|
||||
paint.textAlign = Paint.Align.CENTER
|
||||
canvas.drawText(text, width / 2f, height.toFloat() - paint.descent(), paint)
|
||||
if (disabled) {
|
||||
paint.color = Color.RED
|
||||
paint.strokeWidth = 5f
|
||||
canvas.drawLine(0f, 0f, width.toFloat(), height.toFloat(), paint)
|
||||
}
|
||||
return BitmapDrawable(context.resources, bitmap)
|
||||
}
|
||||
|
||||
private fun getCurrentConversationId(): Pair<String, String?> {
|
||||
val messaging = context.feature(Messaging::class)
|
||||
var focusedConversationTargetUser: String? = null
|
||||
val conversationId = if (messaging.lastFetchConversationUUID == null) {
|
||||
focusedConversationTargetUser = messaging.lastFetchConversationUserUUID.toString()
|
||||
val conversation: UserConversationLink = context.database.getDMConversationIdFromUserId(focusedConversationTargetUser) ?: throw IllegalStateException("No conversation found")
|
||||
conversation.client_conversation_id!!.trim().lowercase()
|
||||
} else {
|
||||
messaging.lastFetchConversationUUID.toString()
|
||||
}
|
||||
|
||||
return Pair(conversationId, focusedConversationTargetUser)
|
||||
}
|
||||
|
||||
private fun createToggleFeature(viewConsumer: ((View) -> Unit), text: String, isChecked: () -> Boolean, toggle: (Boolean) -> Unit) {
|
||||
val switch = Switch(context.androidContext)
|
||||
switch.text = context.translation.get(text)
|
||||
switch.isChecked = isChecked()
|
||||
applyTheme(viewModel, switch)
|
||||
ViewAppearanceHelper.applyTheme(switch)
|
||||
switch.setOnCheckedChangeListener { _: CompoundButton?, checked: Boolean ->
|
||||
toggle(checked)
|
||||
}
|
||||
viewConsumer(switch)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n", "UseSwitchCompatOrMaterialCode", "DefaultLocale")
|
||||
fun injectOldButtons(viewModel: View, viewConsumer: ((View) -> Unit)) {
|
||||
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n", "UseSwitchCompatOrMaterialCode", "DefaultLocale", "InflateParams",
|
||||
"DiscouragedApi", "ClickableViewAccessibility"
|
||||
)
|
||||
fun inject(viewModel: View, viewConsumer: ((View) -> Unit)) {
|
||||
val messaging = context.feature(Messaging::class)
|
||||
var focusedConversationTargetUser: String? = null
|
||||
val conversationId: String
|
||||
if (messaging.lastFetchConversationUserUUID != null) {
|
||||
focusedConversationTargetUser = messaging.lastFetchConversationUserUUID.toString()
|
||||
val conversation: UserConversationLink = context.database.getDMConversationIdFromUserId(focusedConversationTargetUser) ?: return
|
||||
conversationId = conversation.client_conversation_id!!.trim().lowercase()
|
||||
} else {
|
||||
conversationId = messaging.lastFetchConversationUUID.toString()
|
||||
}
|
||||
val modContext = context
|
||||
|
||||
//preview button
|
||||
val previewButton = Button(viewModel.context)
|
||||
previewButton.text = context.translation.get("friend_menu_option.preview")
|
||||
applyTheme(viewModel, previewButton)
|
||||
val finalFocusedConversationTargetUser = focusedConversationTargetUser
|
||||
previewButton.setOnClickListener {
|
||||
showPreview(
|
||||
finalFocusedConversationTargetUser,
|
||||
conversationId,
|
||||
previewButton.context
|
||||
)
|
||||
}
|
||||
val friendFeedMenuOptions = context.config.options(ConfigProperty.FRIEND_FEED_MENU_BUTTONS)
|
||||
if (friendFeedMenuOptions.none { it.value }) return
|
||||
|
||||
//stealth switch
|
||||
val stealthSwitch = Switch(viewModel.context)
|
||||
stealthSwitch.text = context.translation.get("friend_menu_option.stealth_mode")
|
||||
stealthSwitch.isChecked = context.feature(StealthMode::class).isStealth(conversationId)
|
||||
applyTheme(viewModel, stealthSwitch)
|
||||
stealthSwitch.setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
context.feature(StealthMode::class).setStealth(
|
||||
conversationId,
|
||||
isChecked
|
||||
)
|
||||
}
|
||||
val (conversationId, focusedConversationTargetUser) = getCurrentConversationId()
|
||||
|
||||
run {
|
||||
val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run
|
||||
if (context.config.bool(ConfigProperty.DOWNLOAD_BLACKLIST)) {
|
||||
createToggleFeature(viewModel,
|
||||
viewConsumer,
|
||||
"friend_menu_option.anti_auto_download",
|
||||
{ context.feature(AntiAutoDownload::class).isUserIgnored(userId) },
|
||||
{ context.feature(AntiAutoDownload::class).setUserIgnored(userId, it) }
|
||||
)
|
||||
if (!context.config.bool(ConfigProperty.ENABLE_FRIEND_FEED_MENU_BAR)) {
|
||||
//preview button
|
||||
val previewButton = Button(viewModel.context).apply {
|
||||
text = modContext.translation.get("friend_menu_option.preview")
|
||||
ViewAppearanceHelper.applyTheme(this, viewModel.width)
|
||||
setOnClickListener {
|
||||
showPreview(
|
||||
focusedConversationTargetUser,
|
||||
conversationId,
|
||||
context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (context.config.bool(ConfigProperty.ANTI_AUTO_SAVE)) {
|
||||
createToggleFeature(viewModel,
|
||||
viewConsumer,
|
||||
"friend_menu_option.anti_auto_save",
|
||||
{ context.feature(AntiAutoSave::class).isConversationIgnored(conversationId) },
|
||||
{ context.feature(AntiAutoSave::class).setConversationIgnored(conversationId, it) }
|
||||
//stealth switch
|
||||
val stealthSwitch = Switch(viewModel.context).apply {
|
||||
text = modContext.translation.get("friend_menu_option.stealth_mode")
|
||||
isChecked = modContext.feature(StealthMode::class).isStealth(conversationId)
|
||||
ViewAppearanceHelper.applyTheme(this)
|
||||
setOnCheckedChangeListener { _: CompoundButton?, isChecked: Boolean ->
|
||||
modContext.feature(StealthMode::class).setStealth(
|
||||
conversationId,
|
||||
isChecked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
run {
|
||||
val userId = context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId ?: return@run
|
||||
if (friendFeedMenuOptions["auto_download_blacklist"] == true) {
|
||||
createToggleFeature(viewConsumer,
|
||||
"friend_menu_option.auto_download_blacklist",
|
||||
{ context.feature(AntiAutoDownload::class).isUserIgnored(userId) },
|
||||
{ context.feature(AntiAutoDownload::class).setUserIgnored(userId, it) }
|
||||
)
|
||||
}
|
||||
|
||||
if (friendFeedMenuOptions["anti_auto_save"] == true) {
|
||||
createToggleFeature(viewConsumer,
|
||||
"friend_menu_option.anti_auto_save",
|
||||
{ context.feature(AntiAutoSave::class).isConversationIgnored(conversationId) },
|
||||
{ context.feature(AntiAutoSave::class).setConversationIgnored(conversationId, it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
viewConsumer(stealthSwitch)
|
||||
viewConsumer(previewButton)
|
||||
return
|
||||
}
|
||||
|
||||
val menuButtonBar = LinearLayout(viewModel.context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
gravity = Gravity.CENTER
|
||||
}
|
||||
|
||||
fun createActionButton(icon: String, isDisabled: Boolean? = null, onClick: (Boolean) -> Unit) {
|
||||
//FIXME: hardcoded values
|
||||
menuButtonBar.addView(LinearLayout(viewModel.context).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f)
|
||||
gravity = Gravity.CENTER
|
||||
setPadding(0, 20, 0, 20)
|
||||
isClickable = true
|
||||
scaleX = 1.1f
|
||||
scaleY = 1.1f
|
||||
|
||||
var isLineThrough = isDisabled ?: false
|
||||
FriendActionButton.new(viewModel.context).apply {
|
||||
fun setLineThrough(value: Boolean) {
|
||||
setIconDrawable(createEmojiDrawable(icon, 60, 60, 50f, if (isDisabled == null) false else value))
|
||||
}
|
||||
setLineThrough(isLineThrough)
|
||||
(instanceNonNull() as View).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
|
||||
setMargins(0, 60, 0, 60)
|
||||
}
|
||||
setOnTouchListener { _, event ->
|
||||
if (event.action == MotionEvent.ACTION_UP) {
|
||||
isLineThrough = !isLineThrough
|
||||
onClick(isLineThrough)
|
||||
setLineThrough(isLineThrough)
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
}.also { addView(it.instanceNonNull() as View) }
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
if (friendFeedMenuOptions["auto_download_blacklist"] == true) {
|
||||
run {
|
||||
val userId =
|
||||
context.database.getFriendFeedInfoByConversationId(conversationId)?.friendUserId
|
||||
?: return@run
|
||||
createActionButton(
|
||||
"\u2B07\uFE0F",
|
||||
isDisabled = !context.feature(AntiAutoDownload::class).isUserIgnored(userId)
|
||||
) {
|
||||
context.feature(AntiAutoDownload::class).setUserIgnored(userId, !it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (friendFeedMenuOptions["anti_auto_save"] == true) {
|
||||
//diskette
|
||||
createActionButton("\uD83D\uDCAC",
|
||||
isDisabled = !context.feature(AntiAutoSave::class)
|
||||
.isConversationIgnored(conversationId)
|
||||
) {
|
||||
context.feature(AntiAutoSave::class).setConversationIgnored(conversationId, !it)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (friendFeedMenuOptions["stealth_mode"] == true) {
|
||||
//eyes
|
||||
createActionButton(
|
||||
"\uD83D\uDC7B",
|
||||
isDisabled = !context.feature(StealthMode::class).isStealth(conversationId)
|
||||
) { isChecked ->
|
||||
context.feature(StealthMode::class).setStealth(
|
||||
conversationId,
|
||||
!isChecked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
viewConsumer(stealthSwitch)
|
||||
viewConsumer(previewButton)
|
||||
if (friendFeedMenuOptions["conversation_info"] == true) {
|
||||
//user
|
||||
createActionButton("\uD83D\uDC64") {
|
||||
showPreview(
|
||||
focusedConversationTargetUser,
|
||||
conversationId,
|
||||
viewModel.context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
viewConsumer(menuButtonBar)
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class OperaContextActionMenu : AbstractMenu() {
|
||||
val button = Button(childView.getContext())
|
||||
button.text = context.translation.get("opera_context_menu.download")
|
||||
button.setOnClickListener { context.feature(MediaDownloader::class).downloadLastOperaMediaAsync() }
|
||||
applyTheme(linearLayout, button)
|
||||
applyTheme(button)
|
||||
linearLayout.addView(button)
|
||||
(childView as ViewGroup).addView(linearLayout, 0)
|
||||
} catch (e: Throwable) {
|
||||
|
||||
@@ -24,10 +24,10 @@ import me.rhunk.snapenhance.features.impl.ui.menus.ViewAppearanceHelper
|
||||
|
||||
class SettingsMenu : AbstractMenu() {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun createCategoryTitle(viewModel: View, key: String): TextView {
|
||||
val categoryText = TextView(viewModel.context)
|
||||
private fun createCategoryTitle(key: String): TextView {
|
||||
val categoryText = TextView(context.androidContext)
|
||||
categoryText.text = context.translation.get(key)
|
||||
ViewAppearanceHelper.applyTheme(viewModel, categoryText)
|
||||
ViewAppearanceHelper.applyTheme(categoryText)
|
||||
categoryText.textSize = 20f
|
||||
categoryText.typeface = categoryText.typeface?.let { Typeface.create(it, Typeface.BOLD) }
|
||||
categoryText.setOnTouchListener { _, _ -> true }
|
||||
@@ -35,7 +35,7 @@ class SettingsMenu : AbstractMenu() {
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun createPropertyView(viewModel: View, property: ConfigProperty): View {
|
||||
private fun createPropertyView(property: ConfigProperty): View {
|
||||
val propertyName = context.translation.get(property.nameKey)
|
||||
val updateButtonText: (TextView, String) -> Unit = { textView, text ->
|
||||
textView.text = "$propertyName${if (text.isEmpty()) "" else ": $text"}"
|
||||
@@ -57,10 +57,10 @@ class SettingsMenu : AbstractMenu() {
|
||||
}
|
||||
|
||||
val textEditor: ((String) -> Unit) -> Unit = { updateValue ->
|
||||
val builder = AlertDialog.Builder(viewModel.context)
|
||||
val builder = AlertDialog.Builder(context.mainActivity!!)
|
||||
builder.setTitle(propertyName)
|
||||
|
||||
val input = EditText(viewModel.context)
|
||||
val input = EditText(context.androidContext)
|
||||
input.inputType = InputType.TYPE_CLASS_TEXT
|
||||
input.setText(property.valueContainer.value().toString())
|
||||
|
||||
@@ -75,12 +75,12 @@ class SettingsMenu : AbstractMenu() {
|
||||
|
||||
val resultView: View = when (property.valueContainer) {
|
||||
is ConfigStringValue -> {
|
||||
val textView = TextView(viewModel.context)
|
||||
val textView = TextView(context.androidContext)
|
||||
updateButtonText(textView, property.valueContainer.let {
|
||||
if (it.isHidden) it.hiddenValue()
|
||||
else it.value()
|
||||
})
|
||||
ViewAppearanceHelper.applyTheme(viewModel, textView)
|
||||
ViewAppearanceHelper.applyTheme(textView)
|
||||
textView.setOnClickListener {
|
||||
textEditor { value ->
|
||||
property.valueContainer.writeFrom(value)
|
||||
@@ -93,7 +93,7 @@ class SettingsMenu : AbstractMenu() {
|
||||
textView
|
||||
}
|
||||
is ConfigIntegerValue -> {
|
||||
val button = Button(viewModel.context)
|
||||
val button = Button(context.androidContext)
|
||||
updateButtonText(button, property.valueContainer.value().toString())
|
||||
button.setOnClickListener {
|
||||
textEditor { value ->
|
||||
@@ -105,25 +105,25 @@ class SettingsMenu : AbstractMenu() {
|
||||
}
|
||||
}
|
||||
}
|
||||
ViewAppearanceHelper.applyTheme(viewModel, button)
|
||||
ViewAppearanceHelper.applyTheme(button)
|
||||
button
|
||||
}
|
||||
is ConfigStateValue -> {
|
||||
val switch = Switch(viewModel.context)
|
||||
val switch = Switch(context.androidContext)
|
||||
switch.text = propertyName
|
||||
switch.isChecked = property.valueContainer.value()
|
||||
switch.setOnCheckedChangeListener { _, isChecked ->
|
||||
property.valueContainer.writeFrom(isChecked.toString())
|
||||
}
|
||||
ViewAppearanceHelper.applyTheme(viewModel, switch)
|
||||
ViewAppearanceHelper.applyTheme(switch)
|
||||
switch
|
||||
}
|
||||
is ConfigStateSelection -> {
|
||||
val button = Button(viewModel.context)
|
||||
val button = Button(context.androidContext)
|
||||
updateLocalizedText(button, property.valueContainer.value())
|
||||
|
||||
button.setOnClickListener {_ ->
|
||||
val builder = AlertDialog.Builder(viewModel.context)
|
||||
val builder = AlertDialog.Builder(context.mainActivity!!)
|
||||
builder.setTitle(propertyName)
|
||||
|
||||
builder.setSingleChoiceItems(
|
||||
@@ -142,15 +142,15 @@ class SettingsMenu : AbstractMenu() {
|
||||
|
||||
builder.show()
|
||||
}
|
||||
ViewAppearanceHelper.applyTheme(viewModel, button)
|
||||
ViewAppearanceHelper.applyTheme(button)
|
||||
button
|
||||
}
|
||||
is ConfigStateListValue -> {
|
||||
val button = Button(viewModel.context)
|
||||
val button = Button(context.androidContext)
|
||||
updateButtonText(button, "(${property.valueContainer.value().count { it.value }})")
|
||||
|
||||
button.setOnClickListener {_ ->
|
||||
val builder = AlertDialog.Builder(viewModel.context)
|
||||
val builder = AlertDialog.Builder(context.mainActivity!!)
|
||||
builder.setTitle(propertyName)
|
||||
|
||||
val sortedStates = property.valueContainer.value().toSortedMap()
|
||||
@@ -173,11 +173,11 @@ class SettingsMenu : AbstractMenu() {
|
||||
|
||||
builder.show()
|
||||
}
|
||||
ViewAppearanceHelper.applyTheme(viewModel, button)
|
||||
ViewAppearanceHelper.applyTheme(button)
|
||||
button
|
||||
}
|
||||
else -> {
|
||||
TextView(viewModel.context)
|
||||
TextView(context.androidContext)
|
||||
}
|
||||
}
|
||||
return resultView
|
||||
@@ -203,7 +203,7 @@ class SettingsMenu : AbstractMenu() {
|
||||
}
|
||||
val titleText = TextView(viewModel.context)
|
||||
titleText.text = versionTextBuilder.toString()
|
||||
ViewAppearanceHelper.applyTheme(viewModel, titleText)
|
||||
ViewAppearanceHelper.applyTheme(titleText)
|
||||
titleText.textSize = 18f
|
||||
titleText.minHeight = 80 * versionTextBuilder.chars().filter { ch: Int -> ch == '\n'.code }
|
||||
.count().coerceAtLeast(2).toInt()
|
||||
@@ -216,7 +216,7 @@ class SettingsMenu : AbstractMenu() {
|
||||
button.setOnClickListener { _ ->
|
||||
it.run()
|
||||
}
|
||||
ViewAppearanceHelper.applyTheme(viewModel, button)
|
||||
ViewAppearanceHelper.applyTheme(button)
|
||||
button
|
||||
}
|
||||
}
|
||||
@@ -224,9 +224,9 @@ class SettingsMenu : AbstractMenu() {
|
||||
context.config.entries().groupBy {
|
||||
it.key.category
|
||||
}.forEach { (category, value) ->
|
||||
addView(createCategoryTitle(viewModel, category.key))
|
||||
addView(createCategoryTitle(category.key))
|
||||
value.filter { it.key.shouldAppearInSettings }.forEach { (property, _) ->
|
||||
addView(createPropertyView(viewModel, property))
|
||||
addView(createPropertyView(property))
|
||||
actions.find { pair -> pair.first.dependsOnProperty == property}?.let { pair ->
|
||||
addView(pair.second())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user