last commit before migration from GitHub
This commit is contained in:
@@ -100,7 +100,7 @@ class ModContext(
|
||||
runCatching {
|
||||
runnable()
|
||||
}.onFailure {
|
||||
longToast("Async task failed: " + it.message)
|
||||
longToast(translation.format("toast_async_task_failed", "message" to (it.message ?: "")))
|
||||
log.error("Async task failed", it)
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ class ModContext(
|
||||
|
||||
fun logCritical(message: Any?, throwable: Throwable = Throwable()) {
|
||||
log.error(message ?: "Snapchat crash", throwable)
|
||||
longToast(message ?: "Snapchat has crashed! Please check logs for more details.")
|
||||
longToast(message ?: translation["toast_snapchat_crashed"])
|
||||
}
|
||||
|
||||
private fun delayForceCloseApp(delay: Long) = Handler(Looper.getMainLooper()).postDelayed({
|
||||
|
||||
@@ -198,7 +198,7 @@ class PurrfectSnap {
|
||||
log.verbose("Features initialized successfully")
|
||||
}.onFailure { throwable ->
|
||||
log.error("Failed to initialize features", throwable)
|
||||
longToast("Failed to initialize features! Some functionality may not work properly.")
|
||||
longToast(appContext.translation["toast_init_features_failed"])
|
||||
// Continue with other initializations even if features fail
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ class PurrfectSnap {
|
||||
log.verbose("Script runtime initialized successfully")
|
||||
}.onFailure { throwable ->
|
||||
log.error("Failed to initialize script runtime", throwable)
|
||||
longToast("Failed to initialize script runtime!")
|
||||
longToast(appContext.translation["toast_init_script_runtime_failed"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,13 +299,15 @@ class ExportMemories : AbstractAction() {
|
||||
val exportedPath = runCatching { outputTarget.finalize(outputZip) }
|
||||
.getOrElse { error ->
|
||||
context.log.error("Failed to finalize memories export", error)
|
||||
context.longToast("Failed to export memories")
|
||||
context.longToast(context.translation["toast_export_memories_failed"])
|
||||
return
|
||||
}
|
||||
if (outputZip.parentFile == context.androidContext.cacheDir) {
|
||||
outputZip.delete()
|
||||
}
|
||||
context.longToast("Exported to $exportedPath")
|
||||
context.longToast(
|
||||
context.translation.format("toast_exported_to_path", "path" to exportedPath)
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -863,7 +865,7 @@ class ExportMemories : AbstractAction() {
|
||||
}.getOrNull()
|
||||
|
||||
if (database == null) {
|
||||
context.longToast("Failed to open memories database")
|
||||
context.longToast(context.translation["toast_open_memories_db_failed"])
|
||||
return@launch
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ class ManageFriendList : AbstractAction() {
|
||||
private fun addFriend(userId: String) {
|
||||
val friendRelationshipChangerInstance = context.feature(AddFriendSourceSpoof::class).friendRelationshipChangerInstance
|
||||
?: run {
|
||||
context.longToast("Failed to add friend: FriendRelationshipChanger instance not available")
|
||||
context.longToast(context.translation["toast_friend_add_unavailable"])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -153,7 +153,9 @@ class ManageFriendList : AbstractAction() {
|
||||
}
|
||||
}.onFailure {
|
||||
context.log.error("Failed to add friend $userId", it)
|
||||
context.longToast("Failed to add friend: ${it.message}")
|
||||
context.longToast(
|
||||
context.translation.format("toast_friend_add_failed", "message" to (it.message ?: ""))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,7 +175,9 @@ class ManageFriendList : AbstractAction() {
|
||||
context.androidContext.contentResolver.openOutputStream(data)?.bufferedWriter()?.use { writer ->
|
||||
userIds.forEach { writer.write(it); writer.newLine() }
|
||||
}
|
||||
context.longToast("Exported ${userIds.size} friends!")
|
||||
context.longToast(
|
||||
context.translation.format("toast_friends_exported", "count" to userIds.size.toString())
|
||||
)
|
||||
}
|
||||
context.mainActivity?.startActivityForResult(
|
||||
Intent.createChooser(Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
@@ -303,7 +307,12 @@ class ManageFriendList : AbstractAction() {
|
||||
fetchedFriends = context.androidContext.contentResolver.openInputStream(data)?.bufferedReader()?.readLines()?.filter { it.matches(uuidRegex) }?.map { it.trim() }?.toMutableList() ?: mutableListOf()
|
||||
}.onFailure {
|
||||
context.log.error("Failed to import friends", it)
|
||||
context.longToast("Failed to import friends: ${it.message}")
|
||||
context.longToast(
|
||||
context.translation.format(
|
||||
"toast_friends_import_failed",
|
||||
"message" to (it.message ?: "")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
context.mainActivity?.startActivityForResult(Intent.createChooser(Intent(Intent.ACTION_GET_CONTENT).apply { type = "*/*" }, "Select a file"), pendingPickerAction!!.first)
|
||||
|
||||
@@ -99,7 +99,12 @@ class DatabaseAccess(
|
||||
context.log.error("Failed to execute query $query", it)
|
||||
return@onFailure
|
||||
}
|
||||
context.longToast("Database ${this.path} is corrupted! Restarting ...")
|
||||
context.longToast(
|
||||
context.translation.format(
|
||||
"toast_database_corrupted",
|
||||
"path" to this.path
|
||||
)
|
||||
)
|
||||
context.androidContext.deleteDatabase(this.path)
|
||||
context.crash("Database ${this.path} is corrupted!", it)
|
||||
}.getOrNull()
|
||||
@@ -616,4 +621,4 @@ class DatabaseAccess(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,7 +164,12 @@ class FeatureManager(
|
||||
}
|
||||
}.onFailure {
|
||||
context.log.error("Failed to init feature ${feature.key}", it)
|
||||
context.longToast("Failed to init feature ${feature.key}! Check logcat for more details.")
|
||||
context.longToast(
|
||||
context.translation.format(
|
||||
"toast_feature_init_failed",
|
||||
"feature" to feature.key
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ import java.util.zip.ZipOutputStream
|
||||
import kotlin.random.Random
|
||||
|
||||
class AccountSwitcher: Feature("Account Switcher") {
|
||||
private val translation by lazy { context.translation.getCategory("account_switcher_ui") }
|
||||
private var exportCallback: Pair<Int, String>? = null // requestCode -> userId
|
||||
private var importRequestCode: Int? = null
|
||||
|
||||
@@ -107,7 +108,9 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
onClick = {
|
||||
runCatching {
|
||||
if (!isLoginActivity && context.database.myUserId == user.first) {
|
||||
context.shortToast("Already logged in as ${user.second}")
|
||||
context.shortToast(
|
||||
translation.format("already_logged_in", "username" to user.second)
|
||||
)
|
||||
return@runCatching
|
||||
}
|
||||
|
||||
@@ -117,7 +120,7 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
|
||||
login(userId = user.first, username = user.second)
|
||||
}.onFailure {
|
||||
context.shortToast("Failed to login. Check logs for more info.")
|
||||
context.shortToast(translation["login_failed_toast"])
|
||||
context.log.error("Failed to login", it)
|
||||
}
|
||||
}
|
||||
@@ -259,7 +262,7 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
|
||||
private fun logout() {
|
||||
context.androidContext.dataDir.resolve( "shared_prefs/user_session_shared_pref.xml").takeIf { it.exists() }?.delete()
|
||||
context.shortToast("Logged out")
|
||||
context.shortToast(translation["logged_out_toast"])
|
||||
context.softRestartApp()
|
||||
}
|
||||
|
||||
@@ -268,7 +271,7 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
ParcelFileDescriptor.AutoCloseInputStream(pfd).use { it.readBytes() }
|
||||
}
|
||||
if (accountData == null) {
|
||||
context.shortToast("Account data not found")
|
||||
context.shortToast(translation["data_not_found_toast"])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -312,12 +315,12 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
zipInputStream.close()
|
||||
} catch (e: Exception) {
|
||||
context.log.error("Failed to restore account data", e)
|
||||
context.shortToast("Failed to restore account data")
|
||||
context.shortToast(translation["restore_failed_toast"])
|
||||
return
|
||||
}
|
||||
|
||||
context.log.debug("Account data restored")
|
||||
context.shortToast("Logged in as $username")
|
||||
context.shortToast(translation.format("logged_in_as_toast", "username" to username))
|
||||
context.softRestartApp()
|
||||
}
|
||||
|
||||
@@ -390,9 +393,9 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
context.database.getFriendInfo(context.database.myUserId)?.mutableUsername ?: "Unknown username",
|
||||
getCurrentAccountData()
|
||||
)
|
||||
context.shortToast("Account backed up!")
|
||||
context.shortToast(translation["backup_success_toast"])
|
||||
}.onFailure {
|
||||
context.shortToast("Failed to backup account. Check logs for more info.")
|
||||
context.shortToast(translation["backup_failure_toast"])
|
||||
context.log.error("Failed to backup account", it)
|
||||
}
|
||||
}
|
||||
@@ -465,11 +468,13 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
it.toParcelFileDescriptor(context.coroutineScope)
|
||||
)
|
||||
}
|
||||
context.shortToast("Imported $username!")
|
||||
context.shortToast(translation.format("import_success_toast", "username" to username))
|
||||
updateUsers()
|
||||
}
|
||||
}.onFailure {
|
||||
context.shortToast("Failed to import account: ${it.message}")
|
||||
context.shortToast(
|
||||
translation.format("import_failure_toast", "message" to (it.message ?: ""))
|
||||
)
|
||||
context.log.error("Failed to import account", it)
|
||||
}
|
||||
|
||||
@@ -522,10 +527,10 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
it.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
context.shortToast("Account exported!")
|
||||
context.shortToast(translation["export_success_toast"])
|
||||
}
|
||||
}.onFailure {
|
||||
context.shortToast("Failed to export account. Check logs for more info.")
|
||||
context.shortToast(translation["export_failed_toast"])
|
||||
context.log.error("Failed to export account", it)
|
||||
}
|
||||
}
|
||||
@@ -539,10 +544,10 @@ class AccountSwitcher: Feature("Account Switcher") {
|
||||
runCatching {
|
||||
val accountStorage = context.bridgeClient.getAccountStorage()
|
||||
|
||||
if (accountStorage.isAccountExists(context.database.myUserId)) {
|
||||
accountStorage.removeAccount(context.database.myUserId)
|
||||
context.shortToast("Removed account due to forced logout")
|
||||
}
|
||||
if (accountStorage.isAccountExists(context.database.myUserId)) {
|
||||
accountStorage.removeAccount(context.database.myUserId)
|
||||
context.shortToast(translation["forced_logout_toast"])
|
||||
}
|
||||
}
|
||||
return@hook
|
||||
}
|
||||
|
||||
@@ -75,12 +75,22 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
|
||||
private fun askForKeys(conversationId: String) {
|
||||
val friendId = context.database.getDMOtherParticipant(conversationId) ?: run {
|
||||
context.longToast("Can't find friendId for conversationId $conversationId")
|
||||
context.longToast(
|
||||
translation.format(
|
||||
"missing_friend_id_toast",
|
||||
"conversationId" to conversationId
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val publicKey = e2eeInterface.createKeyExchange(friendId) ?: run {
|
||||
context.longToast("Can't create key exchange for friendId $friendId")
|
||||
context.longToast(
|
||||
translation.format(
|
||||
"key_exchange_failed_toast",
|
||||
"friendId" to friendId
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -131,7 +141,12 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
|
||||
private fun handlePublicKeyRequest(conversationId: String, publicKey: ByteArray) {
|
||||
val friendId = context.database.getDMOtherParticipant(conversationId) ?: run {
|
||||
context.longToast("Can't find friendId for conversationId $conversationId")
|
||||
context.longToast(
|
||||
translation.format(
|
||||
"missing_friend_id_toast",
|
||||
"conversationId" to conversationId
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
warnKeyOverwrite(friendId) {
|
||||
@@ -151,7 +166,12 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
|
||||
private fun handleSecretResponse(conversationId: String, secret: ByteArray) {
|
||||
val friendId = context.database.getDMOtherParticipant(conversationId) ?: run {
|
||||
context.longToast("Can't find friendId for conversationId $conversationId")
|
||||
context.longToast(
|
||||
translation.format(
|
||||
"missing_friend_id_toast",
|
||||
"conversationId" to conversationId
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
warnKeyOverwrite(friendId) {
|
||||
@@ -561,4 +581,4 @@ class EndToEndEncryption : MessagingRuleFeature(
|
||||
}
|
||||
|
||||
override fun getRuleState() = RuleState.WHITELIST
|
||||
}
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ class AutoDeleteSentMessages : MessagingRuleFeature("Auto Delete Sent Messages",
|
||||
notificationManager.cancel(9999)
|
||||
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
context.shortToast("Auto delete queue cleared")
|
||||
context.shortToast(translation["queue_cleared_toast"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class MessageTranslator : Feature("Instant Translation") {
|
||||
if (config.pauseOnError.get()) {
|
||||
isPaused = true
|
||||
context.log.warn("Translation paused due to errors")
|
||||
context.shortToast("Translation service temporarily unavailable")
|
||||
context.shortToast(context.translation["toast_translation_service_unavailable"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,12 @@ class Notifications : Feature("Notifications") {
|
||||
val myUser = context.database.myUserId.let { context.database.getFriendInfo(it) } ?: return@subscribe
|
||||
|
||||
context.messageSender.sendChatMessage(listOf(SnapUUID(conversationId)), input, onError = {
|
||||
context.longToast("Failed to send message: $it")
|
||||
context.longToast(
|
||||
context.translation.format(
|
||||
"toast_send_message_failed",
|
||||
"error" to it.toString()
|
||||
)
|
||||
)
|
||||
context.coroutineScope.launch(coroutineDispatcher) {
|
||||
appendNotificationText("Failed to send message: $it")
|
||||
}
|
||||
@@ -221,7 +226,7 @@ class Notifications : Feature("Notifications") {
|
||||
onResult = {
|
||||
if (it != null) {
|
||||
context.log.error("Failed to mark conversation as read: $it")
|
||||
context.shortToast("Failed to mark conversation as read")
|
||||
context.shortToast(context.translation["toast_mark_conversation_read_failed"])
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -245,7 +250,7 @@ class Notifications : Feature("Notifications") {
|
||||
},
|
||||
onError = {
|
||||
context.log.error("Failed to fetch conversation: $it")
|
||||
context.shortToast("Failed to fetch conversation")
|
||||
context.shortToast(context.translation["toast_fetch_conversation_failed"])
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -257,13 +262,13 @@ class Notifications : Feature("Notifications") {
|
||||
conversationManager.updateMessage(conversationId, clientMessageId, MessageUpdate.READ) {
|
||||
if (it != null) {
|
||||
context.log.error("Failed to open snap: $it")
|
||||
context.shortToast("Failed to open snap")
|
||||
context.shortToast(context.translation["toast_open_snap_failed"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
context.log.error("Failed to mark message as read", it)
|
||||
context.shortToast("Failed to mark message as read. Check logs for more details")
|
||||
context.shortToast(context.translation["toast_mark_message_read_failed"])
|
||||
}
|
||||
notificationManager.cancel(notificationId)
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class ConversationToolbox : Feature("Conversation Toolbox") {
|
||||
|
||||
private fun openToolbox() {
|
||||
val openedConversationId = context.feature(Messaging::class).openedConversationUUID?.toString() ?: run {
|
||||
context.shortToast("You must open a conversation first")
|
||||
context.shortToast(context.translation["toast_open_conversation_first"])
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -59,19 +59,19 @@ object LSPatchUpdater {
|
||||
}
|
||||
|
||||
context.log.verbose("updating", TAG)
|
||||
context.shortToast("Updating PurrfectSnap. Please wait...")
|
||||
context.shortToast(context.translation["toast_updating_purrfectsnap"])
|
||||
// copy embedded module to cache
|
||||
runCatching {
|
||||
seAppApk.copyTo(embeddedModule, overwrite = true)
|
||||
}.onFailure {
|
||||
seAppApk.delete()
|
||||
context.log.error("Failed to copy embedded module", it, TAG)
|
||||
context.longToast("Failed to update PurrfectSnap. Please check logcat for more details.")
|
||||
context.longToast(context.translation["toast_update_purrfectsnap_failed"])
|
||||
context.forceCloseApp()
|
||||
return
|
||||
}
|
||||
|
||||
context.longToast("PurrfectSnap updated!")
|
||||
context.longToast(context.translation["toast_purrfectsnap_updated"])
|
||||
context.log.verbose("updated", TAG)
|
||||
context.softRestartApp()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user