Compare commits

...

27 Commits

Author SHA1 Message Date
Imran
49cfa95463 Merge pull request #1265 from ImranR98/dev
- Multi-host support + add '.net' host to APKPure source (#1250)
- HTML link parsing bugfix (#1259)
- APKMirror version extraction bugfix (#1264)
2024-01-08 19:28:31 -05:00
Imran Remtulla
4600ab0593 Increment version 2024-01-08 19:20:08 -05:00
Imran Remtulla
7f2ca98bde Multi-host support + add '.net' host to APKPure source (#1250) 2024-01-08 19:17:50 -05:00
Imran Remtulla
6511485bcf APKMirror version extraction bugfix (#1264) 2024-01-08 18:33:24 -05:00
Imran Remtulla
fd22113e44 HTML link parsing bugfix (#1259) 2024-01-08 18:18:57 -05:00
Imran
e8580dc1d5 Merge pull request #1257 from ImranR98/dev
Add F-Droid changelogs (#1255), Fix corrupt pt.json (#1256)
2024-01-07 21:59:17 -05:00
Imran Remtulla
daffff7eb0 Add F-Droid changelogs (#1255) 2024-01-07 21:57:23 -05:00
Imran Remtulla
751fda5e37 Increment version 2024-01-07 21:45:41 -05:00
Imran Remtulla
1e38abc500 Fix corrupt pt.json (#1256) 2024-01-07 21:40:56 -05:00
Imran
8e7137815b Merge pull request #1254 from ImranR98/dev
URL parsing bugfix for HTML source (#1253)
2024-01-07 12:37:51 -05:00
Imran Remtulla
4226ee453f Merge remote-tracking branch 'origin/main' into dev 2024-01-07 12:37:14 -05:00
Imran
568a110443 Merge pull request #1249 from LilligantMatsuri/main
Update Chinese translation
2024-01-07 12:37:00 -05:00
Imran
7fd7a6ca8d Merge pull request #1252 from jont4/main
Translation: many fixes in PT translation
2024-01-07 12:36:47 -05:00
Imran Remtulla
3092c854ff Increment version 2024-01-07 12:36:20 -05:00
Imran Remtulla
a9566f4b23 Remove redundant function call 2024-01-07 12:34:09 -05:00
Imran Remtulla
7a5aa3c11d URL parsing bugfix for HTML source (#1253) 2024-01-07 12:33:06 -05:00
jont4
f2454bb028 Translation: many fixes in PT translation 2024-01-07 12:28:24 -03:00
Matsuri
2c1023a6fd Update zh.json
- Add & translate new strings
- Correct inaccurate translations

Signed-off-by: Matsuri <matsuri@vmoe.info>
2024-01-07 16:55:00 +08:00
Imran
9187ac6684 Merge pull request #1248 from ImranR98/dev
Removed Steam Source (existing apps auto-switched to HTML) (#1244) + Bugfixes
2024-01-06 23:39:11 -05:00
Imran Remtulla
9640e43bc5 Upgrade packages, increment version 2024-01-06 23:38:36 -05:00
Imran Remtulla
09b24b36f2 Removed Steam Source (existing apps auto-switched to HTML) (#1244) + bugfixes 2024-01-06 23:37:27 -05:00
Imran
06a761c0d0 Merge pull request #1247 from CertainBot/main
Update Spanish
2024-01-06 22:46:06 -05:00
Imran
6ff7d7fcd7 Merge pull request #1246 from bluefly000/japanese-translation
Update ja.json
2024-01-06 22:45:47 -05:00
Imran
0f27c79303 Merge pull request #1243 from pmtpro/patch-1
Update vi.json
2024-01-06 22:45:25 -05:00
CertainBot
9961ad081f Update Spanish
- English: new strings, typos, minor changes
- Español: strings nuevos, correcciones, algunas mejoras
2024-01-07 02:55:18 +01:00
bluefly000
02056b4f78 Update ja.json 2024-01-06 01:05:52 +09:00
PMTpro
0492588c25 Update vi.json 2024-01-04 09:26:37 +07:00
37 changed files with 451 additions and 394 deletions

View File

@@ -21,7 +21,7 @@ Currently supported App sources:
- [SourceForge](https://sourceforge.net/) - [SourceForge](https://sourceforge.net/)
- [SourceHut](https://git.sr.ht/) - [SourceHut](https://git.sr.ht/)
- Other - General: - Other - General:
- [APKPure](https://apkpure.com/) - [APKPure](https://apkpure.net/)
- [Aptoide](https://aptoide.com/) - [Aptoide](https://aptoide.com/)
- [Uptodown](https://uptodown.com/) - [Uptodown](https://uptodown.com/)
- [APKMirror](https://apkmirror.com/) (Track-Only) - [APKMirror](https://apkmirror.com/) (Track-Only)
@@ -34,7 +34,6 @@ Currently supported App sources:
- Other - App-Specific: - Other - App-Specific:
- [WhatsApp](https://whatsapp.com) - [WhatsApp](https://whatsapp.com)
- [Telegram App](https://telegram.org) - [Telegram App](https://telegram.org)
- [Steam Mobile Apps](https://store.steampowered.com/mobile)
- [Neutron Code](https://neutroncode.com) - [Neutron Code](https://neutroncode.com)
- "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files - "HTML" (Fallback): Any other URL that returns an HTML page with links to APK files

View File

@@ -8,13 +8,13 @@
"functionNotImplemented": "Esta clase no ha implementado esta función", "functionNotImplemented": "Esta clase no ha implementado esta función",
"placeholder": "Espacio reservado", "placeholder": "Espacio reservado",
"someErrors": "Han ocurrido algunos errores", "someErrors": "Han ocurrido algunos errores",
"unexpectedError": "Error Inesperado", "unexpectedError": "Error inesperado",
"ok": "OK", "ok": "OK",
"and": "y", "and": "y",
"githubPATLabel": "Token Github de Acceso Personal\n(Reduce tiempos de espera)", "githubPATLabel": "Token GitHub de acceso personal\n(reduce tiempos de espera)",
"includePrereleases": "Incluir versiones preliminares", "includePrereleases": "Incluir versiones preliminares",
"fallbackToOlderReleases": "Retroceder a versiones previas", "fallbackToOlderReleases": "Retroceder a versiones previas",
"filterReleaseTitlesByRegEx": "Filtrar por título de version", "filterReleaseTitlesByRegEx": "Filtrar por título de versión",
"invalidRegEx": "Expresión inválida", "invalidRegEx": "Expresión inválida",
"noDescription": "Sin descripción", "noDescription": "Sin descripción",
"cancel": "Cancelar", "cancel": "Cancelar",
@@ -25,11 +25,11 @@
"githubStarredRepos": "Repositorios favoritos GitHub", "githubStarredRepos": "Repositorios favoritos GitHub",
"uname": "Nombre de usuario", "uname": "Nombre de usuario",
"wrongArgNum": "Número de argumentos provistos inválido", "wrongArgNum": "Número de argumentos provistos inválido",
"xIsTrackOnly": "{} es de 'Solo Seguimiento'", "xIsTrackOnly": "{} es de 'Sólo seguimiento'",
"source": "Origen", "source": "Origen",
"app": "Aplicación", "app": "Aplicación",
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.", "appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'solo seguimiento'.",
"youPickedTrackOnly": "Debe seleccionar la opción de 'Solo Seguimiento'.", "youPickedTrackOnly": "Debe seleccionar la opción de 'solo seguimiento'.",
"trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargarla o actalizarla.", "trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargarla o actalizarla.",
"cancelled": "Cancelado", "cancelled": "Cancelado",
"appAlreadyAdded": "Aplicación ya añadida", "appAlreadyAdded": "Aplicación ya añadida",
@@ -42,7 +42,7 @@
"search": "Buscar", "search": "Buscar",
"additionalOptsFor": "Opciones Adicionales para {}", "additionalOptsFor": "Opciones Adicionales para {}",
"supportedSources": "Fuentes Soportadas", "supportedSources": "Fuentes Soportadas",
"trackOnlyInBrackets": "(Solo Seguimiento)", "trackOnlyInBrackets": "(Solo seguimiento)",
"searchableInBrackets": "(soporta búsqueda)", "searchableInBrackets": "(soporta búsqueda)",
"appsString": "Aplicaciones", "appsString": "Aplicaciones",
"noApps": "Sin Aplicaciones", "noApps": "Sin Aplicaciones",
@@ -61,27 +61,27 @@
"removeSelectedApps": "Eliminar Aplicaciones Seleccionadas", "removeSelectedApps": "Eliminar Aplicaciones Seleccionadas",
"updateX": "Actualizar {}", "updateX": "Actualizar {}",
"installX": "Instalar {}", "installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimiento)\ncomo Actualizada", "markXTrackOnlyAsUpdated": "Marcar {}\n(Solo seguimiento)\ncomo actualizada",
"changeX": "Cambiar {}", "changeX": "Cambiar {}",
"installUpdateApps": "Instalar/Actualizar Aplicaciones", "installUpdateApps": "Instalar/Actualizar aplicaciones",
"installUpdateSelectedApps": "Instalar/Actualizar Aplicaciones Seleccionadas", "installUpdateSelectedApps": "Instalar/Actualizar aplicaciones seleccionadas",
"markXSelectedAppsAsUpdated": "¿Marcar {} Aplicaciones Seleccionadas como Actualizadas?", "markXSelectedAppsAsUpdated": "¿Marcar {} aplicaciones seleccionadas como actualizadas?",
"no": "No", "no": "No",
"yes": "Sí", "yes": "Sí",
"markSelectedAppsUpdated": "Marcar Aplicaciones Seleccionadas como Actualizadas", "markSelectedAppsUpdated": "Marcar aplicaciones seleccionadas como actualizadas",
"pinToTop": "Fijar arriba", "pinToTop": "Fijar arriba",
"unpinFromTop": "Desfijar de arriba", "unpinFromTop": "Desfijar de arriba",
"resetInstallStatusForSelectedAppsQuestion": "¿Restuarar Estado de Instalación para las Aplicaciones Seleccionadas?", "resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?",
"installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.", "installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
"shareSelectedAppURLs": "Compartir URLs de las Aplicaciones Seleccionadas", "shareSelectedAppURLs": "Compartir URLs de las aplicaciones seleccionadas",
"resetInstallStatus": "Restaurar Estado de Instalación", "resetInstallStatus": "Restaurar Estado de Instalación",
"more": "Más", "more": "Más",
"removeOutdatedFilter": "Elimiar Filtro de Aplicaciones Desactualizado", "removeOutdatedFilter": "Elimiar filtro de aplicaciones desactualizado",
"showOutdatedOnly": "Mostrar solo Aplicaciones Desactualizadas", "showOutdatedOnly": "Mostrar solo aplicaciones desactualizadas",
"filter": "Filtrar", "filter": "Filtrar",
"filterActive": "Filtrar *", "filterActive": "Filtrar *",
"filterApps": "Filtrar Actualizaciones", "filterApps": "Filtrar Actualizaciones",
"appName": "Nombre de la Aplicación", "appName": "Nombre de la aplicación",
"author": "Autor", "author": "Autor",
"upToDateApps": "Aplicaciones Actualizadas", "upToDateApps": "Aplicaciones Actualizadas",
"nonInstalledApps": "Aplicaciones No Instaladas", "nonInstalledApps": "Aplicaciones No Instaladas",
@@ -109,7 +109,7 @@
"theme": "Tema", "theme": "Tema",
"dark": "Oscuro", "dark": "Oscuro",
"light": "Claro", "light": "Claro",
"followSystem": "Seguir al Sistema", "followSystem": "Seguir al sistema",
"obtainium": "Obtainium", "obtainium": "Obtainium",
"materialYou": "Material You", "materialYou": "Material You",
"useBlackTheme": "Negro puro en tema Oscuro", "useBlackTheme": "Negro puro en tema Oscuro",
@@ -120,16 +120,16 @@
"appSortOrder": "Orden de Clasificación", "appSortOrder": "Orden de Clasificación",
"ascending": "Ascendente", "ascending": "Ascendente",
"descending": "Descendente", "descending": "Descendente",
"bgUpdateCheckInterval": "Intervalo de Comprobación de Actualizaciones en Segundo Plano", "bgUpdateCheckInterval": "Comprobación actualizaciones en segundo plano",
"neverManualOnly": "Nunca - Solo Manual", "neverManualOnly": "Nunca, solo manual",
"appearance": "Apariencia", "appearance": "Apariencia",
"showWebInAppView": "Mostrar Vista de la Web de Origen", "showWebInAppView": "Mostrar vista de la web de origen",
"pinUpdates": "Fijar Actualizaciones en la Parte Superior de la Vista de Aplicaciones", "pinUpdates": "Fijar Actualizaciones al principio",
"updates": "Actualizaciones", "updates": "Actualizaciones",
"sourceSpecific": "Fuente Específica", "sourceSpecific": "Fuente Específica",
"appSource": "Fuente de la Aplicación", "appSource": "Obtainium en GitHub",
"noLogs": "Sin Logs", "noLogs": "Sin Logs",
"appLogs": "Logs de la Aplicación", "appLogs": "Logs",
"close": "Cerrar", "close": "Cerrar",
"share": "Compartir", "share": "Compartir",
"appNotFound": "Aplicación no encontrada", "appNotFound": "Aplicación no encontrada",
@@ -213,30 +213,30 @@
"importFromURLsInFile": "Importar URLs desde archivo (como OPML)", "importFromURLsInFile": "Importar URLs desde archivo (como OPML)",
"versionDetection": "Detección de Versiones", "versionDetection": "Detección de Versiones",
"standardVersionDetection": "Por versión", "standardVersionDetection": "Por versión",
"groupByCategory": "Agrupar por Categoría", "groupByCategory": "Agrupar por categoría",
"autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador, si es posible", "autoApkFilterByArch": "Filtrar APKs por arquitectura del procesador (si es posible)",
"overrideSource": "Sobrescribir Fuente", "overrideSource": "Sobrescribir Fuente",
"dontShowAgain": "No mostrar de nuevo", "dontShowAgain": "No mostrar de nuevo",
"dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'", "dontShowTrackOnlyWarnings": "No mostrar avisos sobre apps en 'solo seguimiento'",
"dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APKs", "dontShowAPKOriginWarnings": "No mostrar avisos sobre las fuentes de las APKs",
"moveNonInstalledAppsToBottom": "Mover Apps no instaladas en la Parte Inferior de la Vista de Aplicaciones", "moveNonInstalledAppsToBottom": "Mover Apps no instaladas al final",
"gitlabPATLabel": "Token GitLab de Acceso Personal\n(Habilita la Búsqueda y Mejor Detección de APKs)", "gitlabPATLabel": "Token GitLab de acceso personal\n(habilita la búsqueda y mejor detección de APKs)",
"about": "Acerca", "about": "Acerca",
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en Ajustes)", "requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en ajustes)",
"checkOnStart": "Comprobar actualizaciones durante el inicio", "checkOnStart": "Comprobar actualizaciones al inicio",
"tryInferAppIdFromCode": "Intentar deducir la ID de la APP por el código fuente", "tryInferAppIdFromCode": "Intentar deducir la ID de la app por el código fuente",
"removeOnExternalUninstall": "Auto eliminar Apps desinstaladas externamente", "removeOnExternalUninstall": "Auto eliminar apps desinstaladas externamente",
"pickHighestVersionCode": "Auto selección versión superior del código APK", "pickHighestVersionCode": "Auto selección versión superior del código APK",
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la App", "checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la app",
"disablePageTransitions": "Deshabilitar animaciones de transición de la página", "disablePageTransitions": "Deshabilitar animaciones de transición",
"reversePageTransitions": "Invertir animaciones de transición de la página", "reversePageTransitions": "Invertir animaciones de transición",
"minStarCount": "Número Mínimo de Estrellas", "minStarCount": "Número Mínimo de Estrellas",
"addInfoBelow": "Añadir esta información debajo.", "addInfoBelow": "Añadir esta información debajo.",
"addInfoInSettings": "Puede añadir esta información en Ajustes.", "addInfoInSettings": "Puede añadir esta información en Ajustes.",
"githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con una clave API.", "githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con una clave API.",
"gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin una clave API.", "gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin una clave API.",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Filtrar por notas de nersión (release notes)", "filterReleaseNotesByRegEx": "Filtrar por notas de versión (release notes)",
"customLinkFilterRegex": "Filtro personalizado de Enlace APK (por defecto '.apk$')", "customLinkFilterRegex": "Filtro personalizado de Enlace APK (por defecto '.apk$')",
"appsPossiblyUpdated": "Actualización de Apps intentada", "appsPossiblyUpdated": "Actualización de Apps intentada",
"appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones", "appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones",
@@ -244,7 +244,7 @@
"enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano", "enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
"backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.", "backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
"backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede comprobar con Obtainium abierto.", "backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede comprobar con Obtainium abierto.",
"verifyLatestTag": "Comprueba la etiqueta 'Latest'", "verifyLatestTag": "Comprobar la etiqueta 'Latest'",
"intermediateLinkRegex": "Filtrar por enlace 'intermedio' para visitar primero", "intermediateLinkRegex": "Filtrar por enlace 'intermedio' para visitar primero",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filter links by link text",
"intermediateLinkNotFound": "Enlace intermedio no encontrado", "intermediateLinkNotFound": "Enlace intermedio no encontrado",
@@ -252,10 +252,10 @@
"exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)", "exemptFromBackgroundUpdates": "Exenta de actualizciones en segundo plano (si están habilitadas)",
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi", "bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
"autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior", "autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior",
"versionExtractionRegEx": "Versión de Extracción de RegEx", "versionExtractionRegEx": "Versión de extracción regex",
"matchGroupToUse": "Coincidir en Grupo a Usar", "matchGroupToUse": "Grupo a usar para versión de extracción regex",
"highlightTouchTargets": "Resaltar objetivos menos obvios", "highlightTouchTargets": "Resaltar objetivos menos obvios",
"pickExportDir": "Selecciona el Directorio para Exportar", "pickExportDir": "Directorio para Exportar",
"autoExportOnChanges": "Auto Exportar cuando haya cambios", "autoExportOnChanges": "Auto Exportar cuando haya cambios",
"includeSettings": "Incluir ajustes", "includeSettings": "Incluir ajustes",
"filterVersionsByRegEx": "Filtrar por Versiones", "filterVersionsByRegEx": "Filtrar por Versiones",
@@ -267,26 +267,26 @@
"debugMenu": "Menu Depurar", "debugMenu": "Menu Depurar",
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.", "bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano", "runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
"versionExtractWholePage": "Aplicar la Versión de Extracción Regex a la Página Entera", "versionExtractWholePage": "Aplicar la versión de extracción regex a la página entera",
"installing": "Instalando", "installing": "Instalando",
"skipUpdateNotifications": "Omitir de notificaciones sobre actualizaciones", "skipUpdateNotifications": "No notificar sobre actualizaciones",
"updatesAvailableNotifChannel": "Actualizaciones Disponibles", "updatesAvailableNotifChannel": "Actualizaciones disponibles",
"appsUpdatedNotifChannel": "Aplicaciones Actualizadas", "appsUpdatedNotifChannel": "Aplicaciones actualizadas",
"appsPossiblyUpdatedNotifChannel": "Se ha Intentado Actualizar la Aplicación", "appsPossiblyUpdatedNotifChannel": "Se ha intentado actualizar la aplicación",
"errorCheckingUpdatesNotifChannel": "Error Buscando Actualizaciones", "errorCheckingUpdatesNotifChannel": "Error buscando actualizaciones",
"appsRemovedNotifChannel": "Aplicaciones Eliminadas", "appsRemovedNotifChannel": "Aplicaciones eliminadas",
"downloadingXNotifChannel": "Descargando {}", "downloadingXNotifChannel": "Descargando {}",
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación", "completeAppInstallationNotifChannel": "Instalación completada",
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones", "checkingForUpdatesNotifChannel": "Buscando actualizaciones",
"onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas y en seguimiento", "onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas o en seguimiento",
"supportFixedAPKURL": "Soporte para URLs fijas de APK", "supportFixedAPKURL": "Soporte para URLs fijas de APK",
"selectX": "Selecciona {}", "selectX": "Selecciona {}",
"parallelDownloads": "Permitir descargas paralelas", "parallelDownloads": "Permitir descargas paralelas",
"installMethod": "Installation method", "installMethod": "Método de instalación",
"normal": "Normal", "normal": "Normal",
"shizuku": "Shizuku", "shizuku": "Shizuku",
"root": "Root", "root": "Root",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizuku no está operativo",
"removeAppQuestion": { "removeAppQuestion": {
"one": "¿Eliminar Aplicación?", "one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?" "other": "¿Eliminar Aplicaciones?"
@@ -312,16 +312,16 @@
"other": "{} URLs" "other": "{} URLs"
}, },
"minute": { "minute": {
"one": "{} Minuto", "one": "{} minuto",
"other": "{} Minutos" "other": "{} minutos"
}, },
"hour": { "hour": {
"one": "{} Hora", "one": "{} hora",
"other": "{} Horas" "other": "{} horas"
}, },
"day": { "day": {
"one": "{} Día", "one": "{} día",
"other": "{} Días" "other": "{} días"
}, },
"clearedNLogsBeforeXAfterY": { "clearedNLogsBeforeXAfterY": {
"one": "Eliminado {n} log (previo a = {before}, posterior a = {after})", "one": "Eliminado {n} log (previo a = {before}, posterior a = {after})",

View File

@@ -235,7 +235,7 @@
"addInfoInSettings": "設定でこの情報を追加してください。", "addInfoInSettings": "設定でこの情報を追加してください。",
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。", "githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。", "gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。",
"sortByLastLinkSegment": "Sort by only the last segment of the link", "sortByLastLinkSegment": "リンクの最後のセグメントのみでソートする",
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする", "filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')", "customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
"appsPossiblyUpdated": "アプリのアップデートを試行", "appsPossiblyUpdated": "アプリのアップデートを試行",
@@ -245,10 +245,10 @@
"backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。", "backgroundUpdateReqsExplanation": "バックグラウンドアップデートは、すべてのアプリで可能とは限りません。",
"backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。", "backgroundUpdateLimitsExplanation": "バックグラウンドアップデートが成功したかどうかは、Obtainiumを起動したときにしか判断できません。",
"verifyLatestTag": "'latest'タグを確認する", "verifyLatestTag": "'latest'タグを確認する",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "訪問する「中間」リンクのフィルター",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "テキストでリンクをフィルタリングする",
"intermediateLinkNotFound": "中間リンクが見つかりませんでした", "intermediateLinkNotFound": "中間リンクが見つかりませんでした",
"intermediateLink": "Intermediate link", "intermediateLink": "中間リンク",
"exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)", "exemptFromBackgroundUpdates": "バックグラウンドアップデートを行わない (有効な場合)",
"bgUpdatesOnWiFiOnly": "WiFiを使用していない場合バックグラウンドアップデートを無効にする", "bgUpdatesOnWiFiOnly": "WiFiを使用していない場合バックグラウンドアップデートを無効にする",
"autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する", "autoSelectHighestVersionCode": "最も高いバージョンコードのAPKを自動で選択する",
@@ -262,8 +262,8 @@
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する", "trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
"dontSortReleasesList": "APIからのリリース順を保持する", "dontSortReleasesList": "APIからのリリース順を保持する",
"reverseSort": "逆順ソート", "reverseSort": "逆順ソート",
"takeFirstLink": "Take first link", "takeFirstLink": "最初のリンクを取得する",
"skipSort": "Skip sorting", "skipSort": "ソートをスキップする",
"debugMenu": "デバッグメニュー", "debugMenu": "デバッグメニュー",
"bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。", "bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
"runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する", "runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
@@ -279,14 +279,14 @@
"completeAppInstallationNotifChannel": "アプリのインストールを完了する", "completeAppInstallationNotifChannel": "アプリのインストールを完了する",
"checkingForUpdatesNotifChannel": "アップデートを確認中", "checkingForUpdatesNotifChannel": "アップデートを確認中",
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する", "onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
"supportFixedAPKURL": "Support fixed APK URLs", "supportFixedAPKURL": "固定されたAPKURLをサポートする",
"selectX": "Select {}", "selectX": "{} 選択",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "並行ダウンロードを許可する",
"installMethod": "Installation method", "installMethod": "インストール方法",
"normal": "Normal", "normal": "通常",
"shizuku": "Shizuku", "shizuku": "Shizuku",
"root": "Root", "root": "Root",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizukuが起動していません",
"removeAppQuestion": { "removeAppQuestion": {
"one": "アプリを削除しますか?", "one": "アプリを削除しますか?",
"other": "アプリを削除しますか?" "other": "アプリを削除しますか?"

View File

@@ -1,21 +1,21 @@
{ {
"invalidURLForSource": "URL {} inválida", "invalidURLForSource": "URL {} inválida",
"noReleaseFound": "Não foi possivel encontrar uma versão adequada", "noReleaseFound": "Não foi possível encontrar uma versão adequada",
"noVersionFound": "Não foi possivel encontrar uma versão lançada", "noVersionFound": "Não foi possível encontrar uma versão",
"urlMatchesNoSource": "URL não corresponde a uma fonte conhecida", "urlMatchesNoSource": "URL não corresponde a uma fonte conhecida",
"cantInstallOlderVersion": "Não pode instalar uma versão anterior de um App", "cantInstallOlderVersion": "Não é permitido instalar uma versão anterior de um aplicativo",
"appIdMismatch": "ID do pacote baixado não é igual ao ID do App instalado", "appIdMismatch": "ID do pacote baixado não é igual ao ID do aplicativo instalado",
"functionNotImplemented": "Esta classe não implementou essa função", "functionNotImplemented": "Esta classe não implementou essa função",
"placeholder": "Espaço Reservado", "placeholder": "Espaço reservado",
"someErrors": "Alguns Erros Ocorreram", "someErrors": "Alguns erros ocorreram",
"unexpectedError": "Erro Inesperado", "unexpectedError": "Erro inesperado",
"ok": "Ok", "ok": "OK",
"and": "e", "and": "e",
"githubPATLabel": "Token de Acceso Pessoal do GitHub (Reduz tempos de espera)", "githubPATLabel": "Token de acesso pessoal do GitHub (Reduz tempos de espera)",
"includePrereleases": "Incluir pré-lançamentos", "includePrereleases": "Incluir pré-lançamentos",
"fallbackToOlderReleases": "Retornar para versões anteriores", "fallbackToOlderReleases": "Retornar para versões anteriores",
"filterReleaseTitlesByRegEx": "Filtrar Titulos de Versões por Expressão Regular", "filterReleaseTitlesByRegEx": "Filtrar tulos de versões por expressão regular",
"invalidRegEx": "Expressão Regular Inválida", "invalidRegEx": "Expressão regular inválida",
"noDescription": "Sem descrição", "noDescription": "Sem descrição",
"cancel": "Cancelar", "cancel": "Cancelar",
"continue": "Continuar", "continue": "Continuar",
@@ -25,66 +25,66 @@
"githubStarredRepos": "Favoritados no GitHub", "githubStarredRepos": "Favoritados no GitHub",
"uname": "Nome de usuário", "uname": "Nome de usuário",
"wrongArgNum": "Número de argumentos errado", "wrongArgNum": "Número de argumentos errado",
"xIsTrackOnly": "{} é 'Apenas Seguir'", "xIsTrackOnly": "{} é 'Apenas monitorar'",
"source": "Fonte", "source": "Fonte",
"app": "App", "app": "Aplicativo",
"appsFromSourceAreTrackOnly": "Os apps desta fonte são 'Apenas Seguir'.", "appsFromSourceAreTrackOnly": "Os aplicativos desta fonte são 'Apenas monitorar'.",
"youPickedTrackOnly": "Você selecionou a opção 'Apenas Seguir'.", "youPickedTrackOnly": "Você selecionou a opção 'Apenas monitorar'.",
"trackOnlyAppDescription": "Esse App vai ser seguido por atualizações, mais o Obtainium não poderá baixa-lo ou instala-lo.", "trackOnlyAppDescription": "As atualizações desse aplicativo serão monitoradas, mas o Obtainium não poderá baixá-lo ou instalá-lo.",
"cancelled": "Cancelado", "cancelled": "Cancelado",
"appAlreadyAdded": "App já adicionado", "appAlreadyAdded": "Aplicativo já adicionado",
"alreadyUpToDateQuestion": "App já atualizado?", "alreadyUpToDateQuestion": "Aplicativo já foi atualizado?",
"addApp": "Adicionar App", "addApp": "Adicionar aplicativo",
"appSourceURL": "URL de origem do App", "appSourceURL": "URL de origem do aplicativo",
"error": "Erro", "error": "Erro",
"add": "Adicionar", "add": "Adicionar",
"searchSomeSourcesLabel": "Procurar (Apenas Algumas Fontes)", "searchSomeSourcesLabel": "Procurar (Apenas algumas fontes)",
"search": "Procurar", "search": "Procurar",
"additionalOptsFor": "Opções Adicionais para {}", "additionalOptsFor": "Opções adicionais para {}",
"supportedSources": "Fontes Compatíveis", "supportedSources": "Fontes compatíveis",
"trackOnlyInBrackets": "(Apenas Seguir)", "trackOnlyInBrackets": "(Apenas monitorar)",
"searchableInBrackets": "(Pesquisável)", "searchableInBrackets": "(Pesquisável)",
"appsString": "Apps", "appsString": "Aplicativos",
"noApps": "Sem Apps", "noApps": "Não há aplicativos",
"noAppsForFilter": "Sem Apps para Filtrar", "noAppsForFilter": "Sem aplicativos para filtrar",
"byX": "Por {}", "byX": "Por {}",
"percentProgress": "Progresso: {}%", "percentProgress": "Progresso: {}%",
"pleaseWait": "Por Favor Espere", "pleaseWait": "Por favor, espere",
"updateAvailable": "Atualização Disponível", "updateAvailable": "Atualização disponível",
"estimateInBracketsShort": "(Aprox.)", "estimateInBracketsShort": "(Aprox.)",
"notInstalled": "Não Instalado", "notInstalled": "Não instalado",
"estimateInBrackets": "(Aproximado)", "estimateInBrackets": "(Aproximado)",
"selectAll": "Selecionar All", "selectAll": "Selecionar todos",
"deselectX": "Deselecionar {}", "deselectX": "Deselecionar {}",
"xWillBeRemovedButRemainInstalled": "{} sera removido do Obtainium mais permanecerá instalado no dispositivo.", "xWillBeRemovedButRemainInstalled": "{} será removido do Obtainium mais permanecerá instalado no dispositivo.",
"removeSelectedAppsQuestion": "Remover Apps Selecionados?", "removeSelectedAppsQuestion": "Remover aplicativos selecionados?",
"removeSelectedApps": "Remover Apps Selecionados", "removeSelectedApps": "Remover aplicativos selecionados",
"updateX": "Atualizar {}", "updateX": "Atualizar {}",
"installX": "Instalar {}", "installX": "Instalar {}",
"markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas Seguir)\ncomo Atualizado", "markXTrackOnlyAsUpdated": "Marcar {}\n(Apenas monitorar)\ncomo Atualizado",
"changeX": "Mudar {}", "changeX": "Mudar {}",
"installUpdateApps": "Instalar/Atualizar Apps", "installUpdateApps": "Instalar/Atualizar aplicativos",
"installUpdateSelectedApps": "Instalar/Atualizar Apps Selecionados", "installUpdateSelectedApps": "Instalar/Atualizar aplicativos selecionados",
"markXSelectedAppsAsUpdated": "Marcar {} Apps Delecionados como Atualizados?", "markXSelectedAppsAsUpdated": "Marcar {} aplicativos selecionados como atualizados?",
"no": "Não", "no": "Não",
"yes": "Sim", "yes": "Sim",
"markSelectedAppsUpdated": "Marcar Apps Selecionados como Atualizados", "markSelectedAppsUpdated": "Marcar aplicativos selecionados como Atualizados",
"pinToTop": "Fixar no topo", "pinToTop": "Fixar no topo",
"unpinFromTop": "Desafixar do topo", "unpinFromTop": "Desafixar do topo",
"resetInstallStatusForSelectedAppsQuestion": "Reiniciar Status de Instalação para Apps Seleciondos?", "resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação para aplicativos selecionados?",
"installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer app selecionado sera reiniciado.\n\nIsso pode ajudar quando uma versão de um App mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.", "installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer aplicativo selecionado será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.",
"shareSelectedAppURLs": "Compartilhar URLs de Apps Selecionados", "shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados",
"resetInstallStatus": "Reiniciar Status de Instalação", "resetInstallStatus": "Reiniciar status de Iistalação",
"more": "Mais", "more": "Mais",
"removeOutdatedFilter": "Remover Filtro de Apps Desatualizados", "removeOutdatedFilter": "Remover filtro de aplicativos desatualizados",
"showOutdatedOnly": "Mostrar Apenas Apps Desatualizados", "showOutdatedOnly": "Mostrar apenas aplicativos desatualizados",
"filter": "Filtro", "filter": "Filtro",
"filterActive": "Filtro *", "filterActive": "Filtro *",
"filterApps": "Filtrar Apps", "filterApps": "Filtrar aplicativos",
"appName": "Nome do App", "appName": "Nome do aplicativo",
"author": "Autor", "author": "Autor",
"upToDateApps": "Apps Atualizados", "upToDateApps": "Aplicativos tualizados",
"nonInstalledApps": "Apps Não Instalados", "nonInstalledApps": "Aplicativos não instalados",
"importExport": "Importar/Exportar", "importExport": "Importar/Exportar",
"settings": "Configurações", "settings": "Configurações",
"exportedTo": "Exportado para {}", "exportedTo": "Exportado para {}",
@@ -92,16 +92,16 @@
"invalidInput": "Input Inválido", "invalidInput": "Input Inválido",
"importedX": "Importado {}", "importedX": "Importado {}",
"obtainiumImport": "Importar Obtainium", "obtainiumImport": "Importar Obtainium",
"importFromURLList": "Importar de Lista de URLs", "importFromURLList": "Importar de lista de URLs",
"searchQuery": "Pesquisa", "searchQuery": "Pesquisa",
"appURLList": "Lista de URLs de Apps", "appURLList": "Lista de URLs de aplicativos",
"line": "Linha", "line": "Linha",
"searchX": "Pesquisa {}", "searchX": "Pesquisa {}",
"noResults": "Nenhum resultado encontrado", "noResults": "Nenhum resultado encontrado",
"importX": "Importar {}", "importX": "Importar {}",
"importedAppsIdDisclaimer": "Apps Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do App.\n\nAfeta apenas métodos de importação de URL e de terceiros.", "importedAppsIdDisclaimer": "Aplicativos Importados podem ser mostrados incorretamente como \"Não Instalado\".\nPara consertar, reinstale-os usando o Obtainium.\nIsso não deve afetar dados do aplicativo.\n\nAfeta apenas métodos de importação de URL e de terceiros.",
"importErrors": "Erros de Importação", "importErrors": "Erros de importação",
"importedXOfYApps": "{} de {} Apps importados.", "importedXOfYApps": "{} de {} aplicativos importados.",
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:", "followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
"selectURL": "Selecionar URL", "selectURL": "Selecionar URL",
"selectURLs": "Selecionar URLs", "selectURLs": "Selecionar URLs",
@@ -109,125 +109,125 @@
"theme": "Tema", "theme": "Tema",
"dark": "Escuro", "dark": "Escuro",
"light": "Claro", "light": "Claro",
"followSystem": "Seguir o Sistema", "followSystem": "Seguir o sistema",
"obtainium": "Obtainium", "obtainium": "Obtainium",
"materialYou": "Material You", "materialYou": "Material You",
"useBlackTheme": "Usar tema preto completamente escuro", "useBlackTheme": "Usar tema preto completamente escuro",
"appSortBy": "Classificar App por", "appSortBy": "Classificar aplicativo por",
"authorName": "Autor/Nome", "authorName": "Autor/Nome",
"nameAuthor": "Nome/Autor", "nameAuthor": "Nome/Autor",
"asAdded": "Como Adicionado", "asAdded": "Como adicionado",
"appSortOrder": "Ordem de classificação de Apps", "appSortOrder": "Ordem de classificação de aplicativos",
"ascending": "Ascendente", "ascending": "Ascendente",
"descending": "Descendente", "descending": "Descendente",
"bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo plano", "bgUpdateCheckInterval": "Intervalo de verificação de atualizações em segundo-plano",
"neverManualOnly": "Nunca - Apenas Manual", "neverManualOnly": "Nunca - apenas manual",
"appearance": "Aparência", "appearance": "Aparência",
"showWebInAppView": "Mostrar páginas da internet em App view", "showWebInAppView": "Mostrar página da internet em informações do aplicativo",
"pinUpdates": "Fixar atualizações no topo da visão de Apps", "pinUpdates": "Fixar atualizações no topo da janela de aplicativos",
"updates": "Atualizações", "updates": "Atualizações",
"sourceSpecific": "Específico a fonte", "sourceSpecific": "Específico a fonte",
"appSource": "Fonte do App", "appSource": "Fonte do aplicativo",
"noLogs": "Sem Logs", "noLogs": "Sem logs",
"appLogs": "Logs do App", "appLogs": "Logs do aplicativo",
"close": "Fechar", "close": "Fechar",
"share": "Compartilhar", "share": "Compartilhar",
"appNotFound": "App não encontrado", "appNotFound": "Aplicativo não encontrado",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Selecionar um APK", "pickAnAPK": "Selecionar um APK",
"appHasMoreThanOnePackage": "{} tem mais de um pacote:", "appHasMoreThanOnePackage": "{} tem mais de um pacote:",
"deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.", "deviceSupportsXArch": "Seu dispositivo suporta a arquitetura de CPU {}.",
"deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:", "deviceSupportsFollowingArchs": "Seu dispositivo suporta as seguintes arquiteturas de CPU:",
"warning": "Aviso", "warning": "Aviso",
"sourceIsXButPackageFromYPrompt": "A Fonte do App é '{}' mais o pacote lançado vem de '{}'. Continuar?", "sourceIsXButPackageFromYPrompt": "A fonte do aplicativo é '{}' mas a origem do pacote é '{}'. Continuar?",
"updatesAvailable": "Atualizações Disponíveis", "updatesAvailable": "Atualizações disponíveis",
"updatesAvailableNotifDescription": "Notifica o usuário quando atualizações estão disponíveis um ou mais Apps seguidos pelo Obtainium", "updatesAvailableNotifDescription": "Notifica o usuário quando atualizações de um ou mais aplicativos monitorados pelo Obtainium estão disponíveis",
"noNewUpdates": "Sem novas atualizações.", "noNewUpdates": "Sem novas atualizações.",
"xHasAnUpdate": "{} tem uma atualização.", "xHasAnUpdate": "{} tem uma atualização.",
"appsUpdated": "Apps Atualizados", "appsUpdated": "Aplicativos atualizados",
"appsUpdatedNotifDescription": "Notifica o usuário quando atualizações para um ou mais Apps foram aplicadas em segundo plano", "appsUpdatedNotifDescription": "Notifica o usuário quando atualizações foram aplicadas em segundo-plano para um ou mais aplicativos ",
"xWasUpdatedToY": "{} foi atualizado para {}.", "xWasUpdatedToY": "{} foi atualizado para {}.",
"errorCheckingUpdates": "Erro ao Procurar por Atualizações", "errorCheckingUpdates": "Erro ao procurar por atualizações",
"errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo plano falha", "errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo-plano falha",
"appsRemoved": "Apps Removidos", "appsRemoved": "Aplicativos removidos",
"appsRemovedNotifDescription": "Notifica o usuário quando um ou mais Apps foram removidos devido a erros ao carregá-los", "appsRemovedNotifDescription": "Notifica o usuário quando um ou mais aplicativos foram removidos devido a erros de carregamento",
"xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}", "xWasRemovedDueToErrorY": "{} foi removido devido a este erro: {}",
"completeAppInstallation": "Instalação completa do App", "completeAppInstallation": "Instalação do aplicativo completa",
"obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar Apps", "obtainiumMustBeOpenToInstallApps": "Obtainium deve estar aberto para instalar os aplicativos",
"completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um App", "completeAppInstallationNotifDescription": "Pede ao usuário que retorne ao Obtainium para finalizar a instalação de um aplicativo",
"checkingForUpdates": "Checando por Atualizações", "checkingForUpdates": "Verificando atualizações",
"checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando checando por atualizações", "checkingForUpdatesNotifDescription": "Notificação transiente que aparece quando o Obtainium está verificando se há atualizações",
"pleaseAllowInstallPerm": "Por favor, permita o Obtainium instalar Apps", "pleaseAllowInstallPerm": "Por favor, permita que o Obtainium possa instalar aplicativos",
"trackOnly": "Apenas Seguir", "trackOnly": "Apenas monitorar",
"errorWithHttpStatusCode": "Erro {}", "errorWithHttpStatusCode": "Erro {}",
"versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)", "versionCorrectionDisabled": "Correção de versão desativada (plugin parece não funcionar)",
"unknown": "Desconhecido", "unknown": "Desconhecido",
"none": "Nenhum", "none": "Nenhum",
"never": "Nunca", "never": "Nunca",
"latestVersionX": "Última versão: {}", "latestVersionX": "Última versão: {}",
"installedVersionX": "Versão Instalada: {}", "installedVersionX": "Versão instalada: {}",
"lastUpdateCheckX": "Última Checagem por Atualização: {}", "lastUpdateCheckX": "Última verificação de atualizações: {}",
"remove": "Remover", "remove": "Remover",
"yesMarkUpdated": "Sim, Marcar como Atualizado", "yesMarkUpdated": "Sim, marcar como Atualizado",
"fdroid": "F-Droid Official", "fdroid": "F-Droid Official",
"appIdOrName": "ID do App ou Nome", "appIdOrName": "ID do aplicativo ou nome",
"appId": "ID do App", "appId": "ID do aplicativo",
"appWithIdOrNameNotFound": "Nenhum App foi encontrado com esse ID ou nome", "appWithIdOrNameNotFound": "Nenhum aplicativo foi encontrado com esse ID ou nome",
"reposHaveMultipleApps": "Repositórios podem conter multiplos Apps", "reposHaveMultipleApps": "Repositórios podem conter multiplos aplicativos",
"fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid", "fdroidThirdPartyRepo": "Repositórios de terceiros F-Droid",
"steam": "Steam", "steam": "Steam",
"steamMobile": "Steam Mobile", "steamMobile": "Steam Mobile",
"steamChat": "Steam Chat", "steamChat": "Steam Chat",
"install": "Instalar", "install": "Instalar",
"markInstalled": "Marcar Instalado", "markInstalled": "Marcar instalado",
"update": "Atualizar", "update": "Atualizar",
"markUpdated": "Marcar Atualizado", "markUpdated": "Marcar como atualizado",
"additionalOptions": "Opções Adicionais", "additionalOptions": "Opções adicionais",
"disableVersionDetection": "Desativar Detecção de Versão", "disableVersionDetection": "Desativar detecção de versão",
"noVersionDetectionExplanation": "Essa opção deve apenas ser usada por Apps onde detecção de versão não funciona corretamente.", "noVersionDetectionExplanation": "Essa opção deve apenas ser usada por aplicativos onde a detecção de versão não funciona corretamente.",
"downloadingX": "Baixando {}", "downloadingX": "Baixando {}",
"downloadNotifDescription": "Notifica o usuário do progresso ao baixar um App", "downloadNotifDescription": "Notifica o usuário o progresso do download de um aplicativo",
"noAPKFound": "APK não encontrado", "noAPKFound": "APK não encontrado",
"noVersionDetection": "Sem Detecção de versão", "noVersionDetection": "Sem detecção de versão",
"categorize": "Categorizar", "categorize": "Categorizar",
"categories": "Categorias", "categories": "Categorias",
"category": "Categoria", "category": "Categoria",
"noCategory": "Sem Categoria", "noCategory": "Sem categoria",
"noCategories": "Sem Categoria", "noCategories": "Sem categoria",
"deleteCategoriesQuestion": "Deletar Categorias?", "deleteCategoriesQuestion": "Deletar categorias?",
"categoryDeleteWarning": "Todos os Apps em categorias removidas serão descategorizados.", "categoryDeleteWarning": "Todos os aplicativos em categorias removidas serão descategorizados.",
"addCategory": "Adicionar Categoria", "addCategory": "Adicionar categoria",
"label": "Etiqueta", "label": "Etiqueta",
"language": "Linguagem", "language": "Linguagem",
"copiedToClipboard": "Copiado para a área de transferência", "copiedToClipboard": "Copiado para a área de transferência",
"storagePermissionDenied": "Permição ao armazenamento negada", "storagePermissionDenied": "Permissão de armazenamento negada",
"selectedCategorizeWarning": "Isso vai substituir qualquer confirução de categoria para os Apps selecionados.", "selectedCategorizeWarning": "Isso vai substituir qualquer configuração de categoria para os aplicativos selecionados.",
"filterAPKsByRegEx": "Filtrar APKs por Expressão Regular", "filterAPKsByRegEx": "Filtrar APKs por expressão regular",
"removeFromObtainium": "Remover do Obtainium", "removeFromObtainium": "Remover do Obtainium",
"uninstallFromDevice": "Desinstalar do dispositivo", "uninstallFromDevice": "Desinstalar do dispositivo",
"onlyWorksWithNonVersionDetectApps": "Apenas funciona para Apps com detecção de versão desativada.", "onlyWorksWithNonVersionDetectApps": "Apenas funciona para aplicativos com detecção de versão desativada.",
"releaseDateAsVersion": "Usar Data de Lançamento como Versão", "releaseDateAsVersion": "Usar data de lançamento como versão",
"releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.", "releaseDateAsVersionExplanation": "Esta opção só deve ser usada para aplicativos onde a detecção de versão não funciona corretamente, mas há uma data de lançamento disponível.",
"changes": "Mudanças", "changes": "Mudanças",
"releaseDate": "Data de Lançamento", "releaseDate": "Data de lançamento",
"importFromURLsInFile": "Importar de URLs em Arquivo (como OPML)", "importFromURLsInFile": "Importar de URLs em arquivo (como OPML)",
"versionDetection": "Detecção de Versão", "versionDetection": "Detecção de Versão",
"standardVersionDetection": "Detecção de versão padrão", "standardVersionDetection": "Detecção de versão padrão",
"groupByCategory": "Agroupar por Categoria", "groupByCategory": "Agroupar por categoria",
"autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível", "autoApkFilterByArch": "Tente filtrar APKs por arquitetura de CPU, se possível",
"overrideSource": "Substituir Fonte", "overrideSource": "Substituir fonte",
"dontShowAgain": "Não mostrar isso novamente", "dontShowAgain": "Não mostrar isso novamente",
"dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas Seguir'", "dontShowTrackOnlyWarnings": "Não mostrar avisos 'Apenas Monitorar'",
"dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK", "dontShowAPKOriginWarnings": "Não mostrar avisos de origem da APK",
"moveNonInstalledAppsToBottom": "Mover Apps não instalados para o fundo da visão de Apps", "moveNonInstalledAppsToBottom": "Mover aplicativos não instalados para o fundo da lista de aplicativos",
"gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)", "gitlabPATLabel": "Token de Acceso Pessoal do Gitlab\n(Ativa Pesquisa e Melhor Descoberta de APKs)",
"about": "Sobre", "about": "Sobre",
"requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)", "requiresCredentialsInSettings": "{}: Isso requer credenciais adicionais (em Configurações)",
"checkOnStart": "Checar por atualizações ao iniciar ", "checkOnStart": "Checar por atualizações ao iniciar ",
"tryInferAppIdFromCode": "Tente inferir o ID do App pelo código fonte", "tryInferAppIdFromCode": "Tente inferir o ID do aplicativo pelo código-fonte",
"removeOnExternalUninstall": "Remover automaticamente Apps desinstalados externamente", "removeOnExternalUninstall": "Remover automaticamente aplicativos desinstalados externamente",
"pickHighestVersionCode": "Auto-selecionar o maior numero de versão do APK", "pickHighestVersionCode": "Auto-selecionar o maior numero de versão do APK",
"checkUpdateOnDetailPage": "Checar por atualizações ao abrir a pagina de detalhes de um App", "checkUpdateOnDetailPage": "Checar por atualizações ao abrir a página de detalhes de um aplicativo",
"disablePageTransitions": "Desativar animações de transição de pagina", "disablePageTransitions": "Desativar animações de transição de pagina",
"reversePageTransitions": "Reverter animações de transição de pagina", "reversePageTransitions": "Reverter animações de transição de pagina",
"minStarCount": "Contagem Minima de Estrelas", "minStarCount": "Contagem Minima de Estrelas",
@@ -238,19 +238,19 @@
"sortByLastLinkSegment": "Sort by only the last segment of the link", "sortByLastLinkSegment": "Sort by only the last segment of the link",
"filterReleaseNotesByRegEx": "Filtrar Notas de Lançamento por Expressão Regular", "filterReleaseNotesByRegEx": "Filtrar Notas de Lançamento por Expressão Regular",
"customLinkFilterRegex": "Filtro de Link Personalizado por Expressão Regular (Padrão '.apk$')", "customLinkFilterRegex": "Filtro de Link Personalizado por Expressão Regular (Padrão '.apk$')",
"appsPossiblyUpdated": "Tentativas de atualização de Apps", "appsPossiblyUpdated": "Tentativas de atualização de aplicativos",
"appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais Apps foram potencialmente aplicadas em segundo plano", "appsPossiblyUpdatedNotifDescription": "Notifica o usuário de que atualizações de um ou mais aplicativos foram potencialmente aplicadas em segundo-plano",
"xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.", "xWasPossiblyUpdatedToY": "{} pode ter sido atualizado para {}.",
"enableBackgroundUpdates": "Ativar atualizações em segundo plano", "enableBackgroundUpdates": "Ativar atualizações em segundo-plano",
"backgroundUpdateReqsExplanation": "Atualizações em segundo plano podem não ser possíveis para todos os Apps.", "backgroundUpdateReqsExplanation": "Atualizações em segundo-plano podem não ser possíveis para todos os aplicativos.",
"backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo plano só pode ser determinado quando o Obtainium é aberto.", "backgroundUpdateLimitsExplanation": "O sucesso de uma instalação em segundo-plano só pode ser determinado quando o Obtainium é aberto.",
"verifyLatestTag": "Verifique a 'ultima' etiqueta", "verifyLatestTag": "Verifique a 'ultima' etiqueta",
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit",
"filterByLinkText": "Filter links by link text", "filterByLinkText": "Filter links by link text",
"intermediateLinkNotFound": "Link intermediário não encontrado", "intermediateLinkNotFound": "Link intermediário não encontrado",
"intermediateLink": "Intermediate link", "intermediateLink": "Intermediate link",
"exemptFromBackgroundUpdates": "Isento de atualizações em segundo plano (se ativadas)", "exemptFromBackgroundUpdates": "Isento de atualizações em segundo-plano (se ativadas)",
"bgUpdatesOnWiFiOnly": "Desative atualizações em segundo plano quando não estiver em WiFi", "bgUpdatesOnWiFiOnly": "Desative atualizações em segundo-plano quando não estiver em WiFi",
"autoSelectHighestVersionCode": "Auto-selecionar o maior codigo de versão", "autoSelectHighestVersionCode": "Auto-selecionar o maior codigo de versão",
"versionExtractionRegEx": "RegEx para Extração de Versão", "versionExtractionRegEx": "RegEx para Extração de Versão",
"matchGroupToUse": "Grupo de Seleção para Usar", "matchGroupToUse": "Grupo de Seleção para Usar",
@@ -265,18 +265,18 @@
"takeFirstLink": "Take first link", "takeFirstLink": "Take first link",
"skipSort": "Skip sorting", "skipSort": "Skip sorting",
"debugMenu": "Menu Debug", "debugMenu": "Menu Debug",
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.", "bgTaskStarted": "Tarefa em segundo-plano iniciada - verifique os logs.",
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora", "runBgCheckNow": "Execute a verificação de atualização em segundo-plano agora",
"versionExtractWholePage": "Aplicar Regex de Extração de Versão à Página Inteira", "versionExtractWholePage": "Aplicar Regex de Extração de Versão à Página Inteira",
"installing": "Instalando", "installing": "Instalando",
"skipUpdateNotifications": "Pular notificações de update", "skipUpdateNotifications": "Pular notificações de update",
"updatesAvailableNotifChannel": "Atualizações Disponíveis", "updatesAvailableNotifChannel": "Atualizações Disponíveis",
"appsUpdatedNotifChannel": "Apps Atualizados", "appsUpdatedNotifChannel": "Aplicativos Atualizados",
"appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de Apps", "appsPossiblyUpdatedNotifChannel": "Tentativas de atualização de aplicativos",
"errorCheckingUpdatesNotifChannel": "Erro ao Procurar por Atualizações", "errorCheckingUpdatesNotifChannel": "Erro ao Procurar por Atualizações",
"appsRemovedNotifChannel": "Apps Removidos", "appsRemovedNotifChannel": "Aplicativos Removidos",
"downloadingXNotifChannel": "Baixando {}", "downloadingXNotifChannel": "Baixando {}",
"completeAppInstallationNotifChannel": "Instalação completa do App", "completeAppInstallationNotifChannel": "Instalação completa do aplicativo",
"checkingForUpdatesNotifChannel": "Checando por Atualizações", "checkingForUpdatesNotifChannel": "Checando por Atualizações",
"onlyCheckInstalledOrTrackOnlyApps": "Apenas checar apps instalados e 'Apenas Seguir' por updates", "onlyCheckInstalledOrTrackOnlyApps": "Apenas checar apps instalados e 'Apenas Seguir' por updates",
"supportFixedAPKURL": "Suporte APK com URLs fixas", "supportFixedAPKURL": "Suporte APK com URLs fixas",
@@ -286,26 +286,26 @@
"normal": "Normal", "normal": "Normal",
"shizuku": "Shizuku", "shizuku": "Shizuku",
"root": "Root", "root": "Root",
"shizukuBinderNotFound": "Shizuku não esta rodando", "shizukuBinderNotFound": "Shizuku não está rodando",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remover App?", "one": "Remover aplicativo?",
"other": "Remover Apps?" "other": "Remover aplicativos?"
}, },
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "Muitas solicitações (taxa limitada) - tente novamente em {} minuto", "one": "Muitas solicitações (taxa limitada) - tente novamente em {} minuto",
"other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos" "other": "Muitas solicitações (taxa limitada) - tente novamente em {} minutos"
}, },
"bgUpdateGotErrorRetryInMinutes": { "bgUpdateGotErrorRetryInMinutes": {
"one": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minuto", "one": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minuto",
"other": "A verificação de atualizações em segundo plano encontrou um {}, agendada uma nova verificação em {} minutos" "other": "A verificação de atualizações em segundo-plano encontrou um {}, agendada uma nova verificação em {} minutos"
}, },
"bgCheckFoundUpdatesWillNotifyIfNeeded": { "bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "A verificação de atualizações em segundo plano encontrou {} atualização, o usuário sera notificado caso necessário", "one": "A verificação de atualizações em segundo-plano encontrou {} atualização, o usuário sera notificado caso necessário",
"other": "A verificação de atualizações em segundo plano encontrou {} atualizações, o usuário sera notificado caso necessário" "other": "A verificação de atualizações em segundo-plano encontrou {} atualizações, o usuário sera notificado caso necessário"
}, },
"apps": { "apps": {
"one": "{} App", "one": "{} Aplicativo",
"other": "{} Apps" "other": "{} Aplicativos"
}, },
"url": { "url": {
"one": "{} URL", "one": "{} URL",

View File

@@ -9,7 +9,7 @@
"placeholder": "Giữ chỗ", "placeholder": "Giữ chỗ",
"someErrors": "Đã xảy ra một số lỗi", "someErrors": "Đã xảy ra một số lỗi",
"unexpectedError": "Lỗi không mong đợi", "unexpectedError": "Lỗi không mong đợi",
"ok": "Ôkê", "ok": "OK",
"and": "và", "and": "và",
"githubPATLabel": "Mã thông báo truy cập cá nhân GitHub (Tăng tốc độ giới hạn)", "githubPATLabel": "Mã thông báo truy cập cá nhân GitHub (Tăng tốc độ giới hạn)",
"includePrereleases": "Bao gồm các bản phát hành trước", "includePrereleases": "Bao gồm các bản phát hành trước",
@@ -17,7 +17,7 @@
"filterReleaseTitlesByRegEx": "Lọc tiêu đề bản phát hành theo biểu thức chính quy", "filterReleaseTitlesByRegEx": "Lọc tiêu đề bản phát hành theo biểu thức chính quy",
"invalidRegEx": "Biểu thức chính quy không hợp lệ", "invalidRegEx": "Biểu thức chính quy không hợp lệ",
"noDescription": "Không có mô tả", "noDescription": "Không có mô tả",
"cancel": "Hủy bỏ", "cancel": "Hủy",
"continue": "Tiếp tục", "continue": "Tiếp tục",
"requiredInBrackets": "(Yêu cầu)", "requiredInBrackets": "(Yêu cầu)",
"dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN", "dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN",
@@ -112,17 +112,17 @@
"followSystem": "Theo hệ thống", "followSystem": "Theo hệ thống",
"obtainium": "Obtainium", "obtainium": "Obtainium",
"materialYou": "Material You", "materialYou": "Material You",
"useBlackTheme": "Sử dụng chủ đề tối màu đen thuần túy", "useBlackTheme": "Nền đen",
"appSortBy": "Sắp xếp ứng dụng theo", "appSortBy": "Sắp xếp ứng dụng",
"authorName": "Tác giả/Tên", "authorName": "Tác giả",
"nameAuthor": "Tên/Tác giả", "nameAuthor": "Tên",
"asAdded": "Như đã thêm", "asAdded": "Như đã thêm",
"appSortOrder": "Thứ tự sắp xếp ứng dụng", "appSortOrder": "Thứ tự sắp xếp",
"ascending": "Tăng dần", "ascending": "Tăng dần",
"descending": "Giảm dần", "descending": "Giảm dần",
"bgUpdateCheckInterval": "Khoảng thời gian kiểm tra cập nhật nền", "bgUpdateCheckInterval": "Khoảng thời gian kiểm tra cập nhật nền",
"neverManualOnly": "Không bao giờ - Chỉ thủ công", "neverManualOnly": "Không bao giờ - Chỉ thủ công",
"appearance": "Vẻ ngoài", "appearance": "Hiển thị",
"showWebInAppView": "Hiển thị trang web Nguồn trong chế độ xem Ứng dụng", "showWebInAppView": "Hiển thị trang web Nguồn trong chế độ xem Ứng dụng",
"pinUpdates": "Ghim nội dung cập nhật lên đầu chế độ xem Ứng dụng", "pinUpdates": "Ghim nội dung cập nhật lên đầu chế độ xem Ứng dụng",
"updates": "Cập nhật", "updates": "Cập nhật",
@@ -228,7 +228,7 @@
"removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài", "removeOnExternalUninstall": "Tự động xóa ứng dụng đã gỡ cài đặt bên ngoài",
"pickHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất", "pickHighestVersionCode": "Tự động chọn APK mã phiên bản cao nhất",
"checkUpdateOnDetailPage": "Kiểm tra các bản cập nhật khi mở trang chi tiết Ứng dụng", "checkUpdateOnDetailPage": "Kiểm tra các bản cập nhật khi mở trang chi tiết Ứng dụng",
"disablePageTransitions": "Tắt hoạt ảnh chuyển trang", "disablePageTransitions": "Tắt hiệu ứng chuyển trang",
"reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược", "reversePageTransitions": "Hoạt ảnh chuyển đổi trang đảo ngược",
"minStarCount": "Số lượng sao tối thiểu", "minStarCount": "Số lượng sao tối thiểu",
"addInfoBelow": "Thêm thông tin này vào bên dưới.", "addInfoBelow": "Thêm thông tin này vào bên dưới.",
@@ -286,7 +286,7 @@
"normal": "Normal", "normal": "Normal",
"shizuku": "Shizuku", "shizuku": "Shizuku",
"root": "Root", "root": "Root",
"shizukuBinderNotFound": "Shizuku is not running", "shizukuBinderNotFound": "Shizuku chưa khởi động",
"removeAppQuestion":{ "removeAppQuestion":{
"one": "Gỡ ứng dụng?", "one": "Gỡ ứng dụng?",
"other": "Gỡ ứng dụng?" "other": "Gỡ ứng dụng?"

View File

@@ -22,11 +22,11 @@
"requiredInBrackets": "(必填)", "requiredInBrackets": "(必填)",
"dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项", "dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项",
"colour": "配色", "colour": "配色",
"githubStarredRepos": "GitHub 已星标仓库", "githubStarredRepos": "已星标的 GitHub 仓库",
"uname": "用户名", "uname": "用户名",
"wrongArgNum": "参数数量错误", "wrongArgNum": "参数数量错误",
"xIsTrackOnly": "{}为“仅追踪”模式", "xIsTrackOnly": "{}为“仅追踪”模式",
"source": "源代码", "source": "源",
"app": "应用", "app": "应用",
"appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。", "appsFromSourceAreTrackOnly": "此来源的应用为“仅追踪”模式。",
"youPickedTrackOnly": "您选择了“仅追踪”。", "youPickedTrackOnly": "您选择了“仅追踪”。",
@@ -56,12 +56,12 @@
"estimateInBrackets": "(推测)", "estimateInBrackets": "(推测)",
"selectAll": "全选", "selectAll": "全选",
"deselectX": "取消选择 {}", "deselectX": "取消选择 {}",
"xWillBeRemovedButRemainInstalled": "{} 将从 Obtainium 中删除,但仍安装在您的设备中。", "xWillBeRemovedButRemainInstalled": "{}将从 Obtainium 中删除,但仍安装在您的设备中。",
"removeSelectedAppsQuestion": "是否删除选中的应用?", "removeSelectedAppsQuestion": "是否删除选中的应用?",
"removeSelectedApps": "删除选中的应用", "removeSelectedApps": "删除选中的应用",
"updateX": "更新 {}", "updateX": "更新 {}",
"installX": "安装 {}", "installX": "安装 {}",
"markXTrackOnlyAsUpdated": "将 {}\n仅追踪\n标记为已更新", "markXTrackOnlyAsUpdated": "将{}\n仅追踪\n标记为已更新",
"changeX": "更改 {}", "changeX": "更改 {}",
"installUpdateApps": "安装/更新应用", "installUpdateApps": "安装/更新应用",
"installUpdateSelectedApps": "安装/更新选中的应用", "installUpdateSelectedApps": "安装/更新选中的应用",
@@ -88,17 +88,17 @@
"importExport": "导入/导出", "importExport": "导入/导出",
"settings": "设置", "settings": "设置",
"exportedTo": "已导出至 {}", "exportedTo": "已导出至 {}",
"obtainiumExport": "Obtainium 导出", "obtainiumExport": "导出 Obtainium",
"invalidInput": "无效的输入", "invalidInput": "无效的输入",
"importedX": "已导入 {}", "importedX": "已导入 {}",
"obtainiumImport": "Obtainium 导入", "obtainiumImport": "导入 Obtainium",
"importFromURLList": "从 URL 列表导入", "importFromURLList": "从 URL 列表导入",
"searchQuery": "搜索查询", "searchQuery": "搜索查询",
"appURLList": "应用 URL 列表", "appURLList": "应用 URL 列表",
"line": "行", "line": "行",
"searchX": "搜索 {}", "searchX": "搜索{}",
"noResults": "无结果", "noResults": "无结果",
"importX": "导入 {}", "importX": "导入{}",
"importedAppsIdDisclaimer": "导入的应用可能会错误地显示为“未安装”状态。\n请通过 Obtainium 重新安装这些应用来解决此问题。", "importedAppsIdDisclaimer": "导入的应用可能会错误地显示为“未安装”状态。\n请通过 Obtainium 重新安装这些应用来解决此问题。",
"importErrors": "导入错误", "importErrors": "导入错误",
"importedXOfYApps": "已导入 {} 中的 {} 个应用。", "importedXOfYApps": "已导入 {} 中的 {} 个应用。",
@@ -123,7 +123,7 @@
"bgUpdateCheckInterval": "后台更新检查间隔", "bgUpdateCheckInterval": "后台更新检查间隔",
"neverManualOnly": "手动", "neverManualOnly": "手动",
"appearance": "外观", "appearance": "外观",
"showWebInAppView": "应用详情页显示来源网页", "showWebInAppView": "应用详情页显示来源网页",
"pinUpdates": "将待更新应用置顶", "pinUpdates": "将待更新应用置顶",
"updates": "更新", "updates": "更新",
"sourceSpecific": "来源", "sourceSpecific": "来源",
@@ -135,7 +135,7 @@
"appNotFound": "未找到应用", "appNotFound": "未找到应用",
"obtainiumExportHyphenatedLowercase": "obtainium-export", "obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "选择一个 APK 文件", "pickAnAPK": "选择一个 APK 文件",
"appHasMoreThanOnePackage": "{} 有多个架构可用:", "appHasMoreThanOnePackage": "{}有多个架构可用:",
"deviceSupportsXArch": "您的设备支持 {} 架构。", "deviceSupportsXArch": "您的设备支持 {} 架构。",
"deviceSupportsFollowingArchs": "您的设备支持下列架构:", "deviceSupportsFollowingArchs": "您的设备支持下列架构:",
"warning": "警告", "warning": "警告",
@@ -143,15 +143,15 @@
"updatesAvailable": "更新可用", "updatesAvailable": "更新可用",
"updatesAvailableNotifDescription": "Obtainium 追踪的应用有更新时发送通知", "updatesAvailableNotifDescription": "Obtainium 追踪的应用有更新时发送通知",
"noNewUpdates": "全部应用已是最新。", "noNewUpdates": "全部应用已是最新。",
"xHasAnUpdate": "{} 可以更新了。", "xHasAnUpdate": "{}可以更新了。",
"appsUpdated": "应用已更新", "appsUpdated": "应用已更新",
"appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知", "appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知",
"xWasUpdatedToY": "{} 已更新至 {}。", "xWasUpdatedToY": "{}已更新至 {}。",
"errorCheckingUpdates": "检查更新出错", "errorCheckingUpdates": "检查更新出错",
"errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知", "errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知",
"appsRemoved": "应用已删除", "appsRemoved": "应用已删除",
"appsRemovedNotifDescription": "当应用因加载出错而被删除时发送通知", "appsRemovedNotifDescription": "当应用因加载出错而被删除时发送通知",
"xWasRemovedDueToErrorY": "{} 由于以下错误被删除:{}", "xWasRemovedDueToErrorY": "{}由于以下错误被删除:{}",
"completeAppInstallation": "完成应用安装", "completeAppInstallation": "完成应用安装",
"obtainiumMustBeOpenToInstallApps": "必须启动 Obtainium 才能安装应用", "obtainiumMustBeOpenToInstallApps": "必须启动 Obtainium 才能安装应用",
"completeAppInstallationNotifDescription": "提示返回 Obtainium 以完成应用的安装", "completeAppInstallationNotifDescription": "提示返回 Obtainium 以完成应用的安装",
@@ -159,7 +159,7 @@
"checkingForUpdatesNotifDescription": "检查更新时短暂显示的通知", "checkingForUpdatesNotifDescription": "检查更新时短暂显示的通知",
"pleaseAllowInstallPerm": "请授予 Obtainium 安装应用的权限", "pleaseAllowInstallPerm": "请授予 Obtainium 安装应用的权限",
"trackOnly": "仅追踪", "trackOnly": "仅追踪",
"errorWithHttpStatusCode": "错误 {}", "errorWithHttpStatusCode": "{} 错误",
"versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)", "versionCorrectionDisabled": "禁用版本号更正(插件似乎未起作用)",
"unknown": "未知", "unknown": "未知",
"none": "无", "none": "无",
@@ -185,11 +185,11 @@
"additionalOptions": "附加选项", "additionalOptions": "附加选项",
"disableVersionDetection": "禁用版本检测", "disableVersionDetection": "禁用版本检测",
"noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。", "noVersionDetectionExplanation": "此选项应该仅用于无法进行版本检测的应用。",
"downloadingX": "正在下载{}", "downloadingX": "正在下载{}",
"downloadNotifDescription": "提示应用的下载进度", "downloadNotifDescription": "提示应用的下载进度",
"noAPKFound": "未找到 APK 文件", "noAPKFound": "未找到 APK 文件",
"noVersionDetection": "禁用版本检测", "noVersionDetection": "禁用版本检测",
"categorize": "类", "categorize": "类",
"categories": "类别", "categories": "类别",
"category": "类别", "category": "类别",
"noCategory": "无类别", "noCategory": "无类别",
@@ -217,17 +217,17 @@
"autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件", "autoApkFilterByArch": "如果可能,尝试按设备支持的 CPU 架构筛选 APK 文件",
"overrideSource": "覆盖来源", "overrideSource": "覆盖来源",
"dontShowAgain": "不再显示", "dontShowAgain": "不再显示",
"dontShowTrackOnlyWarnings": "不显示“仅追踪”模式警告", "dontShowTrackOnlyWarnings": "忽略“仅追踪”模式警告",
"dontShowAPKOriginWarnings": "不显示 APK 文件来源警告", "dontShowAPKOriginWarnings": "忽略 APK 文件来源警告",
"moveNonInstalledAppsToBottom": "将未安装应用置底", "moveNonInstalledAppsToBottom": "将未安装应用置底",
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)", "gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
"about": "相关文档", "about": "相关文档",
"requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)", "requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)",
"checkOnStart": "启动时进行一次检查", "checkOnStart": "启动时进行一次检查",
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID", "tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
"removeOnExternalUninstall": "自动删除已卸载的外部应用", "removeOnExternalUninstall": "自动删除列表中已卸载的应用",
"pickHighestVersionCode": "自动选择版本号最高的 APK 文件", "pickHighestVersionCode": "自动选择版本号最高的 APK 文件",
"checkUpdateOnDetailPage": "打开应用详情页时检查更新", "checkUpdateOnDetailPage": "打开应用详情页时进行检查",
"disablePageTransitions": "禁用页面过渡动画效果", "disablePageTransitions": "禁用页面过渡动画效果",
"reversePageTransitions": "反转页面过渡动画效果", "reversePageTransitions": "反转页面过渡动画效果",
"minStarCount": "最小星标数", "minStarCount": "最小星标数",
@@ -275,18 +275,20 @@
"appsPossiblyUpdatedNotifChannel": "已尝试更新应用", "appsPossiblyUpdatedNotifChannel": "已尝试更新应用",
"errorCheckingUpdatesNotifChannel": "检查更新出错", "errorCheckingUpdatesNotifChannel": "检查更新出错",
"appsRemovedNotifChannel": "应用已删除", "appsRemovedNotifChannel": "应用已删除",
"downloadingXNotifChannel": "正在下载{}", "downloadingXNotifChannel": "正在下载{}",
"completeAppInstallationNotifChannel": "完成应用安装", "completeAppInstallationNotifChannel": "完成应用安装",
"checkingForUpdatesNotifChannel": "正在检查更新", "checkingForUpdatesNotifChannel": "正在检查更新",
"onlyCheckInstalledOrTrackOnlyApps": "只已安装和“仅追踪”的应用进行更新检查", "onlyCheckInstalledOrTrackOnlyApps": "只检查已安装和“仅追踪”的应用",
"supportFixedAPKURL": "支持固定的 APK 文件链接", "supportFixedAPKURL": "支持固定的 APK 文件链接",
"selectX": "选择 {}", "selectX": "选择{}",
"parallelDownloads": "启用并行下载", "parallelDownloads": "启用并行下载",
"installMethod": "安装方式", "installMethod": "安装方式",
"normal": "常规", "normal": "常规",
"shizuku": "Shizuku", "shizuku": "Shizuku",
"root": "Root", "root": "Root",
"shizukuBinderNotFound": "Shizuku 服务未运行", "shizukuBinderNotFound": "未发现兼容的 Shizuku 服务",
"useSystemFont": "使用系统字体",
"systemFontError": "加载系统字体出错:{}",
"removeAppQuestion": { "removeAppQuestion": {
"one": "是否删除应用?", "one": "是否删除应用?",
"other": "是否删除应用?" "other": "是否删除应用?"
@@ -328,15 +330,15 @@
"other": "清除了 {n} 个日志({before} 之前,{after} 之后)" "other": "清除了 {n} 个日志({before} 之前,{after} 之后)"
}, },
"xAndNMoreUpdatesAvailable": { "xAndNMoreUpdatesAvailable": {
"one": "{} 和另外 1 个应用可以更新了。", "one": "{}和另外 1 个应用可以更新了。",
"other": "{} 和另外 {} 个应用可以更新了。" "other": "{}和另外 {} 个应用可以更新了。"
}, },
"xAndNMoreUpdatesInstalled": { "xAndNMoreUpdatesInstalled": {
"one": "{} 和另外 1 个应用已更新。", "one": "{} 和另外 1 个应用已更新。",
"other": "{} 和另外 {} 个应用已更新。" "other": "{}和另外 {} 个应用已更新。"
}, },
"xAndNMoreUpdatesPossiblyInstalled": { "xAndNMoreUpdatesPossiblyInstalled": {
"one": "{} 和另外 1 个应用已尝试更新。", "one": "{} 和另外 1 个应用已尝试更新。",
"other": "{} 和另外 {} 个应用已尝试更新。" "other": "{}和另外 {} 个应用已尝试更新。"
} }
} }

View File

@@ -5,17 +5,18 @@ import 'package:obtainium/providers/source_provider.dart';
class APKCombo extends AppSource { class APKCombo extends AppSource {
APKCombo() { APKCombo() {
host = 'apkcombo.com'; hosts = ['apkcombo.com'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/+[^/]+/+[^/]+'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+');
var match = standardUrlRegEx.firstMatch(url.toLowerCase()); var match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@@ -32,7 +33,7 @@ class APKCombo extends AppSource {
"User-Agent": "curl/8.0.1", "User-Agent": "curl/8.0.1",
"Accept": "*/*", "Accept": "*/*",
"Connection": "keep-alive", "Connection": "keep-alive",
"Host": "$host" "Host": hosts[0]
}; };
} }

View File

@@ -9,7 +9,7 @@ import 'package:obtainium/providers/source_provider.dart';
class APKMirror extends AppSource { class APKMirror extends AppSource {
APKMirror() { APKMirror() {
host = 'apkmirror.com'; hosts = ['apkmirror.com'];
enforceTrackOnly = true; enforceTrackOnly = true;
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
@@ -33,12 +33,12 @@ class APKMirror extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?$host/apk/[^/]+/[^/]+'); RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/apk/[^/]+/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@@ -84,7 +84,7 @@ class APKMirror extends AppSource {
dateString != null ? HttpDate.parse('$dateString GMT') : null; dateString != null ? HttpDate.parse('$dateString GMT') : null;
String? version = titleString String? version = titleString
?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0, ?.substring(RegExp('[0-9]').firstMatch(titleString)?.start ?? 0,
RegExp(' by ').firstMatch(titleString)?.start ?? 0) RegExp(' by ').allMatches(titleString).last.start)
.trim(); .trim();
if (version == null || version.isEmpty) { if (version == null || version.isEmpty) {
version = titleString; version = titleString;

View File

@@ -20,7 +20,7 @@ parseDateTimeMMMddCommayyyy(String? dateString) {
class APKPure extends AppSource { class APKPure extends AppSource {
APKPure() { APKPure() {
host = 'apkpure.com'; hosts = ['apkpure.net', 'apkpure.com'];
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
} }
@@ -28,18 +28,18 @@ class APKPure extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp standardUrlRegExB =
RegExp('^https?://m.$host/+[^/]+/+[^/]+(/+[^/]+)?'); RegExp('^https?://m.${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?');
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
if (match != null) { if (match != null) {
url = 'https://$host${Uri.parse(url).path}'; url = 'https://${getSourceRegex(hosts)}${Uri.parse(url).path}';
} }
RegExp standardUrlRegExA = RegExp standardUrlRegExA = RegExp(
RegExp('^https?://(www\\.)?$host/+[^/]+/+[^/]+(/+[^/]+)?'); '^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+[^/]+(/+[^/]+)?');
match = standardUrlRegExA.firstMatch(url.toLowerCase()); match = standardUrlRegExA.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -6,7 +6,7 @@ import 'package:obtainium/providers/source_provider.dart';
class Aptoide extends AppSource { class Aptoide extends AppSource {
Aptoide() { Aptoide() {
host = 'aptoide.com'; hosts = ['aptoide.com'];
name = 'Aptoide'; name = 'Aptoide';
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
@@ -14,12 +14,13 @@ class Aptoide extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host'); RegExp standardUrlRegEx =
RegExp('^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -5,7 +5,7 @@ import 'package:obtainium/providers/source_provider.dart';
class Codeberg extends AppSource { class Codeberg extends AppSource {
GitHub gh = GitHub(); GitHub gh = GitHub();
Codeberg() { Codeberg() {
host = 'codeberg.org'; hosts = ['codeberg.org'];
additionalSourceAppSpecificSettingFormItems = additionalSourceAppSpecificSettingFormItems =
gh.additionalSourceAppSpecificSettingFormItems; gh.additionalSourceAppSpecificSettingFormItems;
@@ -16,12 +16,13 @@ class Codeberg extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@@ -35,7 +36,7 @@ class Codeberg extends AppSource {
) async { ) async {
return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings, return await gh.getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
(bool useTagUrl) async { (bool useTagUrl) async {
return 'https://$host/api/v1/repos${standardUrl.substring('https://$host'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100'; return 'https://${hosts[0]}/api/v1/repos${standardUrl.substring('https://${hosts[0]}'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
}, null); }, null);
} }
@@ -50,7 +51,7 @@ class Codeberg extends AppSource {
{Map<String, dynamic> querySettings = const {}}) async { {Map<String, dynamic> querySettings = const {}}) async {
return gh.searchCommon( return gh.searchCommon(
query, query,
'https://$host/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100', 'https://${hosts[0]}/api/v1/repos/search?q=${Uri.encodeQueryComponent(query)}&limit=100',
'data', 'data',
querySettings: querySettings); querySettings: querySettings);
} }

View File

@@ -9,7 +9,7 @@ import 'package:obtainium/providers/source_provider.dart';
class FDroid extends AppSource { class FDroid extends AppSource {
FDroid() { FDroid() {
host = 'f-droid.org'; hosts = ['f-droid.org'];
name = tr('fdroid'); name = tr('fdroid');
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
canSearch = true; canSearch = true;
@@ -37,20 +37,20 @@ class FDroid extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp standardUrlRegExB = RegExp(
RegExp('^https?://(www\\.)?$host/+[^/]+/+packages/+[^/]+'); '^https?://(www\\.)?${getSourceRegex(hosts)}/+[^/]+/+packages/+[^/]+');
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
if (match != null) { if (match != null) {
url = url =
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}'; 'https://${Uri.parse(match.group(0)!).host}/packages/${Uri.parse(url).pathSegments.last}';
} }
RegExp standardUrlRegExA = RegExp standardUrlRegExA =
RegExp('^https?://(www\\.)?$host/+packages/+[^/]+'); RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/+packages/+[^/]+');
match = standardUrlRegExA.firstMatch(url.toLowerCase()); match = standardUrlRegExA.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@@ -85,17 +85,30 @@ class FDroid extends AppSource {
try { try {
var res = await sourceRequest( var res = await sourceRequest(
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml'); 'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml');
String author = res.body var lines = res.body.split('\n');
.split('\n') String author = lines
.where((l) => l.startsWith('AuthorName: ')) .where((l) => l.startsWith('AuthorName: '))
.first .first
.split(': ') .split(': ')
.sublist(1) .sublist(1)
.join(': '); .join(': ');
details.names.author = author; details.names.author = author;
var changelogUrls = lines.where((l) => l.startsWith('Changelog: '));
if (changelogUrls.isNotEmpty) {
details.changeLog = changelogUrls.first;
details.changeLog = (await sourceRequest(details.changeLog!
.split(': ')
.sublist(1)
.join(': ')
.replaceFirst('/blob/', '/raw/')))
.body;
}
} catch (e) { } catch (e) {
// Fail silently // Fail silently
} }
if ((details.changeLog?.length ?? 0) > 1000) {
details.changeLog = '${details.changeLog!.substring(0, 2048)}...';
}
} }
return details; return details;
} }
@@ -104,7 +117,7 @@ class FDroid extends AppSource {
Future<Map<String, List<String>>> search(String query, Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async { {Map<String, dynamic> querySettings = const {}}) async {
Response res = await sourceRequest( Response res = await sourceRequest(
'https://search.$host/?q=${Uri.encodeQueryComponent(query)}'); 'https://search.${hosts[0]}/?q=${Uri.encodeQueryComponent(query)}');
if (res.statusCode == 200) { if (res.statusCode == 200) {
Map<String, List<String>> urlsWithDescriptions = {}; Map<String, List<String>> urlsWithDescriptions = {};
parse(res.body).querySelectorAll('.package-header').forEach((e) { parse(res.body).querySelectorAll('.package-header').forEach((e) {

View File

@@ -14,7 +14,7 @@ import 'package:url_launcher/url_launcher_string.dart';
class GitHub extends AppSource { class GitHub extends AppSource {
GitHub() { GitHub() {
host = 'github.com'; hosts = ['github.com'];
appIdInferIsOptional = true; appIdInferIsOptional = true;
sourceConfigSettingFormItems = [ sourceConfigSettingFormItems = [
@@ -149,12 +149,13 @@ class GitHub extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@@ -203,11 +204,11 @@ class GitHub extends AppSource {
} }
Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async => Future<String> getAPIHost(Map<String, dynamic> additionalSettings) async =>
'https://api.$host'; 'https://api.${hosts[0]}';
Future<String> convertStandardUrlToAPIUrl( Future<String> convertStandardUrlToAPIUrl(
String standardUrl, Map<String, dynamic> additionalSettings) async => String standardUrl, Map<String, dynamic> additionalSettings) async =>
'${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://$host'.length)}'; '${await getAPIHost(additionalSettings)}/repos${standardUrl.substring('https://${hosts[0]}'.length)}';
@override @override
String? changeLogPageFromStandardUrl(String standardUrl) => String? changeLogPageFromStandardUrl(String standardUrl) =>

View File

@@ -13,7 +13,7 @@ import 'package:url_launcher/url_launcher_string.dart';
class GitLab extends AppSource { class GitLab extends AppSource {
GitLab() { GitLab() {
host = 'gitlab.com'; hosts = ['gitlab.com'];
canSearch = true; canSearch = true;
sourceConfigSettingFormItems = [ sourceConfigSettingFormItems = [
@@ -52,12 +52,13 @@ class GitLab extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async { Future<String?> getPATIfAny(Map<String, dynamic> additionalSettings) async {
@@ -81,7 +82,7 @@ class GitLab extends AppSource {
Future<Map<String, List<String>>> search(String query, Future<Map<String, List<String>>> search(String query,
{Map<String, dynamic> querySettings = const {}}) async { {Map<String, dynamic> querySettings = const {}}) async {
var url = var url =
'https://$host/api/v4/projects?search=${Uri.encodeQueryComponent(query)}'; 'https://${hosts[0]}/api/v4/projects?search=${Uri.encodeQueryComponent(query)}';
var res = await sourceRequest(url); var res = await sourceRequest(url);
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
@@ -89,7 +90,7 @@ class GitLab extends AppSource {
var json = jsonDecode(res.body) as List<dynamic>; var json = jsonDecode(res.body) as List<dynamic>;
Map<String, List<String>> results = {}; Map<String, List<String>> results = {};
for (var element in json) { for (var element in json) {
results['https://$host/${element['path_with_namespace']}'] = [ results['https://${hosts[0]}/${element['path_with_namespace']}'] = [
element['name_with_namespace'], element['name_with_namespace'],
element['description'] ?? tr('noDescription') element['description'] ?? tr('noDescription')
]; ];
@@ -113,7 +114,7 @@ class GitLab extends AppSource {
if (PAT != null) { if (PAT != null) {
var names = GitHub().getAppNames(standardUrl); var names = GitHub().getAppNames(standardUrl);
Response res = await sourceRequest( Response res = await sourceRequest(
'https://$host/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT'); 'https://${hosts[0]}/api/v4/projects/${names.author}%2F${names.name}/releases?private_token=$PAT');
if (res.statusCode != 200) { if (res.statusCode != 200) {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }

View File

@@ -17,13 +17,19 @@ String ensureAbsoluteUrl(String ambiguousUrl, Uri referenceAbsoluteUrl) {
.split('/') .split('/')
.where((element) => element.trim().isNotEmpty) .where((element) => element.trim().isNotEmpty)
.toList(); .toList();
String absoluteUrl;
if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) { if (ambiguousUrl.startsWith('/') || currPathSegments.isEmpty) {
return '${referenceAbsoluteUrl.origin}/$ambiguousUrl'; absoluteUrl = '${referenceAbsoluteUrl.origin}$ambiguousUrl';
} else if (currPathSegments.isEmpty) {
absoluteUrl = '${referenceAbsoluteUrl.origin}/$ambiguousUrl';
} else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) { } else if (ambiguousUrl.split('/').where((e) => e.isNotEmpty).length == 1) {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl'; absoluteUrl =
'${referenceAbsoluteUrl.origin}/${currPathSegments.join('/')}/$ambiguousUrl';
} else { } else {
return '${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl'; absoluteUrl =
'${referenceAbsoluteUrl.origin}/${currPathSegments.sublist(0, currPathSegments.length - (currPathSegments.last.contains('.') ? 1 : 0)).join('/')}/$ambiguousUrl';
} }
return Uri.parse(absoluteUrl).toString();
} }
int compareAlphaNumeric(String a, String b) { int compareAlphaNumeric(String a, String b) {
@@ -172,6 +178,8 @@ class HTML extends AppSource {
? element.text ? element.text
: (element.attributes['href'] ?? '').split('/').last)) : (element.attributes['href'] ?? '').split('/').last))
.where((element) => element.key.isNotEmpty) .where((element) => element.key.isNotEmpty)
.map((e) =>
MapEntry(ensureAbsoluteUrl(e.key, res.request!.url), e.value))
.toList(); .toList();
if (allLinks.isEmpty) { if (allLinks.isEmpty) {
allLinks = RegExp( allLinks = RegExp(
@@ -258,7 +266,6 @@ class HTML extends AppSource {
additionalSettings['versionExtractWholePage'] == true additionalSettings['versionExtractWholePage'] == true
? res.body.split('\r\n').join('\n').split('\n').join('\\n') ? res.body.split('\r\n').join('\n').split('\n').join('\\n')
: rel); : rel);
rel = ensureAbsoluteUrl(rel, uri);
version ??= (await checkDownloadHash(rel)).toString(); version ??= (await checkDownloadHash(rel)).toString();
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(), return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
AppNames(uri.host, tr('app'))); AppNames(uri.host, tr('app')));

View File

@@ -6,23 +6,24 @@ import 'package:obtainium/providers/source_provider.dart';
class HuaweiAppGallery extends AppSource { class HuaweiAppGallery extends AppSource {
HuaweiAppGallery() { HuaweiAppGallery() {
name = 'Huawei AppGallery'; name = 'Huawei AppGallery';
host = 'appgallery.huawei.com'; hosts = ['appgallery.huawei.com'];
overrideVersionDetectionFormDefault('releaseDateAsVersion', overrideVersionDetectionFormDefault('releaseDateAsVersion',
disableStandard: true); disableStandard: true);
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/app/[^/]+'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/app/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
getDlUrl(String standardUrl) => getDlUrl(String standardUrl) =>
'https://${host!.replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}'; 'https://${hosts[0].replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}';
requestAppdlRedirect(String dlUrl) async { requestAppdlRedirect(String dlUrl) async {
Response res = await sourceRequest(dlUrl, followRedirects: false); Response res = await sourceRequest(dlUrl, followRedirects: false);

View File

@@ -6,7 +6,7 @@ class IzzyOnDroid extends AppSource {
late FDroid fd; late FDroid fd;
IzzyOnDroid() { IzzyOnDroid() {
host = 'izzysoft.de'; hosts = ['izzysoft.de'];
fd = FDroid(); fd = FDroid();
additionalSourceAppSpecificSettingFormItems = additionalSourceAppSpecificSettingFormItems =
fd.additionalSourceAppSpecificSettingFormItems; fd.additionalSourceAppSpecificSettingFormItems;
@@ -15,17 +15,18 @@ class IzzyOnDroid extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExA = RegExp('^https?://android.$host/repo/apk/[^/]+'); RegExp standardUrlRegExA =
RegExp('^https?://android.${getSourceRegex(hosts)}/repo/apk/[^/]+');
RegExpMatch? match = standardUrlRegExA.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegExA.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
RegExp standardUrlRegExB = RegExp standardUrlRegExB = RegExp(
RegExp('^https?://apt.$host/fdroid/index/apk/[^/]+'); '^https?://apt.${getSourceRegex(hosts)}/fdroid/index/apk/[^/]+');
match = standardUrlRegExB.firstMatch(url.toLowerCase()); match = standardUrlRegExB.firstMatch(url.toLowerCase());
} }
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -16,7 +16,7 @@ class Jenkins extends AppSource {
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -6,17 +6,18 @@ import 'package:obtainium/providers/source_provider.dart';
class Mullvad extends AppSource { class Mullvad extends AppSource {
Mullvad() { Mullvad() {
host = 'mullvad.net'; hosts = ['mullvad.net'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -5,18 +5,18 @@ import 'package:obtainium/providers/source_provider.dart';
class NeutronCode extends AppSource { class NeutronCode extends AppSource {
NeutronCode() { NeutronCode() {
host = 'neutroncode.com'; hosts = ['neutroncode.com'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp standardUrlRegEx = RegExp(
RegExp('^https?://(www\\.)?$host/downloads/file/[^/]+'); '^https?://(www\\.)?${getSourceRegex(hosts)}/downloads/file/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override
@@ -92,7 +92,7 @@ class NeutronCode extends AppSource {
if (version == null) { if (version == null) {
throw NoVersionError(); throw NoVersionError();
} }
String? apkUrl = 'https://$host/download/$filename'; String? apkUrl = 'https://${hosts[0]}/download/$filename';
var dateStringOriginal = var dateStringOriginal =
http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml; http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml;
var dateString = dateStringOriginal != null var dateString = dateStringOriginal != null

View File

@@ -5,12 +5,12 @@ import 'package:obtainium/providers/source_provider.dart';
class Signal extends AppSource { class Signal extends AppSource {
Signal() { Signal() {
host = 'signal.org'; hosts = ['signal.org'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override
@@ -19,7 +19,7 @@ class Signal extends AppSource {
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = Response res =
await sourceRequest('https://updates.$host/android/latest.json'); await sourceRequest('https://updates.${hosts[0]}/android/latest.json');
if (res.statusCode == 200) { if (res.statusCode == 200) {
var json = jsonDecode(res.body); var json = jsonDecode(res.body);
String? apkUrl = json['url']; String? apkUrl = json['url'];

View File

@@ -5,24 +5,25 @@ import 'package:obtainium/providers/source_provider.dart';
class SourceForge extends AppSource { class SourceForge extends AppSource {
SourceForge() { SourceForge() {
host = 'sourceforge.net'; hosts = ['sourceforge.net'];
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegExB = RegExp('^https?://(www\\.)?$host/p/[^/]+'); RegExp standardUrlRegExB =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/p/[^/]+');
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
if (match != null) { if (match != null) {
url = url =
'https://${Uri.parse(url.substring(0, match.end)).host}/projects/${url.substring(Uri.parse(url.substring(0, match.end)).host.length + '/projects/'.length + 1)}'; 'https://${Uri.parse(match.group(0)!).host}/projects/${url.substring(Uri.parse(match.group(0)!).host.length + '/projects/'.length + 1)}';
} }
RegExp standardUrlRegExA = RegExp standardUrlRegExA =
RegExp('^https?://(www\\.)?$host/projects/[^/]+'); RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/projects/[^/]+');
match = standardUrlRegExA.firstMatch(url.toLowerCase()); match = standardUrlRegExA.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -8,7 +8,7 @@ import 'package:easy_localization/easy_localization.dart';
class SourceHut extends AppSource { class SourceHut extends AppSource {
SourceHut() { SourceHut() {
host = 'git.sr.ht'; hosts = ['git.sr.ht'];
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
@@ -20,12 +20,13 @@ class SourceHut extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+'); RegExp standardUrlRegEx =
RegExp('^https?://(www\\.)?${getSourceRegex(hosts)}/[^/]+/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return url.substring(0, match.end); return match.group(0)!;
} }
@override @override

View File

@@ -7,7 +7,7 @@ import 'package:obtainium/providers/source_provider.dart';
class SteamMobile extends AppSource { class SteamMobile extends AppSource {
SteamMobile() { SteamMobile() {
host = 'store.steampowered.com'; hosts = ['store.steampowered.com'];
name = tr('steam'); name = tr('steam');
additionalSourceAppSpecificSettingFormItems = [ additionalSourceAppSpecificSettingFormItems = [
[ [
@@ -21,7 +21,7 @@ class SteamMobile extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override
@@ -29,7 +29,7 @@ class SteamMobile extends AppSource {
String standardUrl, String standardUrl,
Map<String, dynamic> additionalSettings, Map<String, dynamic> additionalSettings,
) async { ) async {
Response res = await sourceRequest('https://$host/mobile'); Response res = await sourceRequest('https://${hosts[0]}/mobile');
if (res.statusCode == 200) { if (res.statusCode == 200) {
var apkNamePrefix = additionalSettings['app'] as String?; var apkNamePrefix = additionalSettings['app'] as String?;
if (apkNamePrefix == null) { if (apkNamePrefix == null) {

View File

@@ -6,13 +6,13 @@ import 'package:obtainium/providers/source_provider.dart';
class TelegramApp extends AppSource { class TelegramApp extends AppSource {
TelegramApp() { TelegramApp() {
host = 'telegram.org'; hosts = ['telegram.org'];
name = 'Telegram ${tr('app')}'; name = 'Telegram ${tr('app')}';
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override

View File

@@ -6,19 +6,20 @@ import 'package:obtainium/providers/source_provider.dart';
class Uptodown extends AppSource { class Uptodown extends AppSource {
Uptodown() { Uptodown() {
host = 'uptodown.com'; hosts = ['uptodown.com'];
allowSubDomains = true; allowSubDomains = true;
naiveStandardVersionDetection = true; naiveStandardVersionDetection = true;
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://([^\\.]+\\.){2,}$host'); RegExp standardUrlRegEx =
RegExp('^https?://([^\\.]+\\.){2,}${getSourceRegex(hosts)}');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase()); RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) { if (match == null) {
throw InvalidURLError(name); throw InvalidURLError(name);
} }
return '${url.substring(0, match.end)}/android/download'; return '${match.group(0)!}/android/download';
} }
@override @override
@@ -94,6 +95,6 @@ class Uptodown extends AppSource {
if (finalUrlKey == null) { if (finalUrlKey == null) {
throw NoAPKError(); throw NoAPKError();
} }
return 'https://dw.$host/dwn/$finalUrlKey'; return 'https://dw.${hosts[0]}/dwn/$finalUrlKey';
} }
} }

View File

@@ -7,9 +7,9 @@ import 'package:obtainium/providers/source_provider.dart';
class VLC extends AppSource { class VLC extends AppSource {
VLC() { VLC() {
host = 'videolan.org'; hosts = ['videolan.org'];
} }
get dwUrlBase => 'https://get.$host/vlc-android/'; get dwUrlBase => 'https://get.${hosts[0]}/vlc-android/';
@override @override
Future<Map<String, String>?> getRequestHeaders( Future<Map<String, String>?> getRequestHeaders(
@@ -21,7 +21,7 @@ class VLC extends AppSource {
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
Future<String?> getLatestVersion(String standardUrl) async { Future<String?> getLatestVersion(String standardUrl) async {

View File

@@ -5,14 +5,14 @@ import 'package:obtainium/providers/source_provider.dart';
class WhatsApp extends AppSource { class WhatsApp extends AppSource {
WhatsApp() { WhatsApp() {
host = 'whatsapp.com'; hosts = ['whatsapp.com'];
overrideVersionDetectionFormDefault('noVersionDetection', overrideVersionDetectionFormDefault('noVersionDetection',
disableStandard: true, disableRelDate: true); disableStandard: true, disableRelDate: true);
} }
@override @override
String sourceSpecificStandardizeURL(String url) { String sourceSpecificStandardizeURL(String url) {
return 'https://$host'; return 'https://${hosts[0]}';
} }
@override @override

View File

@@ -123,7 +123,7 @@ class GeneratedFormSubForm extends GeneratedFormItem {
final List<List<GeneratedFormItem>> items; final List<List<GeneratedFormItem>> items;
GeneratedFormSubForm(super.key, this.items, GeneratedFormSubForm(super.key, this.items,
{super.label, super.belowWidgets, super.defaultValue}); {super.label, super.belowWidgets, super.defaultValue = const []});
@override @override
ensureType(val) { ensureType(val) {

View File

@@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports // ignore: implementation_imports
import 'package:easy_localization/src/localization.dart'; import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.15.4'; const String currentVersion = '0.15.8';
const String currentReleaseTag = const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
@@ -236,17 +236,15 @@ class _ObtainiumState extends State<Obtainium> {
colorScheme: settingsProvider.theme == ThemeSettings.dark colorScheme: settingsProvider.theme == ThemeSettings.dark
? darkColorScheme ? darkColorScheme
: lightColorScheme, : lightColorScheme,
fontFamily: settingsProvider.useSystemFont fontFamily:
? 'SystemFont' settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
: 'Metropolis'),
darkTheme: ThemeData( darkTheme: ThemeData(
useMaterial3: true, useMaterial3: true,
colorScheme: settingsProvider.theme == ThemeSettings.light colorScheme: settingsProvider.theme == ThemeSettings.light
? lightColorScheme ? lightColorScheme
: darkColorScheme, : darkColorScheme,
fontFamily: settingsProvider.useSystemFont fontFamily:
? 'SystemFont' settingsProvider.useSystemFont ? 'SystemFont' : 'Metropolis'),
: 'Metropolis'),
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{ home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
}, child: const HomePage())); }, child: const HomePage()));

View File

@@ -59,7 +59,9 @@ class AddAppPageState extends State<AddAppPage> {
if (updateUrlInput) { if (updateUrlInput) {
urlInputKey++; urlInputKey++;
} }
var prevHost = pickedSource?.host; var prevHost = pickedSource?.hosts.isNotEmpty == true
? pickedSource?.hosts[0]
: null;
try { try {
var naturalSource = var naturalSource =
valid ? sourceProvider.getSource(userInput) : null; valid ? sourceProvider.getSource(userInput) : null;
@@ -77,7 +79,7 @@ class AddAppPageState extends State<AddAppPage> {
overrideSource: pickedSourceOverride) overrideSource: pickedSourceOverride)
: null; : null;
if (pickedSource.runtimeType != source.runtimeType || if (pickedSource.runtimeType != source.runtimeType ||
(prevHost != null && prevHost != source?.host)) { (prevHost != null && prevHost != source?.hosts[0])) {
pickedSource = source; pickedSource = source;
additionalSettings = source != null additionalSettings = source != null
? getDefaultValuesFromFormItems( ? getDefaultValuesFromFormItems(
@@ -508,16 +510,16 @@ class AddAppPageState extends State<AddAppPage> {
height: 16, height: 16,
), ),
...sourceProvider.sources.map((e) => GestureDetector( ...sourceProvider.sources.map((e) => GestureDetector(
onTap: e.host != null onTap: e.hosts.isNotEmpty
? () { ? () {
launchUrlString('https://${e.host}', launchUrlString('https://${e.hosts[0]}',
mode: LaunchMode.externalApplication); mode: LaunchMode.externalApplication);
} }
: null, : null,
child: Text( child: Text(
'${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}', '${e.name}${e.enforceTrackOnly ? ' ${tr('trackOnlyInBrackets')}' : ''}${e.canSearch ? ' ${tr('searchableInBrackets')}' : ''}',
style: TextStyle( style: TextStyle(
decoration: e.host != null decoration: e.hosts.isNotEmpty
? TextDecoration.underline ? TextDecoration.underline
: TextDecoration.none, : TextDecoration.none,
fontStyle: FontStyle.italic), fontStyle: FontStyle.italic),

View File

@@ -199,10 +199,11 @@ class _ImportExportPageState extends State<ImportExportPage> {
...source.searchQuerySettingFormItems.map((e) => [e]), ...source.searchQuerySettingFormItems.map((e) => [e]),
[ [
GeneratedFormTextField('url', GeneratedFormTextField('url',
label: source.host != null label: source.hosts.isNotEmpty
? tr('overrideSource') ? tr('overrideSource')
: plural('url', 1).substring(2), : plural('url', 1).substring(2),
defaultValue: source.host ?? '', defaultValue:
source.hosts.isNotEmpty ? source.hosts[0] : '',
required: true) required: true)
], ],
], ],
@@ -212,7 +213,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
setState(() { setState(() {
importInProgress = true; importInProgress = true;
}); });
if (values['url'] != source.host) { if (values['url'] != source.hosts[0]) {
source = sourceProvider.getSource(values['url'], source = sourceProvider.getSource(values['url'],
overrideSource: source.runtimeType.toString()); overrideSource: source.runtimeType.toString());
} }

View File

@@ -527,9 +527,10 @@ class AppsProvider with ChangeNotifier {
? 0 ? 0
: 1; : 1;
case InstallMethodSettings.root: case InstallMethodSettings.root:
code = (await NativeFeatures.installWithRoot(apkFilePath: file.file.path)) code =
? 0 (await NativeFeatures.installWithRoot(apkFilePath: file.file.path))
: 1; ? 0
: 1;
} }
bool installed = false; bool installed = false;
if (code != null && code != 0 && code != 3) { if (code != null && code != 0 && code != 3) {
@@ -1300,6 +1301,9 @@ class AppsProvider with ChangeNotifier {
settingsProvider.prefs?.setInt(key, value); settingsProvider.prefs?.setInt(key, value);
} else if (value is bool) { } else if (value is bool) {
settingsProvider.prefs?.setBool(key, value); settingsProvider.prefs?.setBool(key, value);
} else if (value is List) {
settingsProvider.prefs
?.setStringList(key, value.map((e) => e as String).toList());
} else { } else {
settingsProvider.prefs?.setString(key, value as String); settingsProvider.prefs?.setString(key, value as String);
} }

View File

@@ -14,7 +14,7 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:shared_storage/shared_storage.dart' as saf; import 'package:shared_storage/shared_storage.dart' as saf;
String obtainiumTempId = 'imranr98_obtainium_${GitHub().host}'; String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}';
String obtainiumId = 'dev.imranr.obtainium'; String obtainiumId = 'dev.imranr.obtainium';
enum InstallMethodSettings { normal, shizuku, root } enum InstallMethodSettings { normal, shizuku, root }

View File

@@ -163,6 +163,24 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
return e['customLinkFilterRegex']?.isNotEmpty == true; return e['customLinkFilterRegex']?.isNotEmpty == true;
}).toList(); }).toList();
} }
// Steam source apps should be converted to HTML (#1244)
var legacySteamSourceApps = SteamMobile().apks.keys;
if (legacySteamSourceApps.contains(additionalSettings['app'] ?? '')) {
json['url'] = '${json['url']}/mobile';
var replacementAdditionalSettings = getDefaultValuesFromFormItems(
HTML().combinedAppSpecificSettingFormItems);
for (var s in replacementAdditionalSettings.keys) {
if (additionalSettings.containsKey(s)) {
replacementAdditionalSettings[s] = additionalSettings[s];
}
}
replacementAdditionalSettings['customLinkFilterRegex'] =
'/${additionalSettings['app']}-(([0-9]+\\.?){1,})\\.apk';
replacementAdditionalSettings['versionExtractionRegEx'] =
replacementAdditionalSettings['customLinkFilterRegex'];
replacementAdditionalSettings['matchGroupToUse'] = '\$1';
additionalSettings = replacementAdditionalSettings;
}
} }
json['additionalSettings'] = jsonEncode(additionalSettings); json['additionalSettings'] = jsonEncode(additionalSettings);
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately // F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
@@ -348,8 +366,12 @@ List<MapEntry<String, String>> getApkUrlsFromUrls(List<String> urls) =>
return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e); return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e);
}).toList(); }).toList();
getSourceRegex(List<String> hosts) {
return '(${hosts.join('|').replaceAll('.', '\\.')})';
}
abstract class AppSource { abstract class AppSource {
String? host; List<String> hosts = [];
bool hostChanged = false; bool hostChanged = false;
late String name; late String name;
bool enforceTrackOnly = false; bool enforceTrackOnly = false;
@@ -658,13 +680,11 @@ class SourceProvider {
APKMirror(), APKMirror(),
HuaweiAppGallery(), HuaweiAppGallery(),
Jenkins(), Jenkins(),
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
Mullvad(), Mullvad(),
Signal(), Signal(),
VLC(), VLC(),
WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date WhatsApp(),
TelegramApp(), TelegramApp(),
SteamMobile(),
NeutronCode(), NeutronCode(),
HTML() // This should ALWAYS be the last option as they are tried in order HTML() // This should ALWAYS be the last option as they are tried in order
]; ];
@@ -681,14 +701,14 @@ class SourceProvider {
throw UnsupportedURLError(); throw UnsupportedURLError();
} }
var res = srcs.first; var res = srcs.first;
res.host = Uri.parse(url).host; res.hosts = [Uri.parse(url).host];
res.hostChanged = true; res.hostChanged = true;
return srcs.first; return srcs.first;
} }
AppSource? source; AppSource? source;
for (var s in sources.where((element) => element.host != null)) { for (var s in sources.where((element) => element.hosts.isNotEmpty)) {
if (RegExp( if (RegExp(
'://${s.allowSubDomains ? '([^\\.]+\\.)*' : '(www\\.)?'}${s.host}(/|\\z)?') '://${s.allowSubDomains ? '([^\\.]+\\.)*' : '(www\\.)?'}(${getSourceRegex(s.hosts)})(/|\\z)?')
.hasMatch(url)) { .hasMatch(url)) {
source = s; source = s;
break; break;
@@ -696,7 +716,7 @@ class SourceProvider {
} }
if (source == null) { if (source == null) {
for (var s in sources.where( for (var s in sources.where(
(element) => element.host == null && !element.neverAutoSelect)) { (element) => element.hosts.isEmpty && !element.neverAutoSelect)) {
try { try {
s.sourceSpecificStandardizeURL(url); s.sourceSpecificStandardizeURL(url);
source = s; source = s;

View File

@@ -30,10 +30,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: animations name: animations
sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5" sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.10" version: "2.0.11"
app_links: app_links:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -328,10 +328,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_markdown name: flutter_markdown
sha256: "35108526a233cc0755664d445f8a6b4b61e6f8fe993b3658b80b4a26827fc196" sha256: "30088ce826b5b9cfbf9e8bece34c716c8a59fa54461dcae1e4ac01a94639e762"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.18+2" version: "0.6.18+3"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@@ -610,18 +610,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: platform name: platform
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59" sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.3" version: "3.1.4"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: plugin_platform_interface name: plugin_platform_interface
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.7" version: "2.1.8"
pointycastle: pointycastle:
dependency: transitive dependency: transitive
description: description:
@@ -831,18 +831,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" sha256: c0766a55ab42cefaa728cabc951e82919ab41a3a4fee0aaa96176ca82da8cc51
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.0" version: "6.2.1"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 sha256: "46b81e3109cbb2d6b81702ad3077540789a3e74e22795eb9f0b7d494dbaa72ea"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.1" version: "6.2.2"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@@ -863,18 +863,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_platform_interface name: url_launcher_platform_interface
sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" sha256: "4aca1e060978e19b2998ee28503f40b5ba6226819c2b5e3e4d1821e8ccd92198"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.3.0"
url_launcher_web: url_launcher_web:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.3"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@@ -911,18 +911,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: "42393b4492e629aa3a88618530a4a00de8bb46e50e7b3993fedbfdc5352f0dbf" sha256: "60e23976834e995c404c0b21d3b9db37ecd77d3303ef74f8b8d7a7b19947fc04"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.4.2" version: "4.4.3"
webview_flutter_android: webview_flutter_android:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: b54c89fe14a6d26a2a46e24880da0441cdd2bf1f6d01a5b3e1d39558feb1de0b sha256: "161af93c2abaf94ef2192bffb53a3658b2d721a3bf99b69aa1e47814ee18cc96"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.13.1" version: "3.13.2"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -935,18 +935,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: eebfabfa8a115b535b52031b8b26f7a4b58ceceab378bc9db8762b0fb46f7b5d sha256: "02d8f3ebbc842704b2b662377b3ee11c0f8f1bbaa8eab6398262f40049819160"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.10.0" version: "3.10.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.1.1" version: "5.2.0"
win32_registry: win32_registry:
dependency: transitive dependency: transitive
description: description:
@@ -959,10 +959,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: xdg_directories name: xdg_directories
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.3" version: "1.0.4"
xml: xml:
dependency: transitive dependency: transitive
description: description:

View File

@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 0.15.4+240 # When changing this, update the tag in main() accordingly version: 0.15.8+244 # When changing this, update the tag in main() accordingly
environment: environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'