Compare commits

...

14 Commits

Author SHA1 Message Date
Imran Remtulla
3bc5837999 Merge pull request #380 from ImranR98/dev
Bugfix: Infinite load on corrupt App JSON (#378)
2023-03-22 22:41:59 -04:00
Imran Remtulla
9fbe524818 Bugfix: Infinite load on corrupt App JSON (#378) 2023-03-22 22:36:04 -04:00
Imran Remtulla
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
Imran Remtulla
9c6068b270 Added WhatsApp (not activated) + rearranged sources 2023-03-20 15:18:55 -04:00
Imran Remtulla
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
Imran Remtulla
1112c79c14 Increment version 2023-03-19 13:53:40 -04:00
Imran Remtulla
08555bac75 Added VLC as a Source 2023-03-19 13:50:14 -04:00
Imran Remtulla
6db31e2b24 Support for normal text changelogs (by Source) 2023-03-19 12:52:34 -04:00
Imran Remtulla
48d2532323 Links in changelog openable 2023-03-19 12:49:43 -04:00
Imran Remtulla
f1fc43a3e7 Don't show 'Changes' button if it doesn't do anything 2023-03-19 12:44:17 -04:00
Imran Remtulla
280827d8ec Changelog now rendered as MarkDown 2023-03-19 12:38:57 -04:00
Imran Remtulla
05ee0f9c48 Merge pull request #366 from ImranR98/dev
Open changelog inside App for supported Sources (#82)
2023-03-18 23:54:08 -04:00
Imran Remtulla
ef06ae289e Open changelog inside App for supported Sources (#82) 2023-03-18 23:53:42 -04:00
Imran Remtulla
bd0e322465 Updated README sources section 2023-03-18 23:20:04 -04:00
20 changed files with 328 additions and 81 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -97,10 +97,13 @@ class NeutronCode extends AppSource {
var dateString = dateStringOriginal != null var dateString = dateStringOriginal != null
? (customDateParse(dateStringOriginal)) ? (customDateParse(dateStringOriginal))
: null; : null;
var changeLogElements = http.querySelectorAll('.pd-fdesc p');
return APKDetails(version, [apkUrl], return APKDetails(version, [apkUrl],
AppNames(runtimeType.toString(), name ?? standardUrl.split('/').last), AppNames(runtimeType.toString(), name ?? standardUrl.split('/').last),
releaseDate: dateString != null ? DateTime.parse(dateString) : null); releaseDate: dateString != null ? DateTime.parse(dateString) : null,
changeLog: changeLogElements.isNotEmpty
? changeLogElements.last.innerHtml
: null);
} else { } else {
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);
} }

View File

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

View File

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

View File

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

View File

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

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

@@ -476,6 +476,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
rowItems.add(Expanded( rowItems.add(Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [ children: [
rowInput.value, rowInput.value,
...widget.items[rowInputs.key][rowInput.key].belowWidgets ...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 // ignore: implementation_imports
import 'package:easy_localization/src/localization.dart'; import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.11.10'; const String currentVersion = '0.11.13';
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

View File

@@ -1,6 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/custom_app_bar.dart';
import 'package:obtainium/components/generated_form.dart'; import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/components/generated_form_modal.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:provider/provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:markdown/markdown.dart' as md;
class AppsPage extends StatefulWidget { class AppsPage extends StatefulWidget {
const AppsPage({super.key}); const AppsPage({super.key});
@@ -229,9 +231,88 @@ class AppsPageState extends State<AppsPage> {
SliverList( SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) { (BuildContext context, int index) {
String? changesUrl = SourceProvider() AppSource appSource =
.getSource(listedApps[index].app.url) SourceProvider().getSource(listedApps[index].app.url);
String? changesUrl = appSource
.changeLogPageFromStandardUrl(listedApps[index].app.url); .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 transparent = const Color.fromARGB(0, 0, 0, 0).value;
var hasUpdate = listedApps[index].app.installedVersion != null && var hasUpdate = listedApps[index].app.installedVersion != null &&
listedApps[index].app.installedVersion != listedApps[index].app.installedVersion !=
@@ -366,25 +447,22 @@ class AppsPageState extends State<AppsPage> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
GestureDetector( GestureDetector(
onTap: changesUrl == null onTap: showChanges,
? null
: () {
launchUrlString(changesUrl,
mode: LaunchMode
.externalApplication);
},
child: Text( child: Text(
listedApps[index].app.releaseDate == listedApps[index].app.releaseDate ==
null null
? tr('changes') ? showChanges != null
? tr('changes')
: ''
: DateFormat('yyyy-MM-dd') : DateFormat('yyyy-MM-dd')
.format(listedApps[index] .format(listedApps[index]
.app .app
.releaseDate!), .releaseDate!),
style: const TextStyle( style: TextStyle(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
decoration: decoration: showChanges != null
TextDecoration.underline), ? TextDecoration.underline
: TextDecoration.none),
)) ))
], ],
), ),

View File

@@ -571,7 +571,21 @@ class AppsProvider with ChangeNotifier {
List<App> newApps = (await getAppsDir()) List<App> newApps = (await getAppsDir())
.listSync() .listSync()
.where((item) => item.path.toLowerCase().endsWith('.json')) .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(); .toList();
var idsToDelete = apps.values var idsToDelete = apps.values
.map((e) => e.app.id) .map((e) => e.app.id)

View File

@@ -20,6 +20,8 @@ import 'package:obtainium/app_sources/signal.dart';
import 'package:obtainium/app_sources/sourceforge.dart'; import 'package:obtainium/app_sources/sourceforge.dart';
import 'package:obtainium/app_sources/steammobile.dart'; import 'package:obtainium/app_sources/steammobile.dart';
import 'package:obtainium/app_sources/telegramapp.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/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart'; import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/mass_app_sources/githubstars.dart'; import 'package:obtainium/mass_app_sources/githubstars.dart';
@@ -36,8 +38,10 @@ class APKDetails {
late List<String> apkUrls; late List<String> apkUrls;
late AppNames names; late AppNames names;
late DateTime? releaseDate; 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 { class App {
@@ -54,6 +58,7 @@ class App {
bool pinned = false; bool pinned = false;
List<String> categories; List<String> categories;
late DateTime? releaseDate; late DateTime? releaseDate;
late String? changeLog;
App( App(
this.id, this.id,
this.url, this.url,
@@ -67,7 +72,8 @@ class App {
this.lastUpdateCheck, this.lastUpdateCheck,
this.pinned, this.pinned,
{this.categories = const [], {this.categories = const [],
this.releaseDate}); this.releaseDate,
this.changeLog});
@override @override
String toString() { String toString() {
@@ -130,34 +136,35 @@ class App {
preferredApkIndex = 0; preferredApkIndex = 0;
} }
return App( return App(
json['id'] as String, json['id'] as String,
json['url'] as String, json['url'] as String,
json['author'] as String, json['author'] as String,
json['name'] as String, json['name'] as String,
json['installedVersion'] == null json['installedVersion'] == null
? null ? null
: json['installedVersion'] as String, : json['installedVersion'] as String,
json['latestVersion'] as String, json['latestVersion'] as String,
json['apkUrls'] == null json['apkUrls'] == null
? [] ? []
: List<String>.from(jsonDecode(json['apkUrls'])), : List<String>.from(jsonDecode(json['apkUrls'])),
preferredApkIndex, preferredApkIndex,
additionalSettings, additionalSettings,
json['lastUpdateCheck'] == null json['lastUpdateCheck'] == null
? null ? null
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
json['pinned'] ?? false, json['pinned'] ?? false,
categories: json['categories'] != null categories: json['categories'] != null
? (json['categories'] as List<dynamic>) ? (json['categories'] as List<dynamic>)
.map((e) => e.toString()) .map((e) => e.toString())
.toList() .toList()
: json['category'] != null : json['category'] != null
? [json['category'] as String] ? [json['category'] as String]
: [], : [],
releaseDate: json['releaseDate'] == null releaseDate: json['releaseDate'] == null
? null ? null
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']), : DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
); changeLog:
json['changeLog'] == null ? null : json['changeLog'] as String);
} }
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@@ -173,7 +180,8 @@ class App {
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
'pinned': pinned, 'pinned': pinned,
'categories': categories, 'categories': categories,
'releaseDate': releaseDate?.microsecondsSinceEpoch 'releaseDate': releaseDate?.microsecondsSinceEpoch,
'changeLog': changeLog
}; };
} }
@@ -222,6 +230,7 @@ class AppSource {
String? host; String? host;
late String name; late String name;
bool enforceTrackOnly = false; bool enforceTrackOnly = false;
bool changeLogIfAnyIsMarkDown = true;
AppSource() { AppSource() {
name = runtimeType.toString(); name = runtimeType.toString();
@@ -334,13 +343,15 @@ class SourceProvider {
Codeberg(), Codeberg(),
FDroid(), FDroid(),
IzzyOnDroid(), IzzyOnDroid(),
Mullvad(), FDroidRepo(),
Signal(),
SourceForge(), SourceForge(),
APKMirror(), APKMirror(),
FDroidRepo(), Mullvad(),
SteamMobile(), Signal(),
VLC(),
// WhatsApp(), // As of 2023-03-20 this is unusable as the version on the webpage is months out of date
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
]; ];
@@ -437,7 +448,8 @@ class SourceProvider {
DateTime.now(), DateTime.now(),
currentApp?.pinned ?? false, currentApp?.pinned ?? false,
categories: currentApp?.categories ?? const [], categories: currentApp?.categories ?? const [],
releaseDate: apk.releaseDate); releaseDate: apk.releaseDate,
changeLog: apk.changeLog);
} }
// Returns errors in [results, errors] instead of throwing them // Returns errors in [results, errors] instead of throwing them

View File

@@ -235,6 +235,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" 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: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@@ -325,6 +333,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
markdown:
dependency: transitive
description:
name: markdown
sha256: b3c60dee8c2af50ad0e6e90cceba98e47718a6ee0a7a6772c77846a0cc21f78b
url: "https://pub.dev"
source: hosted
version: "7.0.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@@ -702,10 +718,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: "7ab1e5b646623d6a2537aa59d5d039f90eebef75a7c25e105f6f75de1f7750c3" sha256: "3dedc66ca3c0bef9e6a93c0999aee102556a450afcc1b7bcfeace7a424927d92"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.2" version: "6.1.3"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@@ -766,10 +782,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: webview_flutter name: webview_flutter
sha256: b6cd42db3ced5411f3d01599906156885b18e4188f7065a8a351eb84bee347e0 sha256: "47663d51a9061451aa3880a214ee9a65dcbb933b77bc44388e194279ab3ccaf6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.6" version: "4.0.7"
webview_flutter_android: webview_flutter_android:
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.11.10+131 # When changing this, update the tag in main() accordingly version: 0.11.13+134 # When changing this, update the tag in main() accordingly
environment: environment:
sdk: '>=2.18.2 <3.0.0' sdk: '>=2.18.2 <3.0.0'
@@ -59,6 +59,7 @@ dependencies:
sqflite: ^2.2.0+3 sqflite: ^2.2.0+3
easy_localization: ^3.0.1 easy_localization: ^3.0.1
android_intent_plus: ^3.1.5 android_intent_plus: ^3.1.5
flutter_markdown: ^0.6.14
dev_dependencies: dev_dependencies: