mirror of
https://github.com/rhunk/SnapEnhance.git
synced 2025-06-13 05:37:48 +02:00
chore(lang): section translation
- profile picture downloader config
This commit is contained in:
@ -1,14 +1,22 @@
|
||||
package me.rhunk.snapenhance.ui.manager.sections
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import me.rhunk.snapenhance.ui.manager.Section
|
||||
|
||||
class NotImplemented : Section() {
|
||||
@Composable
|
||||
override fun Content() {
|
||||
Column {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(text = "Not implemented yet!")
|
||||
}
|
||||
}
|
||||
|
@ -269,9 +269,13 @@ class DownloadsSection : Section() {
|
||||
item {
|
||||
Spacer(Modifier.height(20.dp))
|
||||
if (loadedDownloads.value.isEmpty()) {
|
||||
Text(text = "(empty)", fontSize = 20.sp, modifier = Modifier
|
||||
Text(
|
||||
text = context.translation["manager.sections.downloads.empty_download_list"],
|
||||
fontSize = 20.sp,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp), textAlign = TextAlign.Center)
|
||||
.padding(10.dp), textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
val lastItemIndex = (loadedDownloads.value.size - 1).takeIf { it >= 0 } ?: return@LaunchedEffect
|
||||
|
@ -250,7 +250,9 @@ class HomeSection : Section() {
|
||||
navController.navigate(LOGS_SECTION_ROUTE)
|
||||
showDropDown = false
|
||||
}, text = {
|
||||
Text(text = "Clear logs")
|
||||
Text(
|
||||
text = context.translation["manager.sections.home.logs.clear_logs_button"]
|
||||
)
|
||||
})
|
||||
|
||||
DropdownMenuItem(onClick = {
|
||||
@ -267,7 +269,9 @@ class HomeSection : Section() {
|
||||
}
|
||||
showDropDown = false
|
||||
}, text = {
|
||||
Text(text = "Export logs")
|
||||
Text(
|
||||
text = context.translation["manager.sections.home.logs.export_logs_button"]
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ class AddFriendDialog(
|
||||
private val context: RemoteSideContext,
|
||||
private val section: SocialSection,
|
||||
) {
|
||||
|
||||
private val translation by lazy { context.translation.getCategory("manager.dialogs.add_friend")}
|
||||
|
||||
@Composable
|
||||
private fun ListCardEntry(name: String, getCurrentState: () -> Boolean, onState: (Boolean) -> Unit = {}) {
|
||||
var currentState by remember { mutableStateOf(getCurrentState()) }
|
||||
@ -95,7 +98,7 @@ class AddFriendDialog(
|
||||
.padding(10.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Add Friend or Group",
|
||||
text = translation["title"],
|
||||
fontSize = 23.sp,
|
||||
fontWeight = FontWeight.ExtraBold,
|
||||
modifier = Modifier
|
||||
@ -113,7 +116,7 @@ class AddFriendDialog(
|
||||
value = searchKeyword.value,
|
||||
onValueChange = { searchKeyword.value = it },
|
||||
label = {
|
||||
Text(text = "Search")
|
||||
Text(text = translation["search_hint"])
|
||||
},
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
@ -184,7 +187,7 @@ class AddFriendDialog(
|
||||
) {
|
||||
if (hasFetchError) {
|
||||
Text(
|
||||
text = "Failed to fetch data",
|
||||
text = translation["fetch_error"],
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(bottom = 10.dp, top = 10.dp)
|
||||
@ -222,7 +225,7 @@ class AddFriendDialog(
|
||||
) {
|
||||
item {
|
||||
if (filteredGroups.isEmpty()) return@item
|
||||
Text(text = "Groups",
|
||||
Text(text = translation["category_groups"],
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(bottom = 10.dp, top = 10.dp)
|
||||
@ -248,7 +251,7 @@ class AddFriendDialog(
|
||||
|
||||
item {
|
||||
if (filteredFriends.isEmpty()) return@item
|
||||
Text(text = "Friends",
|
||||
Text(text = translation["category_friends"],
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(bottom = 10.dp, top = 10.dp)
|
||||
|
@ -40,6 +40,8 @@ class ScopeContent(
|
||||
val scope: SocialScope,
|
||||
private val id: String
|
||||
) {
|
||||
private val translation by lazy { context.translation.getCategory("manager.sections.social") }
|
||||
|
||||
fun deleteScope(coroutineScope: CoroutineScope) {
|
||||
when (scope) {
|
||||
SocialScope.FRIEND -> context.modDatabase.deleteFriend(id)
|
||||
@ -68,7 +70,7 @@ class ScopeContent(
|
||||
|
||||
val rules = context.modDatabase.getRules(id)
|
||||
|
||||
SectionTitle("Rules")
|
||||
SectionTitle(translation["rules_title"])
|
||||
|
||||
ContentCard {
|
||||
//manager anti features etc
|
||||
@ -163,7 +165,7 @@ class ScopeContent(
|
||||
private fun Friend() {
|
||||
//fetch the friend from the database
|
||||
val friend = remember { context.modDatabase.getFriendInfo(id) } ?: run {
|
||||
Text(text = "Friend not found")
|
||||
Text(text = translation["not_found"])
|
||||
return
|
||||
}
|
||||
|
||||
@ -197,9 +199,6 @@ class ScopeContent(
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Light
|
||||
)
|
||||
// Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
//DeleteScopeEntityButton()
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@ -207,7 +206,7 @@ class ScopeContent(
|
||||
//streaks
|
||||
streaks?.let {
|
||||
var shouldNotify by remember { mutableStateOf(it.notify) }
|
||||
SectionTitle("Streaks")
|
||||
SectionTitle(translation["streaks_title"])
|
||||
ContentCard {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@ -215,13 +214,13 @@ class ScopeContent(
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(text = "Length: ${streaks.length}", maxLines = 1)
|
||||
Text(text = "Expires in: ${computeStreakETA(streaks.expirationTimestamp)}", maxLines = 1)
|
||||
Text(text = translation.format("streaks_length_text", "count" to streaks.length.toString()), maxLines = 1)
|
||||
Text(text = translation.format("streaks_expiration_text", "eta" to computeStreakETA(streaks.expirationTimestamp)), maxLines = 1)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(text = "Reminder", maxLines = 1, modifier = Modifier.padding(end = 10.dp))
|
||||
Text(text = translation["reminder_button"], maxLines = 1, modifier = Modifier.padding(end = 10.dp))
|
||||
Switch(checked = shouldNotify, onCheckedChange = {
|
||||
context.modDatabase.setFriendStreaksNotify(id, it)
|
||||
shouldNotify = it
|
||||
@ -237,7 +236,7 @@ class ScopeContent(
|
||||
private fun Group() {
|
||||
//fetch the group from the database
|
||||
val group = remember { context.modDatabase.getGroupInfo(id) } ?: run {
|
||||
Text(text = "Group not found")
|
||||
Text(text = translation["not_found"])
|
||||
return
|
||||
}
|
||||
|
||||
@ -256,7 +255,7 @@ class ScopeContent(
|
||||
)
|
||||
Spacer(modifier = Modifier.height(5.dp))
|
||||
Text(
|
||||
text = "Participants: ${group.participantsCount}",
|
||||
text = translation.format("participants_text", "count" to group.participantsCount.toString()),
|
||||
maxLines = 1,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Light
|
||||
|
@ -219,7 +219,7 @@ class SocialSection : Section() {
|
||||
MaterialTheme.colorScheme.error
|
||||
else MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(text = "${streaks.hoursLeft()}h", maxLines = 1, fontWeight = FontWeight.Bold)
|
||||
Text(text = context.translation.format("context.sections.social.streaks_expiration_short", "hours" to streaks.length.toString()), maxLines = 1, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,11 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -26,6 +30,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import me.rhunk.snapenhance.ui.setup.screens.SetupScreen
|
||||
@ -38,6 +43,24 @@ class PermissionsScreen : SetupScreen() {
|
||||
activityLauncherHelper = ActivityLauncherHelper(context.activity!!)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RequestButton(onClick: () -> Unit) {
|
||||
Button(onClick = onClick) {
|
||||
Text(text = context.translation["setup.permissions.request_button"])
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GrantedIcon() {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Check,
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.padding(5.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
@Composable
|
||||
override fun Content() {
|
||||
@ -59,7 +82,7 @@ class PermissionsScreen : SetupScreen() {
|
||||
allowNext(false)
|
||||
}
|
||||
|
||||
DialogText(text = "To continue you need to fit the following requirements:")
|
||||
DialogText(text = context.translation["setup.permissions.dialog"])
|
||||
|
||||
OutlinedCard(
|
||||
modifier = Modifier
|
||||
@ -73,39 +96,36 @@ class PermissionsScreen : SetupScreen() {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Absolute.SpaceAround
|
||||
) {
|
||||
DialogText(text = "Notification access", modifier = Modifier.weight(1f))
|
||||
DialogText(text = context.translation["setup.permissions.dialog"], modifier = Modifier.weight(1f))
|
||||
if (notificationPermissionGranted) {
|
||||
DialogText(text = "Granted")
|
||||
} else {
|
||||
Button(onClick = {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
activityLauncherHelper.requestPermission(Manifest.permission.POST_NOTIFICATIONS) { resultCode, _ ->
|
||||
coroutineScope.launch {
|
||||
notificationPermissionGranted = resultCode == ComponentActivity.RESULT_OK
|
||||
}
|
||||
GrantedIcon()
|
||||
return@Row
|
||||
}
|
||||
|
||||
RequestButton {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
activityLauncherHelper.requestPermission(Manifest.permission.POST_NOTIFICATIONS) { resultCode, _ ->
|
||||
coroutineScope.launch {
|
||||
notificationPermissionGranted = resultCode == ComponentActivity.RESULT_OK
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Text(text = "Request")
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
DialogText(text = "Battery optimisation", modifier = Modifier.weight(1f))
|
||||
DialogText(text = context.translation["setup.permissions.battery_optimization"], modifier = Modifier.weight(1f))
|
||||
if (isBatteryOptimisationIgnored) {
|
||||
DialogText(text = "Ignored")
|
||||
} else {
|
||||
Button(onClick = {
|
||||
activityLauncherHelper.launch(Intent().apply {
|
||||
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
data = Uri.parse("package:${context.androidContext.packageName}")
|
||||
}) { resultCode, _ ->
|
||||
coroutineScope.launch {
|
||||
isBatteryOptimisationIgnored = resultCode == 0
|
||||
}
|
||||
GrantedIcon()
|
||||
return@Row
|
||||
}
|
||||
RequestButton {
|
||||
activityLauncherHelper.launch(Intent().apply {
|
||||
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
data = Uri.parse("package:${context.androidContext.packageName}")
|
||||
}) { resultCode, _ ->
|
||||
coroutineScope.launch {
|
||||
isBatteryOptimisationIgnored = resultCode == 0
|
||||
}
|
||||
}) {
|
||||
Text(text = "Request")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ class AlertDialogs(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
) {
|
||||
Button(onClick = { dismiss() }) {
|
||||
Text(text = "Cancel")
|
||||
Text(text = translation["button.cancel"])
|
||||
}
|
||||
Button(onClick = {
|
||||
when (property.key.dataType.type) {
|
||||
@ -232,7 +232,7 @@ class AlertDialogs(
|
||||
}
|
||||
dismiss()
|
||||
}) {
|
||||
Text(text = "Ok")
|
||||
Text(text = translation["button.ok"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,12 @@
|
||||
"generate_failure_no_snapchat": "SnapEnhance was unable to detect Snapchat, please try reinstalling Snapchat.",
|
||||
"generate_failure": "An error occurred while trying to generate mappings, please try again.",
|
||||
"generate_success": "Mappings generated successfully."
|
||||
},
|
||||
"permissions": {
|
||||
"dialog": "To continue you need to fit the following requirements:",
|
||||
"notification_access": "Notification Access",
|
||||
"battery_optimization": "Battery Optimization",
|
||||
"request_button": "Request"
|
||||
}
|
||||
},
|
||||
|
||||
@ -25,8 +31,38 @@
|
||||
"social": "Social",
|
||||
"plugins": "Plugins"
|
||||
},
|
||||
"features": {
|
||||
"disabled": "Disabled"
|
||||
"sections": {
|
||||
"home": {
|
||||
"logs": {
|
||||
"clear_logs_button": "Clear Logs",
|
||||
"export_logs_button": "Export Logs"
|
||||
}
|
||||
},
|
||||
"downloads": {
|
||||
"empty_download_list": "(empty)"
|
||||
},
|
||||
"features": {
|
||||
"disabled": "Disabled"
|
||||
},
|
||||
"social": {
|
||||
"rules_title": "Rules",
|
||||
"participants_text": "{count} participants",
|
||||
"not_found": "Not found",
|
||||
"streaks_title": "Streaks",
|
||||
"streaks_length_text": "Length: {length}",
|
||||
"streaks_expiration_short": "{hours}h",
|
||||
"streaks_expiration_text": "Expires in {eta}",
|
||||
"reminder_button": "Set Reminder"
|
||||
}
|
||||
},
|
||||
"dialogs": {
|
||||
"add_friend": {
|
||||
"title": "Add Friend or Group",
|
||||
"search_hint": "Search",
|
||||
"fetch_error": "Failed to fetch data",
|
||||
"category_groups": "Groups",
|
||||
"category_friends": "Friends"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -118,6 +154,10 @@
|
||||
"name": "Force Voice Note Format",
|
||||
"description": "Forces Voice Notes to be saved in a specified Format"
|
||||
},
|
||||
"download_profile_pictures": {
|
||||
"name": "Download Profile Pictures",
|
||||
"description": "Allows you to download Profile Pictures from the profile page"
|
||||
},
|
||||
"chat_download_context_menu": {
|
||||
"name": "Chat Download Context Menu",
|
||||
"description": "Allows you to download media from a conversation by long-pressing them"
|
||||
@ -577,16 +617,6 @@
|
||||
"birthday": "Birthday : {month} {day}"
|
||||
},
|
||||
|
||||
"auto_updater": {
|
||||
"no_update_available": "No Update available!",
|
||||
"dialog_title": "New Update available!",
|
||||
"dialog_message": "There is a new Update for SnapEnhance available! ({version})\n\n{body}",
|
||||
"dialog_positive_button": "Download and Install",
|
||||
"dialog_negative_button": "Cancel",
|
||||
"downloading_toast": "Downloading Update...",
|
||||
"download_manager_notification_title": "Downloading SnapEnhance APK..."
|
||||
},
|
||||
|
||||
"chat_export": {
|
||||
"select_export_format": "Select the Export Format",
|
||||
"select_media_type": "Select Media Types to export",
|
||||
@ -610,7 +640,15 @@
|
||||
"positive": "Yes",
|
||||
"negative": "No",
|
||||
"cancel": "Cancel",
|
||||
"open": "Open"
|
||||
"open": "Open",
|
||||
"download": "Download"
|
||||
},
|
||||
|
||||
"profile_picture_downloader": {
|
||||
"button": "Download Profile Picture",
|
||||
"title": "Profile Picture Downloader",
|
||||
"avatar_option": "Avatar",
|
||||
"background_option": "Background"
|
||||
},
|
||||
|
||||
"download_manager_activity": {
|
||||
|
@ -41,6 +41,7 @@ class DownloaderConfig : ConfigContainer() {
|
||||
val forceVoiceNoteFormat = unique("force_voice_note_format", "aac", "mp3", "opus") {
|
||||
addFlags(ConfigFlag.NO_TRANSLATE)
|
||||
}
|
||||
val downloadProfilePictures = boolean("download_profile_pictures")
|
||||
val chatDownloadContextMenu = boolean("chat_download_context_menu")
|
||||
val ffmpegOptions = container("ffmpeg_options", FFMpegOptions()) { addNotices(FeatureNotice.UNSTABLE) }
|
||||
val logging = multiple("logging", "started", "success", "progress", "failure").apply {
|
||||
|
@ -11,6 +11,7 @@ class RootConfig : ConfigContainer() {
|
||||
val rules = container("rules", Rules()) { icon = "Rule" }
|
||||
val camera = container("camera", Camera()) { icon = "Camera"}
|
||||
val streaksReminder = container("streaks_reminder", StreaksReminderConfig()) { icon = "Alarm" }
|
||||
val experimental = container("experimental", Experimental()) { icon = "Science"; addNotices(FeatureNotice.UNSTABLE)
|
||||
val experimental = container("experimental", Experimental()) {
|
||||
icon = "Science"; addNotices(FeatureNotice.UNSTABLE)
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ import java.nio.ByteBuffer
|
||||
class ProfilePictureDownloader : Feature("ProfilePictureDownloader", loadParams = FeatureLoadParams.ACTIVITY_CREATE_ASYNC) {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun asyncOnActivityCreate() {
|
||||
if (!context.config.downloader.downloadProfilePictures.get()) return
|
||||
|
||||
var friendUsername: String? = null
|
||||
var backgroundUrl: String? = null
|
||||
var avatarUrl: String? = null
|
||||
@ -24,7 +26,7 @@ class ProfilePictureDownloader : Feature("ProfilePictureDownloader", loadParams
|
||||
if (event.view::class.java.name != "com.snap.unifiedpublicprofile.UnifiedPublicProfileView") return@subscribe
|
||||
|
||||
event.parent.addView(Button(event.parent.context).apply {
|
||||
text = "Download"
|
||||
text = this@ProfilePictureDownloader.context.translation["profile_picture_downloader.button"]
|
||||
layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT).apply {
|
||||
setMargins(0, 200, 0, 0)
|
||||
}
|
||||
@ -32,12 +34,14 @@ class ProfilePictureDownloader : Feature("ProfilePictureDownloader", loadParams
|
||||
ViewAppearanceHelper.newAlertDialogBuilder(
|
||||
this@ProfilePictureDownloader.context.mainActivity!!
|
||||
).apply {
|
||||
setTitle("Download profile picture")
|
||||
setTitle(this@ProfilePictureDownloader.context.translation["profile_picture_downloader.title"])
|
||||
val choices = mutableMapOf<String, String>()
|
||||
backgroundUrl?.let { choices["Background"] = it }
|
||||
avatarUrl?.let { choices["Avatar"] = it }
|
||||
backgroundUrl?.let { choices["avatar_option"] = it }
|
||||
avatarUrl?.let { choices["background_option"] = it }
|
||||
|
||||
setItems(choices.keys.toTypedArray()) { _, which ->
|
||||
setItems(choices.keys.map {
|
||||
this@ProfilePictureDownloader.context.translation["profile_picture_downloader.$it"]
|
||||
}.toTypedArray()) { _, which ->
|
||||
runCatching {
|
||||
this@ProfilePictureDownloader.context.feature(MediaDownloader::class).downloadProfilePicture(
|
||||
choices.values.elementAt(which),
|
||||
|
Reference in New Issue
Block a user