chore(lang): section translation

- profile picture downloader config
This commit is contained in:
rhunk
2023-09-03 00:11:11 +02:00
parent e587f4700a
commit 77584d67e2
12 changed files with 150 additions and 68 deletions

View File

@ -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!")
}
}

View File

@ -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

View File

@ -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"]
)
})
}
}

View File

@ -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)

View File

@ -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

View File

@ -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)
}
}
}

View File

@ -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")
}
}
}

View File

@ -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"])
}
}
}

View File

@ -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": {

View File

@ -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 {

View File

@ -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)
}
}

View File

@ -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),