From 7d3d76d7314658c051e09f6bbbf9310477029e50 Mon Sep 17 00:00:00 2001 From: aj3423 Date: Thu, 10 Oct 2024 09:24:24 +0800 Subject: [PATCH] add bot import/export, support xml action --- .../main/java/spam/blocker/GlobalEvents.kt | 7 + .../main/java/spam/blocker/GlobalVariables.kt | 5 +- .../main/java/spam/blocker/db/RuleTable.kt | 4 +- .../main/java/spam/blocker/db/SpamTable.kt | 1 + app/src/main/java/spam/blocker/def/Def.kt | 2 + .../java/spam/blocker/service/bot/Action.kt | 19 +- .../java/spam/blocker/service/bot/Actions.kt | 206 +++++++++++++++++- .../service/bot/BotSerializersModule.kt | 11 + .../main/java/spam/blocker/ui/main/Debug.kt | 3 +- .../java/spam/blocker/ui/main/MainActivity.kt | 4 +- .../spam/blocker/ui/setting/SettingScreen.kt | 2 +- .../spam/blocker/ui/setting/bot/BotCard.kt | 40 ++-- .../spam/blocker/ui/setting/bot/BotHeader.kt | 36 ++- .../ui/setting/bot/BotImportExportDialog.kt | 106 +++++++++ .../spam/blocker/ui/setting/bot/BotList.kt | 98 ++++++--- .../spam/blocker/ui/setting/bot/BotPresets.kt | 3 +- .../blocker/ui/setting/bot/EditBotDialog.kt | 1 + .../spam/blocker/ui/setting/quick/SpamDB.kt | 7 + .../ui/setting/regex/EditRuleDialog.kt | 8 +- .../spam/blocker/ui/setting/regex/RuleList.kt | 14 +- .../spam/blocker/ui/widgets/BottomNavView.kt | 2 +- .../java/spam/blocker/ui/widgets/InputBox.kt | 7 +- .../spam/blocker/ui/widgets/NonLazyGrid.kt | 41 ++++ app/src/main/java/spam/blocker/util/Util.kt | 8 +- app/src/main/java/spam/blocker/util/Xml.kt | 41 ++++ .../main/res/drawable/ic_backup_import.xml | 8 +- app/src/main/res/drawable/ic_csv.xml | 28 +-- app/src/main/res/drawable/ic_replace.xml | 18 ++ app/src/main/res/drawable/ic_xml.xml | 20 ++ app/src/main/res/values-de/strings_8.xml | 75 ++++--- app/src/main/res/values-es/strings_8.xml | 73 ++++--- app/src/main/res/values-fr/strings_8.xml | 83 ++++--- app/src/main/res/values-gal/strings_8.xml | 64 +++--- app/src/main/res/values-ja/strings_8.xml | 73 ++++--- app/src/main/res/values-pt-rBR/strings_8.xml | 81 ++++--- app/src/main/res/values-ru/strings_8.xml | 87 ++++---- app/src/main/res/values-uk/strings_8.xml | 81 ++++--- app/src/main/res/values-zh/strings_8.xml | 57 +++-- app/src/main/res/values/strings_8.xml | 33 ++- auto_translate/translate.go | 10 +- 40 files changed, 1045 insertions(+), 422 deletions(-) create mode 100644 app/src/main/java/spam/blocker/ui/setting/bot/BotImportExportDialog.kt create mode 100644 app/src/main/java/spam/blocker/ui/widgets/NonLazyGrid.kt create mode 100644 app/src/main/java/spam/blocker/util/Xml.kt create mode 100644 app/src/main/res/drawable/ic_replace.xml create mode 100644 app/src/main/res/drawable/ic_xml.xml diff --git a/app/src/main/java/spam/blocker/GlobalEvents.kt b/app/src/main/java/spam/blocker/GlobalEvents.kt index 0a7533dd..fbe04109 100644 --- a/app/src/main/java/spam/blocker/GlobalEvents.kt +++ b/app/src/main/java/spam/blocker/GlobalEvents.kt @@ -2,6 +2,7 @@ package spam.blocker import android.content.Context import androidx.compose.runtime.Immutable +import androidx.compose.runtime.mutableIntStateOf import androidx.lifecycle.MutableLiveData import androidx.work.WorkManager import spam.blocker.db.BotTable @@ -12,6 +13,12 @@ import spam.blocker.ui.setting.quick.reScheduleSpamDBCleanup @Immutable object Events { + // An event triggered when spam db is updated, maybe triggered by Workflow + val spamDbUpdated = mutableIntStateOf(0) + + // An event triggered when regex rule list is updated, maybe triggered by Workflow + val regexRuleUpdated = mutableIntStateOf(0) + // An event for notifying the configuration has changed, // observers should restart, such as: // - history cleanup schedule diff --git a/app/src/main/java/spam/blocker/GlobalVariables.kt b/app/src/main/java/spam/blocker/GlobalVariables.kt index 1a24bc84..dc342942 100644 --- a/app/src/main/java/spam/blocker/GlobalVariables.kt +++ b/app/src/main/java/spam/blocker/GlobalVariables.kt @@ -3,11 +3,8 @@ package spam.blocker import androidx.compose.runtime.Immutable import androidx.compose.runtime.MutableIntState import androidx.compose.runtime.MutableState -import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.lifecycle.MutableLiveData import spam.blocker.ui.history.CallViewModel import spam.blocker.ui.history.SmsViewModel import spam.blocker.ui.setting.TestingViewModel @@ -30,7 +27,7 @@ object G { val NumberRuleVM : NumberRuleViewModel = NumberRuleViewModel() val ContentRuleVM : ContentRuleViewModel = ContentRuleViewModel() val QuickCopyRuleVM : QuickCopyRuleViewModel = QuickCopyRuleViewModel() - val BotVM : BotViewModel = BotViewModel() + val botVM : BotViewModel = BotViewModel() lateinit var bottomBarVM : BottomBarViewModel diff --git a/app/src/main/java/spam/blocker/db/RuleTable.kt b/app/src/main/java/spam/blocker/db/RuleTable.kt index 8f946c9c..cc9a8ce0 100644 --- a/app/src/main/java/spam/blocker/db/RuleTable.kt +++ b/app/src/main/java/spam/blocker/db/RuleTable.kt @@ -60,9 +60,9 @@ data class RegexRule( var patternExtra: String = "", @Serializable(with = CompatibleIntSerializer::class) - var patternFlags: Int = Def.FLAG_REGEX_IGNORE_CASE or Def.FLAG_REGEX_DOT_MATCH_ALL, + var patternFlags: Int = Def.DefaultRegexFlags, @Serializable(with = CompatibleIntSerializer::class) - var patternExtraFlags: Int = Def.FLAG_REGEX_IGNORE_CASE or Def.FLAG_REGEX_DOT_MATCH_ALL, + var patternExtraFlags: Int = Def.DefaultRegexFlags, var description: String = "", var priority: Int = 1, diff --git a/app/src/main/java/spam/blocker/db/SpamTable.kt b/app/src/main/java/spam/blocker/db/SpamTable.kt index 04cbd5b4..51a3efd0 100644 --- a/app/src/main/java/spam/blocker/db/SpamTable.kt +++ b/app/src/main/java/spam/blocker/db/SpamTable.kt @@ -8,6 +8,7 @@ import androidx.core.database.getIntOrNull import androidx.core.database.getStringOrNull import kotlinx.serialization.Serializable import spam.blocker.def.Def +import spam.blocker.util.Util import spam.blocker.util.hasFlag import spam.blocker.util.loge diff --git a/app/src/main/java/spam/blocker/def/Def.kt b/app/src/main/java/spam/blocker/def/Def.kt index a465a1cb..e203ddd3 100644 --- a/app/src/main/java/spam/blocker/def/Def.kt +++ b/app/src/main/java/spam/blocker/def/Def.kt @@ -111,6 +111,8 @@ object Def { const val FLAG_REGEX_FOR_CONTACT_GROUP = 1 shl 11 const val FLAG_REGEX_FOR_CONTACT = 1 shl 12 + const val DefaultRegexFlags = FLAG_REGEX_IGNORE_CASE or FLAG_REGEX_DOT_MATCH_ALL + val MAP_REGEX_FLAGS = mapOf( FLAG_REGEX_IGNORE_CASE to "i", FLAG_REGEX_MULTILINE to "m", diff --git a/app/src/main/java/spam/blocker/service/bot/Action.kt b/app/src/main/java/spam/blocker/service/bot/Action.kt index ef278a8d..4e25bace 100644 --- a/app/src/main/java/spam/blocker/service/bot/Action.kt +++ b/app/src/main/java/spam/blocker/service/bot/Action.kt @@ -3,7 +3,6 @@ package spam.blocker.service.bot import android.Manifest import android.content.Context import android.content.Intent -import android.content.pm.PackageManager.PERMISSION_GRANTED import android.net.Uri import android.os.Build import android.os.Environment @@ -25,7 +24,7 @@ import java.lang.Exception // When adding a new IAction type, follow all the steps: // - add to `ActionType` -// - implement it +// - implement it in Actions.kt // - add to `defaultActions` // - add to `botModule` in BotSerializersModule.kt @@ -41,19 +40,23 @@ enum class ActionType { ParseCSV, ImportToSpamDB, ImportAsRegexRule, + ConvertNumber, + ParseXML, } val defaultActions = listOf( HttpDownload(), + ImportToSpamDB(), + ImportAsRegexRule(), + ReadFile(), + WriteFile(), + ParseCSV(), + ParseXML(), + ConvertNumber(), CleanupSpamDB(), CleanupHistory(), BackupExport(), BackupImport(), - ReadFile(), - WriteFile(), - ParseCSV(), - ImportToSpamDB(), - ImportAsRegexRule(), ) @@ -62,7 +65,7 @@ enum class ParamType { None, String, ByteArray, - RuleList + RuleList, } interface IAction { diff --git a/app/src/main/java/spam/blocker/service/bot/Actions.kt b/app/src/main/java/spam/blocker/service/bot/Actions.kt index cfc228ac..eff292ac 100644 --- a/app/src/main/java/spam/blocker/service/bot/Actions.kt +++ b/app/src/main/java/spam/blocker/service/bot/Actions.kt @@ -4,14 +4,19 @@ import android.content.Context import androidx.compose.foundation.layout.Column import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import org.json.JSONObject +import spam.blocker.Events import spam.blocker.R import spam.blocker.config.Configs import spam.blocker.db.CallTable @@ -20,10 +25,12 @@ import spam.blocker.db.RegexRule import spam.blocker.db.SmsTable import spam.blocker.db.SpamNumber import spam.blocker.db.SpamTable +import spam.blocker.def.Def import spam.blocker.ui.setting.LabeledRow import spam.blocker.ui.widgets.GreyIcon import spam.blocker.ui.widgets.GreyLabel import spam.blocker.ui.widgets.NumberInputBox +import spam.blocker.ui.widgets.RegexInputBox import spam.blocker.ui.widgets.Str import spam.blocker.ui.widgets.StrInputBox import spam.blocker.ui.widgets.SwitchBox @@ -32,6 +39,7 @@ import spam.blocker.util.Algorithm.decompressToString import spam.blocker.util.Csv import spam.blocker.util.Now import spam.blocker.util.Util +import spam.blocker.util.Xml import spam.blocker.util.logi import spam.blocker.util.resolvePathTags import spam.blocker.util.resolveTimeTags @@ -41,7 +49,7 @@ import java.net.HttpURLConnection import java.net.URL @Composable -fun NoConfigNeeded() { +fun NoOptionNeeded() { GreyLabel(text = Str(R.string.no_config_needed)) } @@ -189,8 +197,12 @@ class CleanupSpamDB( val now = Now.currentMillis() val expireTimeMs = now - expiry * 24 * 3600 * 1000 - logi("now clean up spam db, deleting data before timestamp: $expireTimeMs") + logi("clean up spam db, deleting data before timestamp: $expireTimeMs") SpamTable.deleteBeforeTimestamp(ctx, expireTimeMs) + + // fire event to update the UI + Events.spamDbUpdated.intValue = Events.spamDbUpdated.intValue.plus(1) + return Pair(true, null) } @@ -242,7 +254,7 @@ class CleanupSpamDB( @Serializable @SerialName("BackupExport") class BackupExport( - private var includeSpamDB: Boolean = true + private var includeSpamDB: Boolean = false ) : IPermissiveAction { override fun execute(ctx: Context, arg: Any?): Pair { // Generate config data bytes @@ -298,13 +310,13 @@ class BackupExport( @Serializable @SerialName("BackupImport") class BackupImport( - private var includeSpamDB: Boolean = true + private var includeSpamDB: Boolean = false ) : IPermissiveAction { override fun execute(ctx: Context, arg: Any?): Pair { if (arg !is ByteArray) { return Pair( false, ctx.getString(R.string.invalid_input_type).format( - "ByteArray", arg?.javaClass?.simpleName + ParamType.ByteArray.name, arg?.javaClass?.simpleName ) ) } @@ -437,7 +449,7 @@ class WriteFile( override fun execute(ctx: Context, arg: Any?): Pair { if (arg !is ByteArray) { return Pair(false, ctx.getString(R.string.invalid_input_type).format( - "ByteArray", arg?.javaClass?.simpleName + ParamType.ByteArray.name, arg?.javaClass?.simpleName )) } val path = dir.resolvePathTags() @@ -512,7 +524,7 @@ class ParseCSV( if (arg !is ByteArray) { return Pair( false, ctx.getString(R.string.invalid_input_type).format( - "ByteArray", arg?.javaClass?.simpleName + ParamType.ByteArray.name, arg?.javaClass?.simpleName ) ) } @@ -569,6 +581,75 @@ class ParseCSV( } } +/* +input: ByteArray +output: List + */ +@Serializable +@SerialName("ParseXML") +class ParseXML( + var xpath: String = "" +) : IPermissiveAction { + + override fun execute(ctx: Context, arg: Any?): Pair { + if (arg !is ByteArray) { + return Pair( + false, ctx.getString(R.string.invalid_input_type).format( + ParamType.ByteArray.name, arg?.javaClass?.simpleName + ) + ) + } + + return try { + val rules = Xml.parse(bytes = arg, xpath).map { + RegexRule.fromMap(it) + } + + Pair(true, rules) + } catch (e: Exception) { + Pair(false, e.toString()) + } + } + + override fun type(): ActionType { + return ActionType.ParseXML + } + + override fun label(ctx: Context): String { + return ctx.getString(R.string.action_parse_xml) + } + + override fun summary(ctx: Context): String { + return "XPath: $xpath" + } + + override fun tooltip(ctx: Context): String { + return ctx.getString(R.string.help_action_parse_xml) + } + + override fun inputParamType(): ParamType { + return ParamType.ByteArray + } + + override fun outputParamType(): ParamType { + return ParamType.RuleList + } + + @Composable + override fun Icon() { + GreyIcon(iconId = R.drawable.ic_xml) + } + + @Composable + override fun Options() { + StrInputBox( + text = xpath, + label = { Text("XPath") }, + onValueChange = { xpath = it } + ) + } +} + /* input: List output: null @@ -577,6 +658,7 @@ output: null @SerialName("ImportToSpamDB") class ImportToSpamDB : IPermissiveAction { + @OptIn(DelicateCoroutinesApi::class) override fun execute(ctx: Context, arg: Any?): Pair { // It must be written like this, cannot be inlined in the `if()`, seems to be kotlin bug val inputValid = (arg is List<*>) && ((arg as List<*>).all { it is RegexRule }) @@ -595,6 +677,14 @@ class ImportToSpamDB : IPermissiveAction { SpamNumber(peer = (it as RegexRule).pattern, time = now) } val errorStr = SpamTable.addAll(ctx, numbers) + + // Fire a global event to update UI + GlobalScope.launch { + withContext(Dispatchers.IO) { + Events.spamDbUpdated.intValue = Events.spamDbUpdated.intValue.plus(1) + } + } + Pair(errorStr == null, errorStr) } catch (e: Exception) { Pair(false, e.toString()) @@ -632,7 +722,7 @@ class ImportToSpamDB : IPermissiveAction { @Composable override fun Options() { - NoConfigNeeded() + NoOptionNeeded() } } @@ -651,7 +741,7 @@ class ImportAsRegexRule( if (!(arg is List<*> && arg.all { it is RegexRule })) { return Pair( false, ctx.getString(R.string.invalid_input_type).format( - "List", arg?.javaClass?.simpleName + ParamType.RuleList.name, arg?.javaClass?.simpleName ) ) } @@ -669,6 +759,9 @@ class ImportAsRegexRule( ) NumberRuleTable().addNewRule(ctx, newRule) + // fire event to update the UI + Events.regexRuleUpdated.intValue = Events.regexRuleUpdated.intValue.plus(1) + Pair(true, null) } catch (e: Exception) { Pair(false, e.toString()) @@ -685,7 +778,11 @@ class ImportAsRegexRule( override fun summary(ctx: Context): String { val labelPriority = ctx.getString(R.string.priority) - return "$labelPriority: $priority" + + return if (description.isEmpty()) + "$labelPriority: $priority" + else + "$description, $labelPriority: $priority" } override fun tooltip(ctx: Context): String { @@ -725,3 +822,92 @@ class ImportAsRegexRule( } } } +/* +input: List +output: List + */ +@Serializable +@SerialName("ConvertNumber") +class ConvertNumber( + var from: String = "", + var flags: Int = Def.DefaultRegexFlags, + var to: String = "", +) : IPermissiveAction { + + override fun execute(ctx: Context, arg: Any?): Pair { + if (!(arg is List<*> && arg.all { it is RegexRule })) { + return Pair( + false, ctx.getString(R.string.invalid_input_type).format( + ParamType.RuleList.name, arg?.javaClass?.simpleName + ) + ) + } + + val clearedRuleList = (arg as List<*>).map { + val r = it as RegexRule + + val newNum = from.toRegex().replace(r.pattern, to) + + r.copy( + pattern = newNum + ) + } + return Pair(true, clearedRuleList) + } + + override fun type(): ActionType { + return ActionType.ConvertNumber + } + + override fun label(ctx: Context): String { + return ctx.getString(R.string.action_convert_number) + } + + override fun summary(ctx: Context): String { + return "$from -> $to" + } + + override fun tooltip(ctx: Context): String { + return ctx.getString(R.string.help_action_convert_number) + } + + override fun inputParamType(): ParamType { + return ParamType.RuleList + } + + override fun outputParamType(): ParamType { + return ParamType.RuleList + } + + @Composable + override fun Icon() { + GreyIcon(iconId = R.drawable.ic_replace) + } + + @Composable + override fun Options() { + + val flagsState = remember { mutableIntStateOf(flags) } + RegexInputBox( + label = { Text(Str(R.string.replace_from))}, + regexStr = from, + onRegexStrChange = { newVal, hasErr -> + if (!hasErr) { + from = newVal + } + }, + regexFlags = flagsState, + onFlagsChange = { + flagsState.intValue = it + flags = it + } + ) + StrInputBox( + label = { Text(Str(R.string.replace_to))}, + text = to, + onValueChange = { + to = it + } + ) + } +} diff --git a/app/src/main/java/spam/blocker/service/bot/BotSerializersModule.kt b/app/src/main/java/spam/blocker/service/bot/BotSerializersModule.kt index 6cd65c3e..1867480c 100644 --- a/app/src/main/java/spam/blocker/service/bot/BotSerializersModule.kt +++ b/app/src/main/java/spam/blocker/service/bot/BotSerializersModule.kt @@ -1,6 +1,7 @@ package spam.blocker.service.bot import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonBuilder import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass @@ -20,8 +21,10 @@ val botModule = SerializersModule { subclass(ReadFile::class) subclass(WriteFile::class) subclass(ParseCSV::class) + subclass(ParseXML::class) subclass(ImportToSpamDB::class) subclass(ImportAsRegexRule::class) + subclass(ConvertNumber::class) } } @@ -33,3 +36,11 @@ val botJson = Json { ignoreUnknownKeys = true } +val botPrettyJson = Json { + prettyPrint = true + + serializersModule = botModule + encodeDefaults = true + classDiscriminator = "type" + ignoreUnknownKeys = true +} \ No newline at end of file diff --git a/app/src/main/java/spam/blocker/ui/main/Debug.kt b/app/src/main/java/spam/blocker/ui/main/Debug.kt index dce0d249..761f2aa8 100644 --- a/app/src/main/java/spam/blocker/ui/main/Debug.kt +++ b/app/src/main/java/spam/blocker/ui/main/Debug.kt @@ -6,10 +6,11 @@ import android.os.Environment import android.util.Log import androidx.core.content.FileProvider.getUriForFile import spam.blocker.util.TAG +import spam.blocker.util.Xml +import spam.blocker.util.loge import java.io.File import java.security.AccessController.getContext fun debug(ctx: Context) { - } \ No newline at end of file diff --git a/app/src/main/java/spam/blocker/ui/main/MainActivity.kt b/app/src/main/java/spam/blocker/ui/main/MainActivity.kt index 1eb332d0..b0b1804d 100644 --- a/app/src/main/java/spam/blocker/ui/main/MainActivity.kt +++ b/app/src/main/java/spam/blocker/ui/main/MainActivity.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalView import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat +import spam.blocker.BuildConfig import spam.blocker.Events import spam.blocker.G import spam.blocker.R @@ -62,7 +63,8 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) - debug(this) + if (BuildConfig.DEBUG) + debug(this) val ctx = this val spf = Global(ctx) diff --git a/app/src/main/java/spam/blocker/ui/setting/SettingScreen.kt b/app/src/main/java/spam/blocker/ui/setting/SettingScreen.kt index fc885403..e477e2c6 100644 --- a/app/src/main/java/spam/blocker/ui/setting/SettingScreen.kt +++ b/app/src/main/java/spam/blocker/ui/setting/SettingScreen.kt @@ -160,7 +160,7 @@ fun SettingScreen() { verticalArrangement = Arrangement.spacedBy(0.dp), ) { // Bot list - LaunchedEffect(true) { G.BotVM.reload(ctx) } + LaunchedEffect(true) { G.botVM.reload(ctx) } BotHeader() BotList() } diff --git a/app/src/main/java/spam/blocker/ui/setting/bot/BotCard.kt b/app/src/main/java/spam/blocker/ui/setting/bot/BotCard.kt index 30161d15..c215cca6 100644 --- a/app/src/main/java/spam/blocker/ui/setting/bot/BotCard.kt +++ b/app/src/main/java/spam/blocker/ui/setting/bot/BotCard.kt @@ -2,33 +2,25 @@ package spam.blocker.ui.setting.bot import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box 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.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.key -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import spam.blocker.G import spam.blocker.R import spam.blocker.db.Bot -import spam.blocker.db.BotTable import spam.blocker.ui.M -import spam.blocker.ui.setting.regex.DisableNestedScrolling import spam.blocker.ui.theme.LocalPalette -import spam.blocker.ui.theme.Salmon import spam.blocker.ui.widgets.GreyLabel +import spam.blocker.ui.widgets.NonLazyGrid import spam.blocker.ui.widgets.OutlineCard import spam.blocker.ui.widgets.RowVCenter import spam.blocker.ui.widgets.RowVCenterSpaced @@ -45,19 +37,18 @@ fun BotCard( OutlineCard( containerBg = MaterialTheme.colorScheme.background ) { - Column( - verticalArrangement = Arrangement.spacedBy(6.dp), + RowVCenterSpaced( + space = 10, modifier = modifier .fillMaxWidth() .padding(horizontal = 10.dp, vertical = 8.dp) ) { - // desc - GreyLabel(text = bot.desc, fontWeight = FontWeight.SemiBold) - - RowVCenter( - horizontalArrangement = Arrangement.SpaceBetween, - modifier = M.fillMaxWidth() + Column( + modifier = M.weight(1f) ) { + // desc + GreyLabel(text = bot.desc, fontWeight = FontWeight.SemiBold) + // schedule RowVCenter { val isScheduled = bot.enabled && bot.schedule != null @@ -79,11 +70,14 @@ fun BotCard( modifier = M.padding(start = 10.dp) ) } + } - // action icons - RowVCenterSpaced(2) { - bot.actions.forEach { it.Icon() } - } + // action icons + NonLazyGrid( + columns = 3, + itemCount = bot.actions.size, + ) { + bot.actions[it].Icon() } } } diff --git a/app/src/main/java/spam/blocker/ui/setting/bot/BotHeader.kt b/app/src/main/java/spam/blocker/ui/setting/bot/BotHeader.kt index cc28a413..51a4eb84 100644 --- a/app/src/main/java/spam/blocker/ui/setting/bot/BotHeader.kt +++ b/app/src/main/java/spam/blocker/ui/setting/bot/BotHeader.kt @@ -1,9 +1,11 @@ package spam.blocker.ui.setting.bot import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import spam.blocker.G import spam.blocker.R @@ -12,10 +14,10 @@ import spam.blocker.db.BotTable import spam.blocker.ui.setting.LabeledRow import spam.blocker.ui.theme.SkyBlue import spam.blocker.ui.widgets.DividerItem +import spam.blocker.ui.widgets.GreyIcon import spam.blocker.ui.widgets.LabelItem import spam.blocker.ui.widgets.MenuButton import spam.blocker.ui.widgets.Str -import spam.blocker.util.loge // The row: // "Number Rule" [Add] [Test] @@ -23,30 +25,46 @@ import spam.blocker.util.loge fun BotHeader() { val ctx = LocalContext.current + val initialBotToEdit = remember { mutableStateOf(Bot()) } val addTrigger = rememberSaveable { mutableStateOf(false) } - - var initial = remember { mutableStateOf(Bot()) } - if (addTrigger.value) { EditBotDialog( trigger = addTrigger, - initial = initial.value, + initial = initialBotToEdit.value, onSave = { newBot -> // 1. add to db BotTable.addNewRecord(ctx, newBot) // 2. reload UI - G.BotVM.reload(ctx) + G.botVM.reload(ctx) } ) } + val importTrigger = remember { mutableStateOf(false) } + if (importTrigger.value) { + BotImportExportDialog( + trigger = importTrigger, + initialText = "", + isExport = false + ) + } + val dropdownItems = remember { val ret = mutableListOf( - LabelItem(label = ctx.getString(R.string.customize)) { - initial.value = Bot() + LabelItem( + label = ctx.getString(R.string.customize), + icon = { GreyIcon(R.drawable.ic_note) } + ) { + initialBotToEdit.value = Bot() addTrigger.value = true }, + LabelItem( + label = ctx.getString(R.string.import_), + icon = { GreyIcon(R.drawable.ic_backup_import) } + ) { + importTrigger.value = true + }, DividerItem(), ) ret += BotPresets.map { preset -> @@ -55,7 +73,7 @@ fun BotHeader() { label = bot.desc, tooltip = ctx.getString(preset.tooltipId) ) { - initial.value = bot + initialBotToEdit.value = bot addTrigger.value = true } diff --git a/app/src/main/java/spam/blocker/ui/setting/bot/BotImportExportDialog.kt b/app/src/main/java/spam/blocker/ui/setting/bot/BotImportExportDialog.kt new file mode 100644 index 00000000..8ad68eac --- /dev/null +++ b/app/src/main/java/spam/blocker/ui/setting/bot/BotImportExportDialog.kt @@ -0,0 +1,106 @@ +package spam.blocker.ui.setting.bot + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import spam.blocker.G +import spam.blocker.R +import spam.blocker.db.Bot +import spam.blocker.service.bot.botJson +import spam.blocker.ui.theme.LocalPalette +import spam.blocker.ui.theme.Teal200 +import spam.blocker.ui.widgets.GreyLabel +import spam.blocker.ui.widgets.PopupDialog +import spam.blocker.ui.widgets.ResIcon +import spam.blocker.ui.widgets.Str +import spam.blocker.ui.widgets.StrInputBox +import spam.blocker.ui.widgets.StrokeButton +import spam.blocker.util.Clipboard +import spam.blocker.util.Launcher +import java.lang.Exception + +@Composable +fun BotImportExportDialog( + trigger: MutableState, + initialText: String, + isExport: Boolean, +) { + val ctx = LocalContext.current + + if (trigger.value) { + + var text by remember { mutableStateOf(initialText) } + + var succeeded by remember { mutableStateOf(false) } + + val resultTrigger = remember { mutableStateOf(false) } + PopupDialog( + trigger = resultTrigger, + icon = { + ResIcon( + iconId = if (succeeded) R.drawable.ic_check_green else R.drawable.ic_fail_red, + color = if (succeeded) LocalPalette.current.pass else LocalPalette.current.block, + ) + }, + content = { + Text( + Str( + if (succeeded) + R.string.imported_successfully + else + R.string.import_fail + ), + color = LocalPalette.current.textGrey, + fontWeight = FontWeight.SemiBold, + ) + } + ) + + PopupDialog( + trigger = trigger, + buttons = { + if (isExport) { // Export + StrokeButton( + label = Str(R.string.copy), + color = Teal200 + ) { + Clipboard.copy(ctx, text) + } + } else { // Import + StrokeButton( + label = Str(R.string.import_), + color = Teal200 + ) { + try { + val newBot = botJson.decodeFromString(text).copy( + id = 0, + enabled = false, + workUUID = null, + ) + G.botVM.list.add(newBot) + succeeded = true + } catch (e: Exception) { + succeeded = false + } + resultTrigger.value = true + } + } + } + ) { + StrInputBox( + label = { GreyLabel(Str(R.string.config_text)) }, + text = text, + maxLines = 20, + onValueChange = { + text = it + } + ) + } + } +} diff --git a/app/src/main/java/spam/blocker/ui/setting/bot/BotList.kt b/app/src/main/java/spam/blocker/ui/setting/bot/BotList.kt index 31718477..535a664e 100644 --- a/app/src/main/java/spam/blocker/ui/setting/bot/BotList.kt +++ b/app/src/main/java/spam/blocker/ui/setting/bot/BotList.kt @@ -16,21 +16,27 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import kotlinx.serialization.encodeToString import spam.blocker.G import spam.blocker.R -import spam.blocker.db.Bot import spam.blocker.db.BotTable +import spam.blocker.service.bot.botJson +import spam.blocker.service.bot.botPrettyJson import spam.blocker.ui.M import spam.blocker.ui.setting.regex.DisableNestedScrolling +import spam.blocker.ui.widgets.DropdownWrapper +import spam.blocker.ui.widgets.GreyIcon16 +import spam.blocker.ui.widgets.LabelItem import spam.blocker.ui.widgets.LeftDeleteSwipeWrapper import spam.blocker.ui.widgets.SnackBar import spam.blocker.ui.widgets.SwipeInfo +import spam.blocker.util.Clipboard @OptIn(ExperimentalFoundationApi::class) @Composable fun BotList() { val ctx = LocalContext.current - val vm = G.BotVM + val vm = G.botVM val coroutineScope = rememberCoroutineScope() val editTrigger = rememberSaveable { mutableStateOf(false) } @@ -50,42 +56,78 @@ fun BotList() { } ) } + + val exportTrigger = remember { mutableStateOf(false) } + if (exportTrigger.value) { + BotImportExportDialog( + trigger = exportTrigger, + isExport = true, + initialText = botPrettyJson.encodeToString(vm.list[clickedIndex]), + ) + } + + val menuLabels = listOf( + R.string.export + ) + val menuIcons = listOf( + R.drawable.ic_backup_export + ) + val contextMenuItems = menuLabels.mapIndexed { menuIndex, label -> + LabelItem( + label = ctx.getString(label), + icon = { GreyIcon16(menuIcons[menuIndex]) } + ) { + when (menuIndex) { + 0 -> { // export + exportTrigger.value = true + } + } + } + } + Column( modifier = M.nestedScroll(DisableNestedScrolling()), verticalArrangement = Arrangement.spacedBy(4.dp) ) { vm.list.forEachIndexed { index, bot -> key(bot.id) { - LeftDeleteSwipeWrapper( - left = SwipeInfo( - onSwipe = { - // 1. delete from db - BotTable.deleteById(ctx, bot.id) + DropdownWrapper(items = contextMenuItems) { contextMenuExpanded -> - // 2. remove from ArrayList - vm.list.removeAt(index) + LeftDeleteSwipeWrapper( + left = SwipeInfo( + onSwipe = { + // 1. delete from db + BotTable.deleteById(ctx, bot.id) - // 3. show snackbar - SnackBar.show( - coroutineScope, - bot.desc, - ctx.getString(R.string.undelete), - ) { - BotTable.addRecordWithId(ctx, bot) - vm.list.add(index, bot) + // 2. remove from ArrayList + vm.list.removeAt(index) + + // 3. show snackbar + SnackBar.show( + coroutineScope, + bot.desc, + ctx.getString(R.string.undelete), + ) { + BotTable.addRecordWithId(ctx, bot) + vm.list.add(index, bot) + } } - } - ) - ) { - BotCard( - bot, - modifier = M.combinedClickable( - onClick = { - clickedIndex = index - editTrigger.value = true - }, ) - ) + ) { + BotCard( + bot, + modifier = M.combinedClickable( + onClick = { + clickedIndex = index + editTrigger.value = true + }, + onLongClick = { + clickedIndex = index + contextMenuExpanded.value = true + } + ) + ) + } } } } diff --git a/app/src/main/java/spam/blocker/ui/setting/bot/BotPresets.kt b/app/src/main/java/spam/blocker/ui/setting/bot/BotPresets.kt index 7ca7cb94..42a075e5 100644 --- a/app/src/main/java/spam/blocker/ui/setting/bot/BotPresets.kt +++ b/app/src/main/java/spam/blocker/ui/setting/bot/BotPresets.kt @@ -40,7 +40,8 @@ val BotPresets = listOf( ), actions = listOf( HttpDownload(url = "https://www.ftc.gov/sites/default/files/DNC_Complaint_Numbers_{year}-{month}-{day}.csv"), - ParseCSV("{'Company_Phone_Number': 'pattern'}"), + ParseCSV(columnMapping = "{'Company_Phone_Number': 'pattern'}"), + // no need to add ClearNumber here ImportToSpamDB(), ) ) diff --git a/app/src/main/java/spam/blocker/ui/setting/bot/EditBotDialog.kt b/app/src/main/java/spam/blocker/ui/setting/bot/EditBotDialog.kt index 110efbfe..936711ba 100644 --- a/app/src/main/java/spam/blocker/ui/setting/bot/EditBotDialog.kt +++ b/app/src/main/java/spam/blocker/ui/setting/bot/EditBotDialog.kt @@ -113,6 +113,7 @@ fun EditBotDialog( bgColor = C.dialogBg ) { Column { + // Enabled switch box LabeledRow(R.string.enabled) { SwitchBox(checked = enabled, onCheckedChange = { isTurningOn -> if (isTurningOn && schedule.value == null) { diff --git a/app/src/main/java/spam/blocker/ui/setting/quick/SpamDB.kt b/app/src/main/java/spam/blocker/ui/setting/quick/SpamDB.kt index 7a1082b0..70e67928 100644 --- a/app/src/main/java/spam/blocker/ui/setting/quick/SpamDB.kt +++ b/app/src/main/java/spam/blocker/ui/setting/quick/SpamDB.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.width import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -14,6 +15,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp +import spam.blocker.Events import spam.blocker.R import spam.blocker.db.SpamTable import spam.blocker.service.bot.CleanupSpamDB @@ -61,6 +63,11 @@ fun SpamDB() { val popupTrigger = rememberSaveable { mutableStateOf(false) } var total by remember { mutableIntStateOf(SpamTable.count(ctx)) } + // Refresh UI on global events, such as workflow action AddToSpamDB and ClearSpamDB + LaunchedEffect(Events.spamDbUpdated.intValue) { + total = SpamTable.count(ctx) + } + // Clear All val deleteConfirm = remember { mutableStateOf(false) } PopupDialog( diff --git a/app/src/main/java/spam/blocker/ui/setting/regex/EditRuleDialog.kt b/app/src/main/java/spam/blocker/ui/setting/regex/EditRuleDialog.kt index 1c537f82..0d138bf1 100644 --- a/app/src/main/java/spam/blocker/ui/setting/regex/EditRuleDialog.kt +++ b/app/src/main/java/spam/blocker/ui/setting/regex/EditRuleDialog.kt @@ -66,11 +66,11 @@ import spam.blocker.ui.widgets.TimeRangePicker import spam.blocker.ui.widgets.WeekdayPicker1 import spam.blocker.util.Lambda1 import spam.blocker.util.NormalPermission -import spam.blocker.util.PermissionChain import spam.blocker.util.TimeSchedule import spam.blocker.util.Util import spam.blocker.util.addFlag import spam.blocker.util.hasFlag +import spam.blocker.util.loge import spam.blocker.util.removeFlag import spam.blocker.util.setFlag @@ -332,6 +332,9 @@ fun RuleEditDialog( patternError = hasErr pattern = newVal }, + onFlagsChange = { + patternFlags.intValue = it + }, leadingIcon = if (forType == Def.ForNumber) { { RegexLeadingDropdownIcon(patternFlags) } } else { @@ -361,6 +364,9 @@ fun RuleEditDialog( patternExtraError = hasErr patternExtra = newValue }, + onFlagsChange = { + patternExtraFlags.intValue = it + }, leadingIcon = { RegexLeadingDropdownIcon(patternExtraFlags) } ) } diff --git a/app/src/main/java/spam/blocker/ui/setting/regex/RuleList.kt b/app/src/main/java/spam/blocker/ui/setting/regex/RuleList.kt index aba39d7c..c2d22bb5 100644 --- a/app/src/main/java/spam/blocker/ui/setting/regex/RuleList.kt +++ b/app/src/main/java/spam/blocker/ui/setting/regex/RuleList.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf @@ -28,9 +29,12 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.coroutines.CoroutineScope +import spam.blocker.Events import spam.blocker.R import spam.blocker.db.RegexRule +import spam.blocker.db.SpamTable import spam.blocker.db.ruleTableForType +import spam.blocker.def.Def import spam.blocker.ui.M import spam.blocker.ui.theme.LocalPalette import spam.blocker.ui.theme.Salmon @@ -73,6 +77,14 @@ fun RuleList( val editRuleTrigger = rememberSaveable { mutableStateOf(false) } val clickedRule = remember { mutableStateOf(RegexRule()) } + // Refresh UI on global events, such as workflow action AddToRegexRule + if (forType == Def.ForNumber) { + LaunchedEffect(Events.regexRuleUpdated.intValue) { + vm.reload(ctx) + } + } + + if (editRuleTrigger.value) { RuleEditDialog( trigger = editRuleTrigger, @@ -135,7 +147,7 @@ fun RuleList( } } LazyColumn(modifier = M.heightIn(max = halfScreenDp.dp)) { - items(duplicatedRules, key = {it.id}) { + items(duplicatedRules, key = { it.id }) { RuleCard(rule = it, forType = forType) } } diff --git a/app/src/main/java/spam/blocker/ui/widgets/BottomNavView.kt b/app/src/main/java/spam/blocker/ui/widgets/BottomNavView.kt index bcd9fead..13c6ec6b 100644 --- a/app/src/main/java/spam/blocker/ui/widgets/BottomNavView.kt +++ b/app/src/main/java/spam/blocker/ui/widgets/BottomNavView.kt @@ -123,7 +123,7 @@ fun BottomBar(vm: BottomBarViewModel) { modifier = M.fillMaxSize(), horizontalArrangement = Arrangement.Center ) { - // 3 tabs + // 3 tab items vm.tabItems.forEach { tab -> Surface( // for the round clicking ripple shape = RoundedCornerShape(30.dp), diff --git a/app/src/main/java/spam/blocker/ui/widgets/InputBox.kt b/app/src/main/java/spam/blocker/ui/widgets/InputBox.kt index 763ee875..d59b9dc2 100644 --- a/app/src/main/java/spam/blocker/ui/widgets/InputBox.kt +++ b/app/src/main/java/spam/blocker/ui/widgets/InputBox.kt @@ -46,6 +46,7 @@ import spam.blocker.ui.theme.LocalPalette import spam.blocker.ui.theme.Orange import spam.blocker.ui.theme.Salmon import spam.blocker.ui.theme.SkyBlue +import spam.blocker.util.Lambda1 import spam.blocker.util.Lambda2 import spam.blocker.util.Util import spam.blocker.util.hasFlag @@ -353,7 +354,8 @@ fun StrInputBox( fun RegexInputBox( regexStr: String, onRegexStrChange: Lambda2, - regexFlags: MutableIntState, + regexFlags: MutableIntState,// don't replace this type to Int, it'll cause bug, not sure why + onFlagsChange: Lambda1, modifier: Modifier = Modifier, label: @Composable (() -> Unit)? = null, placeholder: @Composable (() -> Unit)? = null, @@ -452,7 +454,7 @@ fun RegexInputBox( 1 -> hasD.value = checked 2 -> hasR.value = checked } - regexFlags.intValue = regexFlags.intValue.setFlag( + val newVal = regexFlags.intValue.setFlag( when (idx) { 0 -> Def.FLAG_REGEX_IGNORE_CASE 1 -> Def.FLAG_REGEX_DOT_MATCH_ALL @@ -460,6 +462,7 @@ fun RegexInputBox( }, checked ) + onFlagsChange(newVal) }, ) } diff --git a/app/src/main/java/spam/blocker/ui/widgets/NonLazyGrid.kt b/app/src/main/java/spam/blocker/ui/widgets/NonLazyGrid.kt new file mode 100644 index 00000000..cbcab3ad --- /dev/null +++ b/app/src/main/java/spam/blocker/ui/widgets/NonLazyGrid.kt @@ -0,0 +1,41 @@ +package spam.blocker.ui.widgets + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + + +// For the Bot icons. +// LazyGrid can't be used in scrollable. +// source: https://dev.to/maaxgr/jetpack-compose-grid-without-lazy-5gb8 +@Composable +fun NonLazyGrid( + columns: Int, + itemCount: Int, + modifier: Modifier = Modifier, + content: @Composable() (Int) -> Unit +) { + Column(modifier = modifier) { + var rows = (itemCount / columns) + if (itemCount.mod(columns) > 0) { + rows += 1 + } + + for (rowId in 0 until rows) { + val firstIndex = rowId * columns + + Row { + for (columnId in 0 until columns) { + val index = firstIndex + columnId + Box { + if (index < itemCount) { + content(index) + } + } + } + } + } + } +} diff --git a/app/src/main/java/spam/blocker/util/Util.kt b/app/src/main/java/spam/blocker/util/Util.kt index 0b48dabf..5b0ec1aa 100644 --- a/app/src/main/java/spam/blocker/util/Util.kt +++ b/app/src/main/java/spam/blocker/util/Util.kt @@ -134,11 +134,13 @@ object Util { } // check if a string only contains: - // digits spaces + - ( ) + // digit spaces + - ( ) + // Param: + // when `force`==true, numbers like "+123####" will also be cleared. val pattern = "^[0-9\\s+\\-()]*\$".toRegex() - fun clearNumber(number: String): String { + fun clearNumber(number: String, force: Boolean = false): String { // check if it contains alphabetical characters like "Microsoft" - if (!pattern.matches(number)) { // don't clear for enterprise string number + if (!force && !pattern.matches(number)) { // don't clear for enterprise string number return number } diff --git a/app/src/main/java/spam/blocker/util/Xml.kt b/app/src/main/java/spam/blocker/util/Xml.kt new file mode 100644 index 00000000..a0356f7d --- /dev/null +++ b/app/src/main/java/spam/blocker/util/Xml.kt @@ -0,0 +1,41 @@ +package spam.blocker.util + +import org.w3c.dom.Document +import org.w3c.dom.NodeList +import spam.blocker.db.RegexRule +import java.io.ByteArrayInputStream +import javax.xml.parsers.DocumentBuilderFactory +import javax.xml.xpath.XPathConstants +import javax.xml.xpath.XPathFactory + + +object Xml { + fun parse( + bytes: ByteArray, + expression: String, + ): List> { + val document = parseXml(bytes) + val xPath = XPathFactory.newInstance().newXPath() + + val nodes = + xPath.evaluate(expression, document, XPathConstants.NODESET) as NodeList + + val list = mutableListOf>() + + for (i in 0 until nodes.length) { + list.add( + mapOf( + "pattern" to nodes.item(i).textContent + ) + ) + } + return list + } + + private fun parseXml(bytes: ByteArray): Document { + val documentBuilderFactory = DocumentBuilderFactory.newInstance() + val documentBuilder = documentBuilderFactory.newDocumentBuilder() + val inputStream = ByteArrayInputStream(bytes) + return documentBuilder.parse(inputStream) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_backup_import.xml b/app/src/main/res/drawable/ic_backup_import.xml index 27a8dd0f..525f8535 100644 --- a/app/src/main/res/drawable/ic_backup_import.xml +++ b/app/src/main/res/drawable/ic_backup_import.xml @@ -5,10 +5,10 @@ android:viewportHeight="36"> + android:scaleX="0.9" + android:scaleY="0.9" + android:translateX="2" + android:translateY="2"> - - - - - - + + diff --git a/app/src/main/res/drawable/ic_replace.xml b/app/src/main/res/drawable/ic_replace.xml new file mode 100644 index 00000000..07249ca3 --- /dev/null +++ b/app/src/main/res/drawable/ic_replace.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_xml.xml b/app/src/main/res/drawable/ic_xml.xml new file mode 100644 index 00000000..8477bbd5 --- /dev/null +++ b/app/src/main/res/drawable/ic_xml.xml @@ -0,0 +1,20 @@ + + + + diff --git a/app/src/main/res/values-de/strings_8.xml b/app/src/main/res/values-de/strings_8.xml index 9527536d..fa5eced2 100644 --- a/app/src/main/res/values-de/strings_8.xml +++ b/app/src/main/res/values-de/strings_8.xml @@ -1,99 +1,112 @@ Datenbank Gesamt + Von + Zu + Nummern in dieser Datenbank werden blockiert.

- Du kannst jede öffentliche Datenbank oder sogar mehrere Datenbanken einbinden. Du kannst auch Nummern aus lokalen Dateien importieren.
+ Sie können jede öffentliche Datenbank oder sogar mehrere Datenbanken integrieren. Sie können auch Nummern aus lokalen Dateien importieren.

- Im Abschnitt "Automatisierung" unten findest du Anweisungen zum Einplanen eines automatischen Importauftrags.
+ Überprüfen Sie den Abschnitt "Automatisierung" unten, um zu erfahren, wie Sie eine automatische Importaufgabe planen.

- Gilt sowohl für Anrufe als auch SMS-Nachrichten.
+ Es gilt sowohl für Anrufe als auch für SMS-Nachrichten.

Priorität: 0 ]]>
- Spam-Nummern in der Datenbank verfallen nach N Tagen, abgelaufene Einträge werden automatisch gelöscht. + Spam-Nummern in der Datenbank laufen nach N Tagen ab, abgelaufene Datensätze werden automatisch gelöscht. Anpassen - Workflow - Workflows + Arbeitsablauf + Arbeitsabläufe Automatisierung + Arbeitsabläufe können verwendet werden, um Aufgaben automatisch auszuführen, wie z. B. das Aktualisieren der Datenbank oder das automatische Backup.

- Es gibt Voreinstellungen für eine schnelle Einrichtung, du kannst sie aber auch von Grund auf neu erstellen.
+ Es gibt Voreinstellungen für eine schnelle Einrichtung, Sie können sie aber auch von Grund auf neu erstellen oder aus einer JSON-Konfiguration importieren, die von anderen Benutzern freigegeben wurde.

- Wenn du einen Workflow findest, der weit verbreitet ist und zu den Voreinstellungen hinzugefügt werden sollte, melde ihn bitte auf Github. + Wenn Sie einen Arbeitsablauf finden, der weit verbreitet werden könnte und den Voreinstellungen hinzugefügt werden sollte, melden Sie dies bitte auf Github. ]]>
Manuell - - -
- Wenn du einen Workflow findest, der weit verbreitet ist und zu den Voreinstellungen hinzugefügt werden sollte, melde ihn bitte. - ]]> -
FTC - Do Not Call - Spam-Datenbank-Bereinigung - Verlaufsbereinigung + Bereinigung der Spam-Datenbank + Bereinigung des Verlaufs Automatisches Backup Verlauf bereinigen HTTP-Download - Spam-Datenbank bereinigen + Spam-DB bereinigen Backup-Export Backup-Import Datei lesen Datei schreiben - CSV-Datei parsen - In Spam-Datenbank importieren + CSV parsen + XML parsen + In die Spam-Datenbank importieren Als Regex-Regel importieren + Nummer ersetzen Inhaltsbytes herunterladen ]]> - Abgelaufene Nummern aus der Spam-Datenbank löschen. - Abgelaufene Verlaufseinträge löschen. + Abgelaufene Nummern aus der Spam-Datenbank löschen. + Abgelaufene Verlaufsdatensätze löschen. Inhaltsbytes exportieren. + Exportieren Sie die aktuellen Einstellungen in Inhaltsbytes. ]]> Inhaltsbytes anwenden. + Anwenden der Einstellungen aus Inhaltsbytes. ]]> Inhaltsbytes aus einer Datei beziehen. + Rufen Sie die Inhaltsbytes aus einer Datei ab. ]]> Inhaltsbytes in eine Datei schreiben. + Schreiben Sie Inhaltsbytes in eine Datei. ]]> Inhaltsbytes in eine Liste von Nummern parsen + CSV-Inhaltsbytes in eine Liste von Nummern parsen + ]]> + + + Inhaltsbytes mit xpath parsen und eine Liste von Nummern generieren. ]]> Liste von Nummern zur Spam-Datenbank hinzufügen. + Fügen Sie eine Liste von Nummern zur Spam-Datenbank hinzu. ]]> Liste von Nummern generieren. + Generieren Sie eine neue Regex-Regel aus einer Liste von Nummern. + ]]> + + + Liste der Nummern in das von dieser App verwendete Format.
+
+ Fügen Sie beispielsweise zwei Aktionen hinzu, um Folgendes zu ersetzen:
+ `+12345####` bis `12345....`:
+
+   - Ersetzen Sie `(+)` durch ``
+   - Ersetzen Sie `(#)` durch `.`
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-es/strings_8.xml b/app/src/main/res/values-es/strings_8.xml index e91e2809..3268dbbe 100644 --- a/app/src/main/res/values-es/strings_8.xml +++ b/app/src/main/res/values-es/strings_8.xml @@ -1,99 +1,112 @@ Base de datos Total + De + A + Los números incluidos en esta base de datos se bloquearán.

- Se puede integrar cualquier base de datos pública, o incluso varias bases de datos, también se pueden importar números de archivos locales.
+ Puedes integrar cualquier base de datos pública, o incluso varias bases de datos. También puedes importar números desde archivos locales.

- Consulte la sección "Automatización" a continuación para saber cómo programar una tarea de importación automática.
+ Consulta la sección "Automatización" para programar una tarea de importación automática.

Se aplica tanto a llamadas como a mensajes SMS.

Prioridad: 0 ]]>
- Los números de spam de la base de datos caducarán después de N días, los registros caducados se eliminarán automáticamente. + Los números de spam en la base de datos caducarán después de N días; los registros caducados se eliminarán automáticamente. Personalizar Flujo de trabajo Flujos de trabajo Automatización + Los flujos de trabajo se pueden usar para ejecutar tareas automáticamente, como actualizar la base de datos o realizar copias de seguridad automáticas.

- Hay ajustes preestablecidos para una configuración rápida, también se pueden crear desde cero.
+ Hay ajustes prestablecidos para una configuración rápida. También puedes crearlos desde cero o importarlos desde una configuración json compartida por otros usuarios.

- Si encuentra algún flujo de trabajo que podría usarse ampliamente y debería agregarse a los ajustes preestablecidos, infórmelo en Github. + Si encuentras un flujo de trabajo que podría usarse ampliamente y debería agregarse a los ajustes preestablecidos, infórmalo en github. ]]>
Manual - - -
- Si encuentra algún flujo de trabajo que pueda usarse ampliamente y deba agregarse a los ajustes preestablecidos, infórmelo. - ]]> -
FTC - No llamar - Limpieza de la base de datos de spam - Limpieza del historial - Respaldo automático + Limpieza de base de datos de spam + Limpieza de historial + Copia de seguridad automática Limpiar historial Descarga HTTP Limpiar base de datos de spam - Exportar respaldo - Importar respaldo + Exportar copia de seguridad + Importar copia de seguridad Leer archivo Escribir archivo Analizar CSV + Analizar XML Importar a la base de datos de spam - Importar como regla de expresiones regulares + Importar como regla de expresión regular + Reemplazar número bytes de contenido + Descarga de archivos HTTP como bytes de contenido ]]> - Eliminar números caducados de la base de datos de spam. - Eliminar registros de historial caducados. + Elimina los números caducados de la base de datos de spam. + Elimina los registros del historial caducados. bytes de contenido. + Exporta la configuración actual a bytes de contenido. ]]> bytes de contenido. + Aplica la configuración desde bytes de contenido. ]]> bytes de contenido de un archivo. + Obtén los bytes de contenido de un archivo. ]]> bytes de contenido en un archivo. + Escribe bytes de contenido en un archivo. ]]> bytes de contenido en una lista de números + Analiza los bytes de contenido csv a una lista de números + ]]> + + + bytes de contenido xml mediante xpath y genera una lista de números. ]]> lista de números a la base de datos de spam. + Agrega una lista de números a la base de datos de spam. ]]> lista de números. + Genera una nueva regla de expresión regular desde una lista de números. + ]]> + + + lista de números al formato que utiliza esta aplicación.
+
+ Por ejemplo, añade dos acciones para reemplazar:
+ `+12345####` a `12345....`:
+
+   - Reemplaza `(+)` por ``
+   - Reemplaza `(#)` por `.`
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_8.xml b/app/src/main/res/values-fr/strings_8.xml index 88ded253..d6e1cdf3 100644 --- a/app/src/main/res/values-fr/strings_8.xml +++ b/app/src/main/res/values-fr/strings_8.xml @@ -1,99 +1,112 @@ Base de données Total + De + À + Les numéros de cette base de données seront bloqués.

- Vous pouvez intégrer n\'importe quelle base de données publique, ou même plusieurs bases de données, vous pouvez également importer des numéros à partir de fichiers locaux.
+ Vous pouvez intégrer n\'importe quelle base de données publique, ou même plusieurs bases de données, vous pouvez également importer des numéros à partir de fichiers locaux.

Consultez la section "Automatisation" ci-dessous pour savoir comment planifier une tâche d\'importation automatique.

- Elle s\'applique à la fois aux appels et aux SMS.
+ Cela s\'applique à la fois aux appels et aux SMS.

- Priorité : 0 + Priorité : 0 ]]>
- Les numéros de spam de la base de données expireront après N jours, et les enregistrements expirés seront supprimés automatiquement. + Les numéros de spam dans la base de données expireront après N jours, les enregistrements expirés seront automatiquement supprimés. Personnaliser Flux de travail Flux de travail Automatisation + Les flux de travail peuvent être utilisés pour exécuter des tâches automatiquement, telles que la mise à jour de la base de données ou la sauvegarde automatique.

- Il existe des préréglages pour une configuration rapide, vous pouvez également les créer à partir de zéro.
+ Il existe des préréglages pour une configuration rapide, vous pouvez également le créer à partir de zéro ou l\'importer à partir d\'une configuration json partagée par d\'autres utilisateurs.

Si vous trouvez un flux de travail qui pourrait être largement utilisé et qui devrait être ajouté aux préréglages, veuillez le signaler sur github. ]]>
- Manuelle - - -
- Si vous trouvez un flux de travail qui peut être largement utilisé et qui doit être ajouté aux préréglages, veuillez le signaler. - ]]> -
+ Manuel - FTC - Ne pas appeler - Nettoyage de la base de données de spam + FTC - Do Not Call + Nettoyage de la base de données anti-spam Nettoyage de l\'historique Sauvegarde automatique Nettoyer l\'historique Téléchargement HTTP - Nettoyer la base de données de spam - Sauvegarde/exportation - Sauvegarde/importation - Lire fichier - Écrire fichier - Parser CSV - Importer dans la base de données de spam - Importer en tant que règle d\'expression régulière + Nettoyer la base de données anti-spam + Exportation de sauvegarde + Importation de sauvegarde + Lire le fichier + Écrire un fichier + Analyser un fichier CSV + Analyser un fichier XML + Importer dans la base de données anti-spam + Importer en tant que règle d\'expression rationnelle + Remplacer le numéro octets de contenu + Fichier de téléchargement HTTP sous forme d\'octets de contenu ]]> - Supprimer les numéros expirés de la base de données de spam. - Supprimer les enregistrements d\'historique expirés. + Supprime les numéros expirés de la base de données anti-spam. + Supprime les enregistrements d\'historique expirés. octets de contenu. + Exporte les paramètres actuels vers des octets de contenu. ]]> octets de contenu. + Applique les paramètres à partir d\'octets de contenu. ]]> octets de contenu à partir d\'un fichier. + Obtient les octets de contenu d\'un fichier. ]]> octets de contenu dans un fichier. + Écrit des octets de contenu dans un fichier. ]]> octets de contenu en liste de numéros + Analyse les octets de contenu du fichier CSV pour obtenir une liste de numéros + ]]> + + + octets de contenu du fichier XML en utilisant xpath et génère une liste de numéros. ]]> liste de numéros à la base de données de spam. + Ajoute une liste de numéros à la base de données anti-spam. ]]> liste de numéros. + Génère une nouvelle règle d\'expression rationnelle à partir d\'une liste de numéros. + ]]> + + + liste de numéros au format utilisé par cette application.
+
+ Par exemple, ajoutez deux actions pour remplacer :
+ `+12345####` par `12345....` :
+
+   - Remplacer `(+)` par ``
+   - Remplacer `(#)` par `.`
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-gal/strings_8.xml b/app/src/main/res/values-gal/strings_8.xml index c5fdf06b..0e5b5ce9 100644 --- a/app/src/main/res/values-gal/strings_8.xml +++ b/app/src/main/res/values-gal/strings_8.xml @@ -1,65 +1,50 @@ Base de datos Total + De + A -
- Podes integrar calquera base de datos pública, ou mesmo varias bases de datos, tamén podes importar números de ficheiros locais.
-
- Consulta a sección "Automatización" a continuación para saber como programar unha tarefa de importación automática.
-
- Aplícase ás chamadas e ás mensaxes SMS.
-
- Prioridade: 0 + Os números nesta base de datos serán bloqueados.

