Compare commits

...

29 Commits

Author SHA1 Message Date
3bc5837999 Merge pull request #380 from ImranR98/dev
Bugfix: Infinite load on corrupt App JSON (#378)
2023-03-22 22:41:59 -04:00
9fbe524818 Bugfix: Infinite load on corrupt App JSON (#378) 2023-03-22 22:36:04 -04:00
c21a9d7292 Merge pull request #373 from ImranR98/dev
Rearranged Sources + Added (Non-Activated) WhatsApp Source (Website Currently Provides the Wrong Version)
2023-03-20 15:20:33 -04:00
9c6068b270 Added WhatsApp (not activated) + rearranged sources 2023-03-20 15:18:55 -04:00
cd86d6112b Merge pull request #371 from ImranR98/dev
Render Changelog as MarkDown (#369) (for some Sources) + VLC as a Source (#367)
2023-03-19 13:54:59 -04:00
1112c79c14 Increment version 2023-03-19 13:53:40 -04:00
08555bac75 Added VLC as a Source 2023-03-19 13:50:14 -04:00
6db31e2b24 Support for normal text changelogs (by Source) 2023-03-19 12:52:34 -04:00
48d2532323 Links in changelog openable 2023-03-19 12:49:43 -04:00
f1fc43a3e7 Don't show 'Changes' button if it doesn't do anything 2023-03-19 12:44:17 -04:00
280827d8ec Changelog now rendered as MarkDown 2023-03-19 12:38:57 -04:00
05ee0f9c48 Merge pull request #366 from ImranR98/dev
Open changelog inside App for supported Sources (#82)
2023-03-18 23:54:08 -04:00
ef06ae289e Open changelog inside App for supported Sources (#82) 2023-03-18 23:53:42 -04:00
bd0e322465 Updated README sources section 2023-03-18 23:20:04 -04:00
a93a2411fa Merge pull request #365 from ImranR98/dev
Added 2 Sources: Neutron Code #287, Telegram #290 (Official App, not Channels as Sources) + Tiny UI Bugfix
2023-03-18 23:12:05 -04:00
26e6eef72e Increment version 2023-03-18 23:10:14 -04:00
e49a6e311b Added Telegram App as a Source 2023-03-18 23:09:11 -04:00
53d3397651 Remove download notification when download fails 2023-03-18 22:58:37 -04:00
fe540f5e61 Added Neutron Code as a Source 2023-03-18 22:54:58 -04:00
234374224b Merge pull request #364 from ImranR98/dev
Made download start more responsive (#361, #327)  + very minor form UI spacing improvement
2023-03-17 23:10:59 -04:00
83390f648a Increment version, update packages 2023-03-17 23:10:18 -04:00
1143b6a546 Better form UI spacing 2023-03-17 23:03:38 -04:00
0f3e029312 Bugfix for previous commit 2023-03-17 22:49:12 -04:00
c0120f4e40 Made download start more responsive (#361, #327) 2023-03-17 16:48:06 -04:00
a0199f0ceb Merge pull request #349 from ImranR98/dev
Added FR menu option, increment version
2023-03-05 13:07:53 -05:00
0528936e5a Added FR menu option, increment version 2023-03-05 13:07:31 -05:00
4de98b2f36 Merge pull request #348 from sonalder-darlene/main
Add french translation 🇫🇷
2023-03-05 13:03:09 -05:00
dfb5f5596c Add a missing translation 2023-03-05 17:19:04 +00:00
2e706aac47 Add french translation
just added a fr.json with everything translated
2023-03-05 17:16:48 +00:00
21 changed files with 850 additions and 164 deletions

View File

@ -19,6 +19,9 @@ Currently supported App sources:
- Third Party F-Droid Repos
- Any URLs ending with `/fdroid/<word>`, where `<word>` can be anything - most often `repo`
- [Steam](https://store.steampowered.com/mobile)
- [Telegram App](https://telegram.org)
- [VLC](https://www.videolan.org/vlc/download-android.html)
- [Neutron Code](https://neutroncode.com)
- "HTML" (Fallback)
- Any other URL that returns an HTML page with links to APK files (if multiple, the last file alphabetically is picked)

271
assets/translations/fr.json Normal file
View File

@ -0,0 +1,271 @@
{
"invalidURLForSource": "URL d'application {} invalide",
"noReleaseFound": "Impossible de trouver une version appropriée",
"noVersionFound": "Impossible de déterminer la version de la version",
"urlMatchesNoSource": "L'URL ne correspond pas à une source connue",
"cantInstallOlderVersion": "Impossible d'installer une ancienne version d'une application",
"appIdMismatch": "L'ID de paquet téléchargé ne correspond pas à l'ID de l'application existante",
"functionNotImplemented": "Cette classe n'a pas implémenté cette fonction",
"placeholder": "Espace réservé",
"someErrors": "Des erreurs se sont produites",
"unexpectedError": "Erreur inattendue",
"ok": "Okay",
"and": "et",
"startedBgUpdateTask": "Démarrage de la tâche de vérification de mise à jour en arrière-plan",
"bgUpdateIgnoreAfterIs": "Mise à jour en arrière-plan est ignoré après {}",
"startedActualBGUpdateCheck": "Démarrage de la vérification de la mise à jour en arrière-plan",
"bgUpdateTaskFinished": "Tâche de vérification de la mise à jour en arrière-plan terminée",
"firstRun": "Il s'agit de la toute première exécution d'Obtainium",
"settingUpdateCheckIntervalTo": "Définition de l'intervalle de mise à jour sur {}",
"githubPATLabel": "Jeton d'Accès Personnel GitHub (Augmente la limite de débit)",
"githubPATHint": "Le JAP doit être dans ce format : username:token",
"githubPATFormat": "username:token",
"githubPATLinkText": "À propos des JAP GitHub",
"includePrereleases": "Inclure les avant-premières",
"fallbackToOlderReleases": "Retour aux anciennes versions",
"filterReleaseTitlesByRegEx": "Filtrer les titres de version par expression régulière",
"invalidRegEx": "Expression régulière invalide",
"noDescription": "Pas de description",
"cancel": "Annuler",
"continue": "Continuer",
"requiredInBrackets": "(Requis)",
"dropdownNoOptsError": "ERREUR : LE DÉROULEMENT DOIT AVOIR AU MOINS UNE OPT",
"colour": "Couleur",
"githubStarredRepos": "Dépôts étoilés GitHub",
"uname": "Nom d'utilisateur",
"wrongArgNum": "Mauvais nombre d'arguments fournis",
"xIsTrackOnly": "{} est en 'Suivi uniquement'",
"source": "Source",
"app": "Application",
"appsFromSourceAreTrackOnly": "Les applications de cette source sont en 'Suivi uniquement'.",
"youPickedTrackOnly": "Vous avez sélectionné l'option 'Suivi uniquement'.",
"trackOnlyAppDescription": "L'application sera suivie pour les mises à jour, mais Obtainium ne pourra pas la télécharger ou l'installer.",
"cancelled": "Annulé",
"appAlreadyAdded": "Application déjà ajoutée",
"alreadyUpToDateQuestion": "Application déjà à jour ?",
"addApp": "Ajouter une application",
"appSourceURL": "URL de la source de l'application",
"error": "Erreur",
"add": "Ajoutée",
"searchSomeSourcesLabel": "Rechercher (certaines sources uniquement)",
"search": "Rechercher",
"additionalOptsFor": "Options supplémentaires pour {}",
"supportedSourcesBelow": "Sources prises en charge :",
"trackOnlyInBrackets": "(Suivi uniquement)",
"searchableInBrackets": "(Recherchable)",
"appsString": "Applications",
"noApps": "Aucune application",
"noAppsForFilter": "Aucune application pour le filtre",
"byX": "Par {}",
"percentProgress": "Progrès: {}%",
"pleaseWait": "Veuillez patienter",
"updateAvailable": "Mise à jour disponible",
"estimateInBracketsShort": "(Est.)",
"notInstalled": "Pas installé",
"estimateInBrackets": "(Estimation)",
"selectAll": "Tout sélectionner",
"deselectN": "Déselectionner {}",
"xWillBeRemovedButRemainInstalled": "{} sera supprimé d'Obtainium mais restera installé sur l'appareil.",
"removeSelectedAppsQuestion": "Supprimer les applications sélectionnées ?",
"removeSelectedApps": "Supprimer les applications sélectionnées",
"updateX": "Mise à jour {}",
"installX": "Installer {}",
"markXTrackOnlyAsUpdated": "Marquer {}\n(Suivi uniquement)\nas mis à jour",
"changeX": "Changer {}",
"installUpdateApps": "Installer/Mettre à jour les applications",
"installUpdateSelectedApps": "Installer/Mettre à jour les applications sélectionnées",
"markXSelectedAppsAsUpdated": "Marquer {} les applications sélectionnées comme mises à jour ?",
"no": "Non",
"yes": "Oui",
"markSelectedAppsUpdated": "Marquer les applications sélectionnées comme mises à jour",
"pinToTop": "Épingler en haut",
"unpinFromTop": "Détacher du haut",
"resetInstallStatusForSelectedAppsQuestion": "Réinitialiser l'état d'installation des applications sélectionnées ?",
"installStatusOfXWillBeResetExplanation": "L'état d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut aider lorsque la version de l'application affichée dans Obtainium est incorrecte en raison d'échecs de mises à jour ou d'autres problèmes.",
"shareSelectedAppURLs": "Partager les URL d'application sélectionnées",
"resetInstallStatus": "Réinitialiser le statut d'installation",
"more": "Plus",
"removeOutdatedFilter": "Supprimer le filtre d'application obsolète",
"showOutdatedOnly": "Afficher uniquement les applications obsolètes",
"filter": "Filtre",
"filterActive": "Filtre *",
"filterApps": "Filtrer les applications",
"appName": "Nom de l'application",
"author": "Auteur",
"upToDateApps": "Applications à jour",
"nonInstalledApps": "Applications non installées",
"importExport": "Importer/Exporter",
"settings": "Paramètres",
"exportedTo": "Exporté vers {}",
"obtainiumExport": "Exportation d'Obtainium",
"invalidInput": "Entrée invalide",
"importedX": "Importé {}",
"obtainiumImport": "Importation d'Obtainium",
"importFromURLList": "Importer à partir de la liste d'URL",
"searchQuery": "Requête de recherche",
"appURLList": "Liste d'URL d'application",
"line": "Queue",
"searchX": "Rechercher {}",
"noResults": "Aucun résultat trouvé",
"importX": "Importer {}",
"importedAppsIdDisclaimer": "Les applications importées peuvent s'afficher à tort comme \"Non installées\".\nPour résoudre ce problème, réinstallez-les via Obtainium.\nCela ne devrait pas affecter les données de l'application.\n\nN'affecte que les URL et les méthodes d'importation tierces.",
"importErrors": "Erreurs d'importation",
"importedXOfYApps": "{} sur {} applications importées.",
"followingURLsHadErrors": "Les URL suivantes comportaient des erreurs :",
"okay": "Okay",
"selectURL": "Sélectionnez l'URL",
"selectURLs": "Sélectionnez les URLs",
"pick": "Prendre",
"theme": "Thème",
"dark": "Sombre",
"light": "Clair",
"followSystem": "Suivre le système",
"obtainium": "Obtainium",
"materialYou": "Material You",
"appSortBy": "Applications triées par",
"authorName": "Auteur/Nom",
"nameAuthor": "Nom/Auteur",
"asAdded": "Comme ajouté",
"appSortOrder": "Ordre de tri des applications",
"ascending": "Ascendant",
"descending": "Descendanr",
"bgUpdateCheckInterval": "Intervalle de vérification des mises à jour en arrière-plan",
"neverManualOnly": "Jamais - Manuel uniquement",
"appearance": "Apparence",
"showWebInAppView": "Afficher la page Web source dans la vue de l'application",
"pinUpdates": "Épingler les mises à jour dans la vue Top des applications",
"updates": "Mises à jour",
"sourceSpecific": "Spécifique à la source",
"appSource": "Source de l'application",
"noLogs": "Aucun journal",
"appLogs": "Journaux d'application",
"close": "Fermer",
"share": "Partager",
"appNotFound": "Application introuvable",
"obtainiumExportHyphenatedLowercase": "obtainium-export",
"pickAnAPK": "Choisissez un APK",
"appHasMoreThanOnePackage": "{} a plus d'un paquet :",
"deviceSupportsXArch": "Votre appareil prend en charge l'architecture de processeur {}.",
"deviceSupportsFollowingArchs": "Votre appareil prend en charge les architectures CPU suivantes :",
"warning": "Avertissement",
"sourceIsXButPackageFromYPrompt": "La source de l'application est '{}' mais le paquet de version provient de '{}'. Continuer?",
"updatesAvailable": "Mises à jour disponibles",
"updatesAvailableNotifDescription": "Avertit l'utilisateur que des mises à jour sont disponibles pour une ou plusieurs applications suivies par Obtainium",
"noNewUpdates": "Aucune nouvelle mise à jour.",
"xHasAnUpdate": "{} a une mise à jour.",
"appsUpdated": "Applications mises à jour",
"appsUpdatedNotifDescription": "Avertit l'utilisateur que les mises à jour d'une ou plusieurs applications ont été appliquées en arrière-plan",
"xWasUpdatedToY": "{} a été mis à jour pour {}.",
"errorCheckingUpdates": "Erreur lors de la vérification des mises à jour",
"errorCheckingUpdatesNotifDescription": "Une notification qui s'affiche lorsque la vérification de la mise à jour en arrière-plan échoue",
"appsRemoved": "Applications supprimées",
"appsRemovedNotifDescription": "Avertit l'utilisateur qu'une ou plusieurs applications ont été supprimées en raison d'erreurs lors de leur chargement",
"xWasRemovedDueToErrorY": "{} a été supprimé en raison de cette erreur : {}",
"completeAppInstallation": "Installation complète de l'application",
"obtainiumMustBeOpenToInstallApps": "Obtainium doit être ouvert pour installer des applications",
"completeAppInstallationNotifDescription": "Demande à l'utilisateur de retourner sur Obtainium pour terminer l'installation d'une application",
"checkingForUpdates": "Vérification des mises à jour",
"checkingForUpdatesNotifDescription": "Notification transitoire qui apparaît lors de la recherche de mises à jour",
"pleaseAllowInstallPerm": "Veuillez autoriser Obtainium à installer des applications",
"trackOnly": "Suivi uniquement",
"errorWithHttpStatusCode": "Erreur {}",
"versionCorrectionDisabled": "Correction de version désactivée (le plugin ne semble pas fonctionner)",
"unknown": "Inconnu",
"none": "Aucun",
"never": "Jamais",
"latestVersionX": "Dernière version: {}",
"installedVersionX": "Version installée : {}",
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
"remove": "Retirer",
"yesMarkUpdated": "Oui, marquer comme mis à jour",
"fdroid": "F-Droid",
"appIdOrName": "ID ou nom de l'application",
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
"fdroidThirdPartyRepo": "Dépôt tiers F-Droid",
"steam": "Steam",
"steamMobile": "Steam Mobile",
"steamChat": "Steam Chat",
"install": "Installer",
"markInstalled": "Marquer installée",
"update": "Mettre à jour",
"markUpdated": "Marquer à jour",
"additionalOptions": "Options additionelles",
"disableVersionDetection": "Désactiver la détection de version",
"noVersionDetectionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de version ne fonctionne pas correctement.",
"downloadingX": "Téléchargement {}",
"downloadNotifDescription": "Avertit l'utilisateur de la progression du téléchargement d'une application",
"noAPKFound": "Aucun APK trouvé",
"noVersionDetection": "Pas de détection de version",
"categorize": "Catégoriser",
"categories": "Catégories",
"category": "Catégorie",
"noCategory": "No Category",
"noCategories": "Aucune catégorie",
"deleteCategoriesQuestion": "Supprimer les catégories ?",
"categoryDeleteWarning": "Toutes les applications dans les catégories supprimées seront définies sur non catégorisées.",
"addCategory": "Ajouter une catégorie",
"label": "Étiquette",
"language": "Langue",
"storagePermissionDenied": "Autorisation de stockage refusée",
"selectedCategorizeWarning": "Cela remplacera tous les paramètres de catégorie existants pour les applications sélectionnées.",
"filterAPKsByRegEx": "Filtrer les APK par expression régulière",
"removeFromObtainium": "Supprimer d'Obtainium",
"uninstallFromDevice": "Désinstaller de l'appareil",
"onlyWorksWithNonVersionDetectApps": "Fonctionne uniquement pour les applications avec la détection de version désactivée.",
"releaseDateAsVersion": "Utiliser la date de sortie comme version",
"releaseDateAsVersionExplanation": "Cette option ne doit être utilisée que pour les applications où la détection de version ne fonctionne pas correctement, mais une date de sortie est disponible.",
"changes": "Changements",
"releaseDate": "Date de sortie",
"importFromURLsInFile": "Importer à partir d'URL dans un fichier (comme OPML)",
"versionDetection": "Détection des versions",
"standardVersionDetection": "Détection de version standard",
"removeAppQuestion": {
"one": "Supprimer l'application ?",
"other": "Supprimer les applications ?"
},
"tooManyRequestsTryAgainInMinutes": {
"one": "Trop de demandes (taux limité) - réessayez dans {} minute",
"other": "Trop de demandes (taux limité) - réessayez dans {} minutes"
},
"bgUpdateGotErrorRetryInMinutes": {
"one": "La vérification de la mise à jour en arrière-plan a rencontré un {}, planifiera une nouvelle tentative de vérification dans {} minute",
"other": "La vérification de la mise à jour en arrière-plan a rencontré un {}, planifiera une nouvelle tentative de vérification dans {} minutes"
},
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
"one": "La vérification des mises à jour en arrière-plan trouvée {} mise à jour - avertira l'utilisateur si nécessaire",
"other": "La vérification des mises à jour en arrière-plan a trouvé {} mises à jour - avertira l'utilisateur si nécessaire"
},
"apps": {
"one": "{} Application",
"other": "{} Applications"
},
"url": {
"one": "{} URL",
"other": "{} URLs"
},
"minute": {
"one": "{} Minute",
"other": "{} Minutes"
},
"hour": {
"one": "{} Heure",
"other": "{} Heures"
},
"day": {
"one": "{} Jour",
"other": "{} Jours"
},
"clearedNLogsBeforeXAfterY": {
"one": "{n} journal effacé (avant = {before}, après = {after})",
"other": "{n} journaux effacés (avant = {before}, après = {after})"
},
"xAndNMoreUpdatesAvailable": {
"one": "{} et 1 autre application ont des mises à jour.",
"other": "{} et {} autres applications ont des mises à jour."
},
"xAndNMoreUpdatesInstalled": {
"one": "{} et 1 autre application ont été mises à jour.",
"other": "{} et {} autres applications ont été mises à jour."
}
}

View File

@ -118,9 +118,11 @@ class Codeberg extends AppSource {
if (version == null) {
throw NoVersionError();
}
var changeLog = targetRelease['body'].toString();
return APKDetails(version, targetRelease['apkUrls'] as List<String>,
getAppNames(standardUrl),
releaseDate: releaseDate);
releaseDate: releaseDate,
changeLog: changeLog.isEmpty ? null : changeLog);
} else {
throw getObtainiumHttpError(res);
}

View File

@ -27,9 +27,6 @@ class FDroid extends AppSource {
return url.substring(0, match.end);
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => null;
@override
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {

View File

@ -160,9 +160,11 @@ class GitHub extends AppSource {
if (version == null) {
throw NoVersionError();
}
var changeLog = targetRelease['body'].toString();
return APKDetails(version, targetRelease['apkUrls'] as List<String>,
getAppNames(standardUrl),
releaseDate: releaseDate);
releaseDate: releaseDate,
changeLog: changeLog.isEmpty ? null : changeLog);
} else {
rateLimitErrorCheck(res);
throw getObtainiumHttpError(res);

View File

@ -10,9 +10,6 @@ class HTML extends AppSource {
return url;
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => null;
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,

View File

@ -18,9 +18,6 @@ class IzzyOnDroid extends AppSource {
return url.substring(0, match.end);
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => null;
@override
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {

View File

@ -0,0 +1,111 @@
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class NeutronCode extends AppSource {
NeutronCode() {
host = 'neutroncode.com';
}
@override
String standardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://$host/downloads/file/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) {
throw InvalidURLError(name);
}
return url.substring(0, match.end);
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => standardUrl;
String monthNameToNumberString(String s) {
switch (s.toLowerCase()) {
case 'january':
return '01';
case 'february':
return '02';
case 'march':
return '03';
case 'april':
return '04';
case 'may':
return '05';
case 'june':
return '06';
case 'july':
return '07';
case 'august':
return '08';
case 'september':
return '09';
case 'october':
return '10';
case 'november':
return '11';
case 'december':
return '12';
default:
throw ArgumentError('Invalid month name: $s');
}
}
customDateParse(String dateString) {
List<String> parts = dateString.split(' ');
if (parts.length != 3) {
return null;
}
String result = '';
for (var s in parts.reversed) {
try {
try {
int.parse(s);
result += '$s-';
} catch (e) {
result += '${monthNameToNumberString(s)}-';
}
} catch (e) {
return null;
}
}
return result.substring(0, result.length - 1);
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Response res = await get(Uri.parse(standardUrl));
if (res.statusCode == 200) {
var http = parse(res.body);
var name = http.querySelector('.pd-title')?.innerHtml;
var filename = http.querySelector('.pd-filename .pd-float')?.innerHtml;
if (filename == null) {
throw NoReleasesError();
}
var version =
http.querySelector('.pd-version-txt')?.nextElementSibling?.innerHtml;
if (version == null) {
throw NoVersionError();
}
String? apkUrl = 'https://$host/download/$filename';
var dateStringOriginal =
http.querySelector('.pd-date-txt')?.nextElementSibling?.innerHtml;
var dateString = dateStringOriginal != null
? (customDateParse(dateStringOriginal))
: null;
var changeLogElements = http.querySelectorAll('.pd-fdesc p');
return APKDetails(version, [apkUrl],
AppNames(runtimeType.toString(), name ?? standardUrl.split('/').last),
releaseDate: dateString != null ? DateTime.parse(dateString) : null,
changeLog: changeLogElements.isNotEmpty
? changeLogElements.last.innerHtml
: null);
} else {
throw getObtainiumHttpError(res);
}
}
}

View File

@ -13,9 +13,6 @@ class Signal extends AppSource {
return 'https://$host';
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => null;
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,

View File

@ -18,9 +18,6 @@ class SourceForge extends AppSource {
return url.substring(0, match.end);
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => null;
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,

View File

@ -24,9 +24,6 @@ class SteamMobile extends AppSource {
return 'https://$host';
}
@override
String? changeLogPageFromStandardUrl(String standardUrl) => null;
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,

View File

@ -0,0 +1,40 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class TelegramApp extends AppSource {
TelegramApp() {
host = 'telegram.org';
name = 'Telegram ${tr('app')}';
}
@override
String standardizeURL(String url) {
return 'https://$host';
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Response res = await get(Uri.parse('https://t.me/s/TAndroidAPK'));
if (res.statusCode == 200) {
var http = parse(res.body);
var messages =
http.querySelectorAll('.tgme_widget_message_text.js-message_text');
var version = messages.isNotEmpty
? messages.last.innerHtml.split('\n').first.trim().split(' ').first
: null;
if (version == null) {
throw NoVersionError();
}
String? apkUrl = 'https://telegram.org/dl/android/apk';
return APKDetails(version, [apkUrl], AppNames('Telegram', 'Telegram'));
} else {
throw getObtainiumHttpError(res);
}
}
}

62
lib/app_sources/vlc.dart Normal file
View File

@ -0,0 +1,62 @@
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class VLC extends AppSource {
VLC() {
host = 'videolan.org';
}
@override
String standardizeURL(String url) {
return 'https://$host';
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Response res = await get(
Uri.parse('https://www.videolan.org/vlc/download-android.html'));
if (res.statusCode == 200) {
var dwUrlBase = 'get.videolan.org/vlc-android';
var dwLinks = parse(res.body)
.querySelectorAll('a')
.where((element) =>
element.attributes['href']?.contains(dwUrlBase) ?? false)
.toList();
String? version = dwLinks.isNotEmpty
? dwLinks.first.attributes['href']
?.split('/')
.where((s) => s.isNotEmpty)
.last
: null;
if (version == null) {
throw NoVersionError();
}
String? targetUrl = 'https://$dwUrlBase/$version/';
Response res2 = await get(Uri.parse(targetUrl));
String mirrorDwBase =
'https://plug-mirror.rcac.purdue.edu/vlc/vlc-android/$version/';
List<String> apkUrls = [];
if (res2.statusCode == 200) {
apkUrls = parse(res2.body)
.querySelectorAll('a')
.map((e) => e.attributes['href'])
.where((h) =>
h != null && h.isNotEmpty && h.toLowerCase().endsWith('.apk'))
.map((e) => mirrorDwBase + e!)
.toList();
} else {
throw getObtainiumHttpError(res2);
}
return APKDetails(version, apkUrls, AppNames('VideoLAN', 'VLC'));
} else {
throw getObtainiumHttpError(res);
}
}
}

View File

@ -0,0 +1,75 @@
import 'package:html/parser.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class WhatsApp extends AppSource {
WhatsApp() {
host = 'whatsapp.com';
}
@override
String standardizeURL(String url) {
return 'https://$host';
}
@override
Future<String> apkUrlPrefetchModifier(String apkUrl) async {
Response res = await get(Uri.parse('https://www.whatsapp.com/android'));
if (res.statusCode == 200) {
var targetLinks = parse(res.body)
.querySelectorAll('a')
.map((e) => e.attributes['href'])
.where((e) => e != null)
.where((e) =>
e!.contains('scontent.whatsapp.net') &&
e.contains('WhatsApp.apk'))
.toList();
if (targetLinks.isEmpty) {
throw NoAPKError();
}
return targetLinks[0]!;
} else {
throw getObtainiumHttpError(res);
}
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Response res = await get(Uri.parse('https://www.whatsapp.com/android'));
if (res.statusCode == 200) {
var targetElements = parse(res.body)
.querySelectorAll('p')
.where((element) => element.innerHtml.contains('Version '))
.toList();
if (targetElements.isEmpty) {
throw NoVersionError();
}
var vLines = targetElements[0]
.innerHtml
.split('\n')
.where((element) => element.contains('Version '))
.toList();
if (vLines.isEmpty) {
throw NoVersionError();
}
var versionMatch = RegExp('[0-9]+(\\.[0-9]+)+').firstMatch(vLines[0]);
if (versionMatch == null) {
throw NoVersionError();
}
String version =
vLines[0].substring(versionMatch.start, versionMatch.end);
return APKDetails(
version,
[
'https://www.whatsapp.com/android?v=$version&=thisIsaPlaceholder&a=realURLPrefetchedAtDownloadTime'
],
AppNames('Meta', 'WhatsApp'));
} else {
throw getObtainiumHttpError(res);
}
}
}

View File

@ -460,10 +460,9 @@ class _GeneratedFormState extends State<GeneratedForm> {
if (rowInputs.key > 0) {
rows.add([
SizedBox(
height: widget.items[rowInputs.key][0] is GeneratedFormSwitch &&
widget.items[rowInputs.key - 1][0] is! GeneratedFormSwitch
? 25
: 8,
height: widget.items[rowInputs.key - 1][0] is GeneratedFormSwitch
? 8
: 25,
)
]);
}
@ -477,6 +476,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
rowItems.add(Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
rowInput.value,
...widget.items[rowInputs.key][rowInput.key].belowWidgets

View File

@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.11.7';
const String currentVersion = '0.11.13';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
@ -34,7 +34,8 @@ const supportedLocales = [
Locale('ja'),
Locale('hu'),
Locale('de'),
Locale('fa')
Locale('fa'),
Locale('fr')
];
const fallbackLocale = Locale('en');
const localeDir = 'assets/translations';

View File

@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:obtainium/components/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.dart';
@ -14,6 +15,7 @@ import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:markdown/markdown.dart' as md;
class AppsPage extends StatefulWidget {
const AppsPage({super.key});
@ -229,9 +231,88 @@ class AppsPageState extends State<AppsPage> {
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
String? changesUrl = SourceProvider()
.getSource(listedApps[index].app.url)
AppSource appSource =
SourceProvider().getSource(listedApps[index].app.url);
String? changesUrl = appSource
.changeLogPageFromStandardUrl(listedApps[index].app.url);
String? changeLog = listedApps[index].app.changeLog;
var showChanges = (changeLog == null && changesUrl == null)
? null
: () {
if (changeLog != null) {
showDialog(
context: context,
builder: (BuildContext context) {
return GeneratedFormModal(
title: tr('changes'),
items: const [],
additionalWidgets: [
changesUrl != null
? GestureDetector(
child: Text(
changesUrl,
style: const TextStyle(
decoration:
TextDecoration.underline,
fontStyle: FontStyle.italic),
),
onTap: () {
launchUrlString(changesUrl,
mode: LaunchMode
.externalApplication);
},
)
: const SizedBox.shrink(),
changesUrl != null
? const SizedBox(
height: 16,
)
: const SizedBox.shrink(),
appSource.changeLogIfAnyIsMarkDown
? SizedBox(
width:
MediaQuery.of(context).size.width,
height: MediaQuery.of(context)
.size
.height -
350,
child: Markdown(
data: changeLog,
onTapLink: (text, href, title) {
if (href != null) {
launchUrlString(
href.startsWith(
'http://') ||
href.startsWith(
'https://')
? href
: '${Uri.parse(listedApps[index].app.url).origin}/$href',
mode: LaunchMode
.externalApplication);
}
},
extensionSet: md.ExtensionSet(
md.ExtensionSet.gitHubFlavored
.blockSyntaxes,
[
md.EmojiSyntax(),
...md
.ExtensionSet
.gitHubFlavored
.inlineSyntaxes
],
),
))
: Text(changeLog),
],
singleNullReturnButton: tr('ok'),
);
});
} else {
launchUrlString(changesUrl!,
mode: LaunchMode.externalApplication);
}
};
var transparent = const Color.fromARGB(0, 0, 0, 0).value;
var hasUpdate = listedApps[index].app.installedVersion != null &&
listedApps[index].app.installedVersion !=
@ -366,25 +447,22 @@ class AppsPageState extends State<AppsPage> {
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
onTap: changesUrl == null
? null
: () {
launchUrlString(changesUrl,
mode: LaunchMode
.externalApplication);
},
onTap: showChanges,
child: Text(
listedApps[index].app.releaseDate ==
null
? showChanges != null
? tr('changes')
: ''
: DateFormat('yyyy-MM-dd')
.format(listedApps[index]
.app
.releaseDate!),
style: const TextStyle(
style: TextStyle(
fontStyle: FontStyle.italic,
decoration:
TextDecoration.underline),
decoration: showChanges != null
? TextDecoration.underline
: TextDecoration.none),
))
],
),

View File

@ -145,13 +145,19 @@ class AppsProvider with ChangeNotifier {
}
Future<DownloadedApk> downloadApp(App app, BuildContext? context) async {
NotificationsProvider? notificationsProvider =
context?.read<NotificationsProvider>();
var notifId = DownloadNotification(app.name, 0).id;
if (apps[app.id] != null) {
apps[app.id]!.downloadProgress = 0;
notifyListeners();
}
try {
var fileName =
'${app.id}-${app.latestVersion}-${app.preferredApkIndex}.apk';
String downloadUrl = await SourceProvider()
.getSource(app.url)
.apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex]);
NotificationsProvider? notificationsProvider =
context?.read<NotificationsProvider>();
var notif = DownloadNotification(app.name, 100);
notificationsProvider?.cancel(notif.id);
int? prevProg;
@ -168,7 +174,6 @@ class AppsProvider with ChangeNotifier {
}
prevProg = prog;
});
notificationsProvider?.cancel(notif.id);
// Delete older versions of the APK if any
for (var file in downloadedFile.parent.listSync()) {
var fn = file.path.split('/').last;
@ -195,6 +200,13 @@ class AppsProvider with ChangeNotifier {
}
}
return DownloadedApk(app.id, downloadedFile);
} finally {
notificationsProvider?.cancel(notifId);
if (apps[app.id] != null) {
apps[app.id]!.downloadProgress = null;
notifyListeners();
}
}
}
bool areDownloadsRunning() => apps.values
@ -559,7 +571,21 @@ class AppsProvider with ChangeNotifier {
List<App> newApps = (await getAppsDir())
.listSync()
.where((item) => item.path.toLowerCase().endsWith('.json'))
.map((e) => App.fromJson(jsonDecode(File(e.path).readAsStringSync())))
.map((e) {
try {
return App.fromJson(jsonDecode(File(e.path).readAsStringSync()));
} catch (err) {
if (err is FormatException) {
logs.add('Corrupt JSON when loading App (will be ignored): $e');
e.renameSync('${e.path}.corrupt');
return App(
'', '', '', '', '', '', [], 0, {}, DateTime.now(), false);
} else {
rethrow;
}
}
})
.where((element) => element.id.isNotEmpty)
.toList();
var idsToDelete = apps.values
.map((e) => e.app.id)

View File

@ -15,9 +15,13 @@ import 'package:obtainium/app_sources/gitlab.dart';
import 'package:obtainium/app_sources/izzyondroid.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/app_sources/mullvad.dart';
import 'package:obtainium/app_sources/neutroncode.dart';
import 'package:obtainium/app_sources/signal.dart';
import 'package:obtainium/app_sources/sourceforge.dart';
import 'package:obtainium/app_sources/steammobile.dart';
import 'package:obtainium/app_sources/telegramapp.dart';
import 'package:obtainium/app_sources/vlc.dart';
import 'package:obtainium/app_sources/whatsapp.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/mass_app_sources/githubstars.dart';
@ -34,8 +38,10 @@ class APKDetails {
late List<String> apkUrls;
late AppNames names;
late DateTime? releaseDate;
late String? changeLog;
APKDetails(this.version, this.apkUrls, this.names, {this.releaseDate});
APKDetails(this.version, this.apkUrls, this.names,
{this.releaseDate, this.changeLog});
}
class App {
@ -52,6 +58,7 @@ class App {
bool pinned = false;
List<String> categories;
late DateTime? releaseDate;
late String? changeLog;
App(
this.id,
this.url,
@ -65,7 +72,8 @@ class App {
this.lastUpdateCheck,
this.pinned,
{this.categories = const [],
this.releaseDate});
this.releaseDate,
this.changeLog});
@override
String toString() {
@ -155,7 +163,8 @@ class App {
releaseDate: json['releaseDate'] == null
? null
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
);
changeLog:
json['changeLog'] == null ? null : json['changeLog'] as String);
}
Map<String, dynamic> toJson() => {
@ -171,7 +180,8 @@ class App {
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
'pinned': pinned,
'categories': categories,
'releaseDate': releaseDate?.microsecondsSinceEpoch
'releaseDate': releaseDate?.microsecondsSinceEpoch,
'changeLog': changeLog
};
}
@ -220,6 +230,7 @@ class AppSource {
String? host;
late String name;
bool enforceTrackOnly = false;
bool changeLogIfAnyIsMarkDown = true;
AppSource() {
name = runtimeType.toString();
@ -332,12 +343,16 @@ class SourceProvider {
Codeberg(),
FDroid(),
IzzyOnDroid(),
Mullvad(),
Signal(),
FDroidRepo(),
SourceForge(),
APKMirror(),
FDroidRepo(),
Mullvad(),
Signal(),
VLC(),
// WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
TelegramApp(),
SteamMobile(),
NeutronCode(),
HTML() // This should ALWAYS be the last option as they are tried in order
];
@ -433,7 +448,8 @@ class SourceProvider {
DateTime.now(),
currentApp?.pinned ?? false,
categories: currentApp?.categories ?? const [],
releaseDate: apk.releaseDate);
releaseDate: apk.releaseDate,
changeLog: apk.changeLog);
}
// Returns errors in [results, errors] instead of throwing them

View File

@ -181,10 +181,10 @@ packages:
dependency: "direct main"
description:
name: file_picker
sha256: d090ae03df98b0247b82e5928f44d1b959867049d18d73635e2e0bc3f49542b9
sha256: d8e9ca7e5d1983365c277f12c21b4362df6cf659c99af146ad4d04eb33033013
url: "https://pub.dev"
source: hosted
version: "5.2.5"
version: "5.2.6"
flutter:
dependency: "direct main"
description: flutter
@ -235,6 +235,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_markdown:
dependency: "direct main"
description:
name: flutter_markdown
sha256: "7b25c10de1fea883f3c4f9b8389506b54053cd00807beab69fd65c8653a2711f"
url: "https://pub.dev"
source: hosted
version: "0.6.14"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@ -325,6 +333,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
markdown:
dependency: transitive
description:
name: markdown
sha256: b3c60dee8c2af50ad0e6e90cceba98e47718a6ee0a7a6772c77846a0cc21f78b
url: "https://pub.dev"
source: hosted
version: "7.0.1"
matcher:
dependency: transitive
description:
@ -401,26 +417,26 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: "7623b7d4be0f0f7d9a8b5ee6879fc13e4522d4c875ab86801dee4af32b54b83e"
sha256: "019f18c9c10ae370b08dce1f3e3b73bc9f58e7f087bb5e921f06529438ac0ae7"
url: "https://pub.dev"
source: hosted
version: "2.0.23"
version: "2.0.24"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: eec003594f19fe2456ea965ae36b3fc967bc5005f508890aafe31fa75e41d972
sha256: "12eee51abdf4d34c590f043f45073adbb45514a108bd9db4491547a2fd891059"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.2.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a"
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
url: "https://pub.dev"
source: hosted
version: "2.1.9"
version: "2.1.10"
path_provider_platform_interface:
dependency: transitive
description:
@ -433,10 +449,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
sha256: "642ddf65fde5404f83267e8459ddb4556316d3ee6d511ed193357e25caa3632d"
sha256: f53720498d5a543f9607db4b0e997c4b5438884de25b0f73098cc2671a51b130
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
permission_handler:
dependency: "direct main"
description:
@ -545,26 +561,26 @@ packages:
dependency: transitive
description:
name: shared_preferences_android
sha256: a51a4f9375097f94df1c6e0a49c0374440d31ab026b59d58a7e7660675879db4
sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521
url: "https://pub.dev"
source: hosted
version: "2.0.16"
version: "2.0.17"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "6b84fdf06b32bb336f972d373cd38b63734f3461ba56ac2ba01b56d052796259"
sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: d7fb71e6e20cd3dfffcc823a28da3539b392e53ed5fc5c2b90b55fdaa8a7e8fa
sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
shared_preferences_platform_interface:
dependency: transitive
description:
@ -577,18 +593,18 @@ packages:
dependency: transitive
description:
name: shared_preferences_web
sha256: "6737b757e49ba93de2a233df229d0b6a87728cea1684da828cbc718b65dcf9d7"
sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8"
url: "https://pub.dev"
source: hosted
version: "2.0.5"
version: "2.0.6"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: bd014168e8484837c39ef21065b78f305810ceabc1d4f90be6e3b392ce81b46d
sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
sky_engine:
dependency: transitive
description: flutter
@ -606,18 +622,18 @@ packages:
dependency: "direct main"
description:
name: sqflite
sha256: "851d5040552cf911f4cabda08d003eca76b27da3ed0002978272e27c8fbf8ecc"
sha256: "500d6fec583d2c021f2d25a056d96654f910662c64f836cd2063167b8f1fa758"
url: "https://pub.dev"
source: hosted
version: "2.2.5"
version: "2.2.6"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: bfd6973aaeeb93475bc0d875ac9aefddf7965ef22ce09790eb963992ffc5183f
sha256: "963dad8c4aa2f814ce7d2d5b1da2f36f31bd1a439d8f27e3dc189bb9d26bc684"
url: "https://pub.dev"
source: hosted
version: "2.4.2+2"
version: "2.4.3"
stack_trace:
dependency: transitive
description:
@ -694,34 +710,34 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "1f4d9ebe86f333c15d318f81dcdc08b01d45da44af74552608455ebdc08d9732"
sha256: "845530e5e05db5500c1a4c1446785d60cbd8f9bd45e21e7dd643a3273bb4bbd1"
url: "https://pub.dev"
source: hosted
version: "6.0.24"
version: "6.0.25"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: c9cd648d2f7ab56968e049d4e9116f96a85517f1dd806b96a86ea1018a3a82e5
sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92"
url: "https://pub.dev"
source: hosted
version: "6.1.1"
version: "6.1.3"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: e29039160ab3730e42f3d811dc2a6d5f2864b90a70fb765ea60144b03307f682
sha256: "206fb8334a700ef7754d6a9ed119e7349bc830448098f21a69bf1b4ed038cabc"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.4"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "2dddb3291a57b074dade66b5e07e64401dd2487caefd4e9e2f467138d8c7eb06"
sha256: "0ef2b4f97942a16523e51256b799e9aa1843da6c60c55eefbfa9dbc2dcb8331a"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
version: "3.0.4"
url_launcher_platform_interface:
dependency: transitive
description:
@ -734,18 +750,18 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "574cfbe2390666003c3a1d129bdc4574aaa6728f0c00a4829a81c316de69dd9b"
sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
version: "2.0.16"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "97c9067950a0d09cbd93e2e3f0383d1403989362b97102fbf446473a48079a4b"
sha256: a83ba3607a507758669cfafb03f9de09bf6e6280c14d9b9cb18f013e406dcacd
url: "https://pub.dev"
source: hosted
version: "3.0.4"
version: "3.0.5"
uuid:
dependency: transitive
description:
@ -766,34 +782,34 @@ packages:
dependency: "direct main"
description:
name: webview_flutter
sha256: b6cd42db3ced5411f3d01599906156885b18e4188f7065a8a351eb84bee347e0
sha256: "47663d51a9061451aa3880a214ee9a65dcbb933b77bc44388e194279ab3ccaf6"
url: "https://pub.dev"
source: hosted
version: "4.0.6"
version: "4.0.7"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: "5dd3f32b5c2d8f4bf9d05a349e4a65fa718eb137f396f336c3893d558a58fe84"
sha256: "34f83c2f0f64c75ad75c77a2ccfc8d2e531afbe8ad41af1fd787d6d33336aa90"
url: "https://pub.dev"
source: hosted
version: "3.3.2"
version: "3.4.3"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: df6472164b3f4eaf3280422227f361dc8424b106726b7f21d79a8656ba53f71f
sha256: "1939c39e2150fb4d30fd3cc59a891a49fed9935db53007df633ed83581b6117b"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
version: "2.1.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: "87b6353b40e04f04d5f895a484ad6d92d682d9cce4d2d5b32d2d8aca2448d46e"
sha256: ab12479f7a0cf112b9420c36aaf206a1ca47cd60cd42de74a4be2e97a697587b
url: "https://pub.dev"
source: hosted
version: "3.2.0"
version: "3.2.1"
win32:
dependency: transitive
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
# 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.
version: 0.11.7+128 # When changing this, update the tag in main() accordingly
version: 0.11.13+134 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=2.18.2 <3.0.0'
@ -59,6 +59,7 @@ dependencies:
sqflite: ^2.2.0+3
easy_localization: ^3.0.1
android_intent_plus: ^3.1.5
flutter_markdown: ^0.6.14
dev_dependencies: