diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..f6577cb --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,49 @@ +name: Android CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: android + + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Set up Flutter + uses: subosito/flutter-action@v2 + with: + channel: stable + + - name: Create local.properties + run: | + echo "sdk.dir=$ANDROID_HOME" > local.properties + echo "flutter.sdk=$FLUTTER_ROOT" >> local.properties + + - name: Set up Gradle + uses: gradle/actions/setup-gradle@v6 + with: + gradle-version: '9.4.1' + + - name: Generate Gradle wrapper + run: gradle wrapper --gradle-version 9.4.1 + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Build with Gradle + run: ./gradlew build --android-skip-build-dependency-validation diff --git a/android/app/src/main/assets/liveupdate_dictionary.json b/android/app/src/main/assets/liveupdate_dictionary.json index e90950f..a7950b9 100644 --- a/android/app/src/main/assets/liveupdate_dictionary.json +++ b/android/app/src/main/assets/liveupdate_dictionary.json @@ -21,7 +21,16 @@ "swiggy", "blinkit", "instamart", - "bigbasket" + "bigbasket", + "ifood", + "i.food", + "aiqfome", + "rappi", + "99food", + "food to save", + "delivery much", + "anota ai", + "menu direto" ], "text_triggers": [ "заказ", @@ -46,7 +55,23 @@ "送餐員", "餐点", "餐點", - "配送" + "配送", + "pedido", + "entrega", + "entregador", + "motoboy", + "restaurante", + "lanche", + "comida", + "delivery", + "saiu para entrega", + "a caminho", + "preparando", + "pronto para retirada", + "retirada", + "retirar", + "cozinha", + "pagamento aprovado" ], "exclude_patterns": [ "(с дн[её]м рождения|happy birthday|birthday|生日快乐|生日快樂)", @@ -55,7 +80,12 @@ "(cart|basket|checkout|complete\\s+(?:your\\s+)?checkout|finish\\s+(?:your\\s+)?order|корзин|заверш(?:ите|ить)\\s+оформлени|оформите\\s+заказ|перейдите\\s+к\\s+оформлени|продолж(?:ите|ить)\\s+оформлени)", "(you'?ll\\s+get\\s+an\\s+alert[^\\n]{0,80}(?:at\\s+your\\s+door|delivered)|keep\\s+your\\s+phone\\s+nearby\\s+until\\s+it'?s\\s+delivered|until\\s+it'?s\\s+delivered)", "(при\\s+заказе\\s+от\\s*\\d|order\\s+from\\s*\\d|满\\d+减|滿\\d+減)", - "(почт[аы]\\s+росси|посыл(?:к|ок)|маркетплейс|отделени[ея]|трек-?номер|tracking|shipment|parcel|пункт\\s*выдачи|取件码|取件碼|快递|快遞|驿站|驛站|包裹|收发室|收發室)" + "(почт[аы]\\s+росси|посыл(?:к|ок)|маркетплейс|отделени[ея]|трек-?номер|tracking|shipment|parcel|пункт\\s*выдачи|取件码|取件碼|快递|快遞|驿站|驛站|包裹|收发室|收發室)", + "(feliz\\s+anivers[áa]rio|parab[ée]ns)", + "(cupom|promo[cç][aã]o|promocional|desconto|cashback|oferta|ganhe\\s+\\d+%|\\d+%\\s*(?:off|de\\s+desconto)|frete\\s+gr[áa]tis|voucher|benef[íi]cio)", + "(pagamento\\s*(?:falhou|recusado|n[aã]o\\s+aprovado|n[aã]o\\s+conclu[ií]do)|pedido\\s+cancelado|cancelamento|estorno|reembolso)", + "(carrinho|sacola|checkout|finalize\\s+(?:seu\\s+)?pedido|conclua\\s+(?:seu\\s+)?pedido|continuar\\s+compra)", + "(correios|encomenda|pacote|rastreamento|c[óo]digo\\s+de\\s+rastreio|objeto\\s+postal|retirada\\s+em\\s+ag[êe]ncia|marketplace)" ], "signals": [ { @@ -77,6 +107,26 @@ { "stage": 0, "pattern": "(заказ[^\\n]*(принят|создан|оформлен(?:а|о|ы)?\\b|подтвержд[её]н|оплачен)|order[^\\n]*(received|accepted|confirmed|created|placed|paid)|оплат[аы][^\\n]*(прошла|подтверждена|успешна|принята)|payment[^\\n]*(successful|confirmed|received|accepted)|订单已接|訂單已接|已付款|支付成功|订单已确认|訂單已確認|下单成功|下單成功|已建立)" + }, + { + "stage": 4, + "pattern": "(pedido\\s+(?:entregue|finalizado|conclu[ií]do)|entrega\\s+(?:realizada|conclu[ií]da)|bom\\s+apetite|aproveite\\s+(?:seu\\s+)?pedido|obrigado\\s+por\\s+pedir)" + }, + { + "stage": 3, + "pattern": "((?:seu\\s+)?pedido\\s+(?:saiu\\s+para\\s+entrega|est[aá]\\s+a\\s+caminho|est[aá]\\s+indo\\s+at[eé]\\s+voc[eê])|entregador[^\\n]*(?:a\\s+caminho|chegando|pr[óo]ximo|retirou|pegou)|motoboy[^\\n]*(?:a\\s+caminho|chegando|pr[óo]ximo|retirou|pegou))" + }, + { + "stage": 2, + "pattern": "((?:seu\\s+)?pedido[^\\n]{0,50}(?:pronto|preparado|embalado|aguardando\\s+(?:retirada|entregador)|pronto\\s+para\\s+(?:retirada|entrega))|retire\\s+(?:seu\\s+)?pedido|pronto\\s+para\\s+retirar)" + }, + { + "stage": 1, + "pattern": "((?:seu\\s+)?pedido[^\\n]{0,50}(?:est[aá]\\s+sendo\\s+preparado|est[aá]\\s+em\\s+preparo|em\\s+preparo|na\\s+cozinha|come[cç]ou\\s+a\\s+ser\\s+preparado)|preparando\\s+(?:seu\\s+)?pedido)" + }, + { + "stage": 0, + "pattern": "((?:seu\\s+)?pedido[^\\n]{0,50}(?:recebido|confirmado|aceito|criado|realizado|aprovado|pago)|pagamento[^\\n]{0,50}(?:aprovado|confirmado|recebido|realizado)|compra\\s+aprovada)" } ] }, @@ -91,7 +141,14 @@ "yango", "yandex", "didi", - "grab" + "grab", + "99", + "99app", + "cabify", + "indrive", + "inDrive", + "mobizap", + "lady driver" ], "text_triggers": [ "такси", @@ -116,7 +173,19 @@ "乘車", "网约车", "網約車", - "乘客" + "乘客", + "corrida", + "viagem", + "motorista", + "carro", + "placa", + "embarque", + "desembarque", + "partida", + "destino", + "chegando", + "chegou", + "solicitação de corrida" ], "signals": [ { @@ -138,6 +207,26 @@ { "stage": 0, "pattern": "(поиск[^\\n]*водител|ищем[^\\n]*водител|searching for (a )?driver|finding driver|正在寻找司机|正在尋找司機|呼叫中|等待应答|等待應答|正在派单|正在派單|等待接驾|等待接駕)" + }, + { + "stage": 4, + "pattern": "(viagem\\s+(?:finalizada|conclu[ií]da|encerrada)|corrida\\s+(?:finalizada|conclu[ií]da|encerrada)|voc[eê]\\s+chegou|chegamos\\s+ao\\s+destino|chegada\\s+ao\\s+destino)" + }, + { + "stage": 3, + "pattern": "(viagem\\s+(?:iniciada|em\\s+andamento)|corrida\\s+(?:iniciada|em\\s+andamento)|aproveite\\s+(?:sua\\s+)?viagem|a\\s+caminho\\s+do\\s+destino)" + }, + { + "stage": 2, + "pattern": "(motorista[^\\n]*(?:a\\s+caminho|chegando|pr[óo]ximo|chegou|j[aá]\\s+chegou|est[aá]\\s+no\\s+local)|carro[^\\n]*(?:a\\s+caminho|chegando|pr[óo]ximo)|v[aá]\\s+para\\s+o\\s+local\\s+de\\s+embarque)" + }, + { + "stage": 1, + "pattern": "(motorista\\s+(?:encontrado|definido|a\\s+caminho|aceitou)|corrida\\s+aceita|viagem\\s+aceita|encontramos\\s+(?:um\\s+)?motorista)" + }, + { + "stage": 0, + "pattern": "(procurando\\s+(?:um\\s+)?motorista|buscando\\s+(?:um\\s+)?motorista|encontrando\\s+(?:um\\s+)?motorista|solicita[cç][aã]o\\s+de\\s+corrida|chamando\\s+motorista)" } ] }, @@ -159,7 +248,10 @@ "navi", "amap", "baidu.BaiduMap", - "citymapper" + "citymapper", + "mapas", + "rotas", + "gps" ], "text_triggers": [ "маршрут", @@ -216,12 +308,33 @@ "向左", "往左", "向右", - "往右" + "往右", + "rota", + "rotas", + "navegação", + "navegacao", + "vire à esquerda", + "vire a esquerda", + "vire à direita", + "vire a direita", + "mantenha-se à esquerda", + "mantenha-se à direita", + "pegue a saída", + "retorno", + "rotatória", + "destino", + "chegou ao destino", + "siga em frente", + "continue", + "radar" ], "exclude_patterns": [ "(с дн[её]м рождения|happy birthday|birthday|生日快乐|生日快樂)", "(дарим\\s+скидк|скидк[\\p{L}]*\\s*\\d+%|\\d+%\\s*(скидк|off)|акци[яи]|промокод|promo\\s*code|coupon|cash\\s*back|кэшб[еэ]к|红包|紅包|优惠|優惠|折扣|满减|滿減|返现|返現|促销|促銷|券|免運|免运费)", - "(вы получили это письмо|отписаться от рассыл|unsubscribe|newsletter|follow us|скачать приложение|download app)" + "(вы получили это письмо|отписаться от рассыл|unsubscribe|newsletter|follow us|скачать приложение|download app)", + "(feliz\\s+anivers[áa]rio|parab[ée]ns)", + "(cupom|promo[cç][aã]o|desconto|cashback|oferta|frete\\s+gr[áa]tis)", + "(voc[eê]\\s+recebeu\\s+este\\s+e-?mail|descadastrar|cancelar\\s+assinatura|newsletter|baixar\\s+app)" ], "signals": [ { @@ -239,6 +352,22 @@ { "stage": 1, "pattern": "(маршрут построен|маршрут начат|navigation started|starting route|follow the route|route started|开始导航|開始導航|路线已规划|路線已規劃|准备出发|準備出發)" + }, + { + "stage": 4, + "pattern": "(voc[eê]\\s+chegou|chegou\\s+ao\\s+destino|destino\\s+alcan[cç]ado|rota\\s+(?:finalizada|conclu[ií]da)|navega[cç][aã]o\\s+(?:finalizada|encerrada))" + }, + { + "stage": 3, + "pattern": "(destino[^\\n]*(?:[àa]\\s+direita|[àa]\\s+esquerda)|quase\\s+l[aá]|aproximando-se\\s+do\\s+destino|voc[eê]\\s+est[aá]\\s+quase\\s+chegando)" + }, + { + "stage": 2, + "pattern": "(em\\s+\\d+[\\d\\s.,]*(?:m|km|metros?|quil[oô]metros?)|vire\\s+(?:[àa]\\s+)?(?:esquerda|direita)|mantenha-se\\s+(?:[àa]\\s+)?(?:esquerda|direita)|pegue\\s+a\\s+sa[ií]da|fa[cç]a\\s+o\\s+retorno|entre\\s+na\\s+rotat[óo]ria|siga\\s+em\\s+frente|continue\\s+(?:em\\s+frente|reto)|radar\\s+[àa]\\s+frente)" + }, + { + "stage": 1, + "pattern": "(rota\\s+(?:iniciada|calculada|tra[cç]ada|pronta)|navega[cç][aã]o\\s+iniciada|siga\\s+a\\s+rota|come[cç]ando\\s+a\\s+rota)" } ] }, @@ -254,7 +383,13 @@ "dji", "wearable", "galaxywearable", - "miui" + "miui", + "fone", + "fone de ouvido", + "relógio", + "relogio", + "smartwatch", + "galaxy wearable" ], "text_triggers": [ "camera", @@ -282,12 +417,27 @@ "耳机", "耳機", "手表", - "手錶" + "手錶", + "conectado", + "conectada", + "conectando", + "pareado", + "pareando", + "emparelhado", + "dispositivo", + "fone", + "fone de ouvido", + "relógio", + "relogio", + "smartwatch" ], "exclude_patterns": [ "(с дн[её]м рождения|happy birthday|birthday|生日快乐|生日快樂)", "(дарим\\s+скидк|скидк[\\p{L}]*\\s*\\d+%|\\d+%\\s*(скидк|off)|акци[яи]|промокод|promo\\s*code|coupon|cash\\s*back|кэшб[еэ]к|红包|紅包|优惠|優惠|折扣|满减|滿減|返现|返現|促销|促銷|券|免運|免运费)", - "(hotspot|personal\\s+hotspot|tether(?:ing)?|wifi\\s+hotspot|точк[аеи]\\s+доступа|раздач[аи]\\s+интернета|клиент(?:ов|ы)?\\s+подключ(?:ен|ено|ены)?|\\b\\d+\\s+devices?\\b)" + "(hotspot|personal\\s+hotspot|tether(?:ing)?|wifi\\s+hotspot|точк[аеи]\\s+доступа|раздач[аи]\\s+интернета|клиент(?:ов|ы)?\\s+подключ(?:ен|ено|ены)?|\\b\\d+\\s+devices?\\b)", + "(feliz\\s+anivers[áa]rio|parab[ée]ns)", + "(cupom|promo[cç][aã]o|desconto|cashback|oferta)", + "(roteador|ponto\\s+de\\s+acesso|hotspot|compartilhamento\\s+de\\s+internet|\\b\\d+\\s+dispositivos?\\s+conectados?)" ], "signals": [ { @@ -297,6 +447,14 @@ { "stage": 1, "pattern": "(connecting\\b|pair(?:ing)?\\b|подключ(?:ается|ение)\\b|соедин(?:яется|ение)\\b|正在连接|正在連接|配对中|配對中|连线中|連線中)" + }, + { + "stage": 2, + "pattern": "(conectad[oa]s?\\b|paread[oa]s?\\b|emparelhad[oa]s?\\b|conex[aã]o\\s+estabelecida)" + }, + { + "stage": 1, + "pattern": "(conectando\\b|pareando\\b|emparelhando\\b|tentando\\s+conectar|conex[aã]o\\s+em\\s+andamento)" } ] }, @@ -318,7 +476,10 @@ "hiddify", "tailscale", "zerotier", - "shadowsocks" + "shadowsocks", + "tunel", + "túnel", + "proxy" ], "text_triggers": [ "vpn", @@ -339,12 +500,25 @@ "已连线", "已連線", "已连接", - "已連接" + "已連接", + "túnel", + "tunel", + "conexão segura", + "conexao segura", + "conectado", + "conectada", + "proxy", + "nó", + "servidor" ], "signals": [ { "stage": 1, "pattern": "(? lbDictionaryLanguages = label: 'English', assetFileName: 'liveupdate_dictionary_en.json', ), + DictionaryLanguageOption( + id: 'pt-BR', + label: 'Português (Brasil)', + assetFileName: 'liveupdate_dictionary_pt-BR.json', + ), DictionaryLanguageOption( id: 'ru', label: 'Русский', @@ -146,7 +151,9 @@ const Set _languageSensitiveStringKeys = { final RegExp _hangulRegExp = RegExp(r'[\uAC00-\uD7AF\u3131-\u318E]'); final RegExp _cjkRegExp = RegExp(r'[\u3400-\u9FFF\uF900-\uFAFF]'); final RegExp _cyrillicRegExp = RegExp(r'[\u0400-\u04FF]'); -final RegExp _latinRegExp = RegExp(r'[A-Za-z]'); +final RegExp _latinRegExp = RegExp( + r'[A-Za-zÀ-ÖØ-öø-ÿ\u0100-\u017F\u0300-\u036F]', +); List _scopeSmartRules(List? raw, String languageId) { if (raw == null) { @@ -286,7 +293,7 @@ bool _matchesLanguageForLocale(String localeKey, String languageId) { Set _detectLanguages(String raw) { final String value = raw.trim(); if (value.isEmpty) { - return const {'en', 'ru', 'zh', 'ko'}; + return const {'en', 'pt-BR', 'ru', 'zh', 'ko'}; } final Set languages = {}; @@ -301,9 +308,10 @@ Set _detectLanguages(String raw) { } if (_latinRegExp.hasMatch(value)) { languages.add('en'); + languages.add('pt-BR'); } - return languages.isEmpty ? const {'en', 'ru', 'zh', 'ko'} : languages; + return languages.isEmpty ? const {'en', 'pt-BR', 'ru', 'zh', 'ko'} : languages; } dynamic _deepCopyJsonNode(dynamic node) {