Podes integrar calquera base de datos pública, ou mesmo múltiples bases de datos, tamén podes importar números de ficheiros locais.

Comproba a sección "Automatización" de abaixo para ver como programar unha tarefa de importación automática.

Resulta válido tanto para chamadas coma para mensaxes SMS.

Prioridade: 0 ]]>
- Os números de spam da base de datos caducarán despois de N días, os rexistros caducados serán eliminados automaticamente. + Os números de spam na base de datos caducarán despois de N días, e os rexistros caducados serán borrados automaticamente. Personalizar Fluxo de traballo Fluxos de traballo Automatización -
- Hai predefinidos para unha configuración rápida, tamén podes construílos desde cero.
-
- Se atopas algún fluxo de traballo que se poida usar amplamente e se deba engadir aos predefinidos, informa diso en github. + Os fluxos de traballo poden ser usados para executar tarefas automaticamente, como actualizar a base de datos ou realizar unha copia de seguridade automática.

Hai axustes preestablecidos para unha configuración rápida, tamén podes construílo dende cero ou importar dende unha configuración json compartida por outros usuarios.

Se atopas calquera fluxo de traballo que poida ser moi empregado e que debería ser engadido aos axustes preestablecidos, informa diso en github. ]]>
Manual - - -
- Se atopas algún fluxo de traballo que se poida usar amplamente e se deba engadir aos predefinidos, informa. - ]]> -
FTC - Do Not Call Limpeza da base de datos de spam Limpeza do historial Copia de seguridade automática - Limpar historial + Limpar o historial Descarga HTTP - Limpar a base de datos de spam - Exportación da copia de seguridade - Importación da copia de seguridade + Limpar a BD de spam + Exportar copia de seguridade + Importar copia de seguridade Ler ficheiro Escribir ficheiro Analizar CSV - Importar á base de datos de spam - Importar como regra Regex + Analizar XML + Importar a base de datos de spam + Importar como regra de expresión regular + Substituír números bytes de contido + HTTP descarga un ficheiro como bytes de contido ]]> - Borrar números caducados da base de datos de spam. + Borrar números caducados da base de datos de spam. Borrar rexistros do historial caducados. bytes de contido. + Aplicar axustes dende bytes de contido. ]]> @@ -83,17 +68,28 @@ bytes de contido nun lista de números + Analizar csv bytes de contido a unha lista de números + ]]> + + + bytes de contido usando xpath e xerar unha lista de números. ]]> lista de números á base de datos de spam. + Engadir unha lista de números a base de datos de spam. ]]> lista de números. + Xerar unha nova regra de expresión regular dende unha lista de números. + ]]> + + + lista de números ao formato que usa esta aplicación.

Por exemplo, engadir dúas accións para substituír:
+ `+12345####` a `12345....`:

  - Substituír `(+)` a ``
  - Substituír `(#)` a `.`
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-ja/strings_8.xml b/app/src/main/res/values-ja/strings_8.xml index 3e6e5136..1a7d5a36 100644 --- a/app/src/main/res/values-ja/strings_8.xml +++ b/app/src/main/res/values-ja/strings_8.xml @@ -1,62 +1,59 @@ データベース 合計 + から + + このデータベースの番号はブロックされます。

- パブリックデータベースや複数のデータベースを統合できます。また、ローカルファイルから番号をインポートすることもできます。
+ パブリックデータベースまたは複数のデータベースを統合することもできます。また、ローカルファイルから番号をインポートすることもできます。

- 自動インポートタスクのスケジュール設定方法は、以下の「自動化」セクションを参照してください。
+ 自動インポートタスクをスケジュールする方法については、下記の「自動化」セクションで確認してください。

- 通話と SMS メッセージの両方に適用されます。
+ 通話とSMSメッセージの両方に適用されます。

優先度: 0 ]]>
- データベース内のスパム番号は N 日後に期限切れになり、期限切れのレコードは自動的に削除されます。 + データベース内のスパム番号はN日後に期限切れになり、有効期限が切れたレコードは自動的に削除されます。 カスタマイズ ワークフロー ワークフロー 自動化 + ワークフローを使用して、データベースの更新、または自動バックアップなどのタスクを自動的に実行できます。

- クイックセットアップ用のプリセットがありますが、最初から作成することもできます。
+ すばやく設定するためのプリセットがあり、最初から作成したり、他のユーザーから共有されたjson設定からインポートしたりすることもできます。

- 広く使用でき、プリセットに追加する必要があるワークフローを見つけた場合は、github で報告してください。 - ]]> -
- 手動 - - -
- 広く使用でき、プリセットに追加する必要があるワークフローを見つけた場合は、報告してください。 + 広く使用でき、プリセットに追加する必要があるワークフローを見つけた場合は、githubで報告してください。 ]]>
+ マニュアル FTC - Do Not Call - スパムデータベースのクリーニング - 履歴のクリーニング + スパムデータベースのクリーンアップ + 履歴のクリーンアップ 自動バックアップ - 履歴のクリーニング - HTTP ダウンロード - スパム DB のクリーニング - バックアップのエクスポート - バックアップのインポート + 履歴のクリーンアップ + HTTPダウンロード + スパムデータベースのクリーンアップ + バックアップエクスポート + バックアップインポート ファイルの読み取り - ファイルの書き込み - CSV の解析 + ファイルへの書き込み + CSVのパース + XMLのパース スパムデータベースにインポート 正規表現ルールとしてインポート + 番号の置換 コンテンツバイトとして + コンテンツバイトとしてファイルをHTTPダウンロードします ]]> スパムデータベースから期限切れの番号を削除します。 @@ -73,7 +70,7 @@ コンテンツバイトをファイルから取得します。 + ファイルからコンテンツバイトを取得します。 ]]> @@ -83,17 +80,33 @@ コンテンツバイトを番号リストに解析します + CSVコンテンツバイト数値リストにパースします + ]]> + + + xpathを使用してXMLコンテンツバイトを解析し、数値リストを生成します。 ]]> 番号リストを追加します。 + スパムデータベースに数値リストを追加します。 ]]> 番号リストから新しい正規表現ルールを生成します。 + 数値リストから新しい正規表現ルールを生成します。 + ]]> + + + 数値リストをこのアプリが使用する形式に変換します。
+
+ たとえば、次の置換を行うアクションを2つ追加します:
+ `+12345####` から `12345....`:
+
+   - `(+)` を `` に置換
+   - `(#)` を `.` に置換
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-pt-rBR/strings_8.xml b/app/src/main/res/values-pt-rBR/strings_8.xml index b0952e2d..d67b8ff9 100644 --- a/app/src/main/res/values-pt-rBR/strings_8.xml +++ b/app/src/main/res/values-pt-rBR/strings_8.xml @@ -1,66 +1,63 @@ Banco de dados Total + De + Para + Números nesse banco de dados serão bloqueados.

- Você pode integrar qualquer banco de dados público ou até mesmo vários bancos de dados, você também pode importar números de arquivos locais.
+ Você pode integrar qualquer banco de dados público, ou até mesmo múltiplos bancos de dados, você também pode importar números de arquivos locais.

- Verifique a seção "Automação" abaixo para saber como agendar uma tarefa de importação automática.
+ Verifique a seção de "Automação" abaixo sobre como agendar uma tarefa de auto-importação.

- Aplica-se a chamadas e mensagens SMS.
+ Isso se aplica tanto a chamadas quanto a mensagens SMS.

Prioridade: 0 ]]>
- Números de spam no banco de dados expiram após N dias, registros expirados serão excluídos automaticamente. + Números de spam no banco de dados expirarão após N dias, registros expirados serão deletados automaticamente. Personalizar - Fluxo de trabalho - Fluxos de trabalho + Fluxo de Trabalho + Fluxos de Trabalho Automação + Os fluxos de trabalho podem ser usados para executar tarefas automaticamente, tais como atualizar banco de dados, ou auto backup.

- Há predefinições para uma configuração rápida, você também pode construí-lo do zero.
+ Há predefinições para uma configuração rápida, você também pode construir do zero, ou importar de uma configuração json compartilhada de outros usuários.

- Se você encontrar algum fluxo de trabalho que possa ser amplamente usado e deva ser adicionado às predefinições, relate no github. + Se você encontrar um fluxo de trabalho que possa ser amplamente usado e que deveria ser adicionado as predefinições, favor reporte no Github. ]]>
Manual - - -
- Se você encontrar algum fluxo de trabalho que possa ser amplamente usado e deva ser adicionado às predefinições, relate. - ]]> -
FTC - Do Not Call - Limpeza do banco de dados de spam - Limpeza do histórico - Backup automático + Limpeza do Banco de Dados de Spam + Limpeza do Histórico + Auto Backup - Limpar histórico + Limpar Histórico Download HTTP - Limpar banco de dados de spam - Exportar backup - Importar backup - Ler arquivo - Gravar arquivo - Analisar CSV - Importar para banco de dados de spam - Importar como regra Regex + Limpar Banco de Dados de Spam + Exportar Backup + Importar Backup + Ler Arquivo + Escrever Arquivo + Converter CSV + Converter XML + Importar para o Banco de Dados de Spam + Importar como Regra Regex + Substituir Número bytes de conteúdo + Baixar arquivo HTTP como bytes de conteúdo ]]> - Excluir números expirados do banco de dados de spam. - Excluir registros de histórico expirados. + Apagar números expirados do banco de dados de spam. + Apagar registros de histórico expirados. bytes de conteúdo. @@ -78,12 +75,17 @@ bytes de conteúdo em um arquivo. + Escrever bytes de conteúdo para um arquivo. ]]> bytes de conteúdo para uma lista de números + Converter bytes de conteúdo CSV para uma lista de números + ]]> + + + xpath e gerar uma lista de números ]]> @@ -96,4 +98,15 @@ Gerar uma nova regra regex a partir de uma lista de números. ]]> + + lista de números para o formato que o aplicativo usa.
+
+ Por exemplo, adicione duas ações para substituir:
+ `+12345####` para `12345....`:
+
+   - Substituir `(+)` para ``
+   - Substituir `(#)` para `.`
+ ]]> +
\ No newline at end of file diff --git a/app/src/main/res/values-ru/strings_8.xml b/app/src/main/res/values-ru/strings_8.xml index feb545b4..b2a449a0 100644 --- a/app/src/main/res/values-ru/strings_8.xml +++ b/app/src/main/res/values-ru/strings_8.xml @@ -1,99 +1,100 @@ База данных - Общее - + Всего + От + К -
- Вы можете интегрировать любую общедоступную базу данных или даже несколько баз данных, а также импортировать номера из локальных файлов.
-
- Ознакомьтесь с разделом "Автоматизация" ниже, чтобы узнать, как запланировать задачу автоматического импорта.
-
- Применяется как к вызовам, так и к SMS-сообщениям.
-
+ Номера в этой базе данных будут заблокированы.

+ Можете интегрировать любую публичную базу данных или даже несколько баз данных, также можете импортировать номера из локальных файлов.

+ Проверьте раздел "Автоматизация" ниже, чтобы узнать, как запланировать задачу автоматического импорта.

+ Применяется как к звонкам, так и к SMS сообщениям.

Приоритет: 0 ]]>
- Ск срок действия спам-номеров в базе данных истекает через N дней, устаревшие записи будут удалены автоматически. + Спам номера в базе данных истекут через N дней, истекшие записи будут удалены автоматически. Настроить Рабочий процесс Рабочие процессы Автоматизация -
- Имеются пресеты для быстрой настройки, а также их можно создать с нуля.
-
- Если вы обнаружите какой-либо рабочий процесс, который можно широко использовать и который следует добавить в пресеты, сообщите об этом на github. + Рабочие процессы могут использоваться для автоматического выполнения задач, таких как обновление базы данных или автоматическое резервное копирования.

+ Есть предустановки для быстрой настройки, также можете создать их с нуля или импортировать из JSON конфигурации, обменятся другими пользователями.

+ Если найдете какой-либо рабочий процесс, который может быть широко использован и должен быть добавлен в предустановки, сообщите об этом на Github. ]]>
Вручную - - -
- Если вы обнаружите какие-либо рабочие процессы, которые можно широко использовать и которые следует добавить в пресеты, сообщите об этом. - ]]> -
- FTC - Do Not Call Очистка базы данных спама Очистка истории Автоматическое резервное копирование - Очистить историю - Загрузка по HTTP - Очистка базы спама + HTTP Скачать + Очистить базу данных спама Экспорт резервной копии Импорт резервной копии - Чтение файла - Запись в файл - Парсинг CSV - Импорт в базу спама - Импорт как правило регулярного выражения - + Читать файл + Записать файл + Разобрать CSV + Разобрать XML + Импортировать в базу данных спама + Импортировать как правило регулярных выражений + Заменить номер байты содержимого + HTTP скачать файл как байты контента. ]]> - Удалить просроченные номера из базы спама. - Удалите просроченные записи истории. + Удалить истекшие номера из базы данных спама. + Удалить просроченные записи истории. байты содержимого. + Экспортировать текущие настройки в байты содержимого. ]]> байтов содержимого. + Применить настройки из байтов содержимого. ]]> байты содержимого из файла. + Получить байты контента из файла. ]]> байты содержимого в файл. + Записать байты контента в файл. ]]> байтов содержимого в список номеров + Разобрать байты содержания CSV на список номеров. + ]]> + + + список номеров в базу спама. + Добавить список номеров в базу данных спама. ]]> списка номеров. + Создать новое правило регулярного выражения из списка номеров. + ]]> + + +
+ Например, добавьте два действия для замены:
+ `+12345####` на `12345....`:

+   - Заменить `(+)` на ``
+   - Заменить `(#)` на `.`
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-uk/strings_8.xml b/app/src/main/res/values-uk/strings_8.xml index 239da9f2..45d156f5 100644 --- a/app/src/main/res/values-uk/strings_8.xml +++ b/app/src/main/res/values-uk/strings_8.xml @@ -1,89 +1,91 @@ База даних - Разом + Загалом + Від + До + Numbers у цій базі даних будуть заблоковані.

- Ви можете додати будь-яку публічну базу даних або навіть декілька, а також імпортувати номери з локальних файлів.
+ Ви можете під\'єднати будь-яку публічну базу даних або навіть декілька баз даних, ви також можете імпортувати numbers з локальних файлів.

- Перегляньте розділ "Автоматизація" нижче для отримання інформації про те, як запланувати завдання автоматичного імпорту.
+ Ознайомтеся з розділом "Автоматизація" нижче, щоб дізнатися, як запланувати завдання автоматичного імпорту.

- Призначається для викликів та SMS-повідомлень.
+ It applies як до дзвінків, так і до SMS-повідомлень.

Пріоритет: 0 ]]>
- Спам-номери в базі даних видалятимуться через N днів, а прострочені записи видалятимуться автоматично. + Spam numbers в базі даних втратять чинність через N днів, прострочені записи будуть автоматично видалені. Налаштувати Робочий процес Робочі процеси Автоматизація + Workflows можуть використовуватися для автоматичного виконання завдань, як-от оновлення бази даних або автоматичне резервне копіювання.

- Існують пресети для швидкого налаштування, або ви можете створити їх з нуля.
+ Є пресети для швидкого налаштування, ви також можете створити їх з нуля або імпортувати з json-конфігурації, яка була розповсюджена іншими користувачами.

- Якщо ви знайдете будь-який робочий процес, який може широко використовуватися і який потрібно додати до пресетів, повідомте про це на github. + Якщо ви знайшли workflow, який можна широко використовувати і який слід додати до пресетів, повідомте про це на github. ]]>
Вручну - - -
- Якщо ви знайдете будь-який робочий процес, який може широко використовуватися і який потрібно додати до пресетів, повідомте про це. - ]]> -
- FTC - Do Not Call + FTC - Do Not Call Очищення бази даних спаму Очищення історії Автоматичне резервне копіювання Очистити історію - Завантажити по HTTP + HTTP Завантаження Очистити базу даних спаму - Експортувати резервну копію - Імпортувати резервну копію - Прочитати файл - Записати файл - Аналізувати CSV - Імпортувати в базу даних спаму - Імпортувати як правило регулярних виразів + Експортування резервної копії + Імпорт резервної копії + Читати файл + Записувати файл + Аналiзуваи CSV + Аналiзуваи XML + Імпорт у базу даних спаму + Імпорт як правила регулярних виразів + Замінити number байти вмісту + HTTP завантажити файл як вміст байтів ]]> - Видалити прострочені номери з бази даних спаму. - Видалити прострочені записи історії. + Видалити spam numbers з бази даних спаму. + Видалити записи простроченої історії. байти вмісту. + Експортувати поточні налаштування до вмісту байтів. ]]> байт вмісту. + Застосувати налаштування з вмісту байтів. ]]> байти вмісту з файлу. + Отримати вміст байтів з файлу. ]]> байти вмісту у файл. + Записати вміст байтів у файл. ]]> байти вмісту до списку номерів + Аналiзувати csv-файл із вмістом байтів як список номерів + ]]> + + + вмістом байтів, використати xpath і згенерувати список номерів. ]]> @@ -93,7 +95,18 @@ списку номерів. + Згенерувати нове правило регулярних виразів із списку номерів. + ]]> + + + список номерів у формат, який використовує цей додаток.
+
+ Наприклад, додайте дві дії для заміни:
+ `+12345####` на `12345....`:
+
+   - Замінити `(+)` на ``
+   - Замінити `(#)` на `.`
]]>
\ No newline at end of file diff --git a/app/src/main/res/values-zh/strings_8.xml b/app/src/main/res/values-zh/strings_8.xml index 99358565..10bb6131 100644 --- a/app/src/main/res/values-zh/strings_8.xml +++ b/app/src/main/res/values-zh/strings_8.xml @@ -1,16 +1,18 @@ 数据库 - 总计 + 总数 + + + 此数据库中的号码将被阻止。

- 你可以集成任意公共数据库,甚至是多个数据库,你还可以从本地文件中导入号码。
+ 可以整合任何公共数据库,甚至多个数据库,还可以从本地文件导入号码。

- 在下面的“自动化”部分查看如何安排自动导入任务。
+ 在下面的"自动化"部分中查看如何安排自动导入任务。

- 它适用于通话和短信。
+ 这既适用于电话,也适用于短信。

优先级:0 ]]> @@ -22,21 +24,14 @@ 自动化 + 工作流用于自动运行任务,例如更新数据库或自动备份。

- 有用于快速设置的预设,你还可以从头开始构建。
+ 有一些预设可以快速设置,还可以从头开始构建,或从其他用户共享的 json 配置中导入。

- 如果你发现任何可能广泛使用且应该添加到预设中的工作流,请在 github 上报告它。 + 如果你发现任何可被广泛使用并应该添加到预设中的工作流,请在 github 上报告。 ]]>
手动 - - -
- 如果你发现任何可以广泛使用且应该添加到预设中的工作流,请报告。 - ]]> -
FTC - Do Not Call 垃圾数据库清理 @@ -51,15 +46,17 @@ 读取文件 写入文件 解析 CSV - 导入到垃圾数据库 - 作为正则表达式规则导入 + 解析 XML + 导入至垃圾数据库 + 导入为正则表达式规则 + 替换号码 内容字节 + HTTP 下载文件为内容字节 ]]> - 从垃圾数据库中删除过期的号码。 + 删除垃圾数据库中过期的号码。 删除过期的历史记录。 内容字节的设置。 + 从内容字节应用设置。 ]]> @@ -83,12 +80,17 @@ 内容字节解析为号码列表 + 解析 CSV 内容字节号码列表 + ]]> + + + xpath解析 XML 内容字节并生成号码列表. ]]> 号码列表。 + 将号码列表添加到垃圾数据库中。 ]]> @@ -96,4 +98,15 @@ 从号码列表生成新的正则表达式规则。 ]]> + + 号码列表转换为本应用使用的格式。
+
+ 例如,添加两个动作进行替换:
+ 将`+12345####`替换为`12345....`:
+
+   - 将`(+)`替换为空
+   - 将`(#)`替换为`.`
+ ]]> +
\ No newline at end of file diff --git a/app/src/main/res/values/strings_8.xml b/app/src/main/res/values/strings_8.xml index c854e63b..144a3aea 100644 --- a/app/src/main/res/values/strings_8.xml +++ b/app/src/main/res/values/strings_8.xml @@ -1,6 +1,8 @@ Database Total + From + To Check the "Automation" section below for how to schedule an auto-import task.

- It Applies to both calls and SMS messages.
+ It applies to both calls and SMS messages.

Priority: 0 ]]> @@ -24,19 +26,12 @@
- There are presets for a quick setup, you can also build it from scratch.
+ There are presets for a quick setup, you can also build it from scratch, or import from a json configuration that shared from other users.

If you find any workflow that could be widely used and should be added to the presets, please report it on github. ]]>
Manual - - -
- If you find any workflow that can be widely used and should be added to the presets, please report. - ]]> -
FTC - Do Not Call Spam Database Cleanup @@ -51,8 +46,10 @@ Read File Write File Parse CSV + Parse XML Import to Spam Database Import as Regex Rule + Replace Number content bytes to a list of numbers + Parse csv content bytes to a list of numbers + ]]> + + + content bytes using xpath and generate a list of numbers. ]]> @@ -96,4 +98,15 @@ Generate a new regex rule from a list of numbers. ]]> + + list of numbers to the format this app uses.
+
+ For example, add two actions to replace:
+ `+12345####` to `12345....`:
+
+   - Replace `(+)` to ``
+   - Replace `(#)` to `.`
+ ]]> +
\ No newline at end of file diff --git a/auto_translate/translate.go b/auto_translate/translate.go index 0d597ae9..968981d0 100644 --- a/auto_translate/translate.go +++ b/auto_translate/translate.go @@ -112,12 +112,12 @@ func translate_1_file(lang string, fn string) error { GeminiToken := os.Getenv("GeminiToken") prompt := fmt.Sprintf( - "Translate the following xml content to language \"%s\"(\"%s\"), it's about a call blocking app "+ - "which blocks spam calls, for the word 'spam', it always references to spam calls or spam number database, it's never about email."+ - "Make sure leave the XML tags unmodified, "+ - "do not translate text within tag. "+ + "Translate the following xml content to language \"%s\"(\"%s\"), it's about a call blocking app which blocks spam calls. "+ + "For the word 'number', it always references to phone number. "+ + "For the word 'spam', it always references to spam calls, it's never about email."+ + "Make sure leave the XML tags unmodified, do not translate text within tag. "+ - "For those text to translate that are just 1 or 2 or 3 words, find all possible translation alternatives, "+ + "For the origin text that are just 1 or 2 or 3 words, find all possible translation alternatives, "+ "then pick the shortest one, as short as possible, use single word translation if possible."+ "For contents that wrapped in tag , force use single word translation."+