mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-11 17:40:15 +02:00
Various bugfixes + refactors
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:obtainium/pages/home.dart';
|
import 'package:obtainium/pages/home.dart';
|
||||||
import 'package:obtainium/services/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/services/notifications_provider.dart';
|
import 'package:obtainium/providers/notifications_provider.dart';
|
||||||
import 'package:obtainium/services/settings_provider.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:obtainium/services/source_service.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:workmanager/workmanager.dart';
|
import 'package:workmanager/workmanager.dart';
|
||||||
@@ -15,6 +15,7 @@ const String currentReleaseTag =
|
|||||||
|
|
||||||
@pragma('vm:entry-point')
|
@pragma('vm:entry-point')
|
||||||
void bgTaskCallback() {
|
void bgTaskCallback() {
|
||||||
|
// Background update checking process
|
||||||
Workmanager().executeTask((task, taskName) async {
|
Workmanager().executeTask((task, taskName) async {
|
||||||
var appsProvider = AppsProvider(bg: true);
|
var appsProvider = AppsProvider(bg: true);
|
||||||
var notificationsProvider = NotificationsProvider();
|
var notificationsProvider = NotificationsProvider();
|
||||||
@@ -66,9 +67,16 @@ class MyApp extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
SettingsProvider settingsProvider = context.watch<SettingsProvider>();
|
||||||
AppsProvider appsProvider = context.read<AppsProvider>();
|
AppsProvider appsProvider = context.read<AppsProvider>();
|
||||||
|
|
||||||
if (settingsProvider.prefs == null) {
|
if (settingsProvider.prefs == null) {
|
||||||
settingsProvider.initializeSettings();
|
settingsProvider.initializeSettings().then((value) {
|
||||||
|
// Delete past downloads and check for updates every time the app is launched
|
||||||
|
// Only runs once as the settings are only initialized once (so not on every build)
|
||||||
|
appsProvider.deleteSavedAPKs();
|
||||||
|
appsProvider.checkUpdates();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// Register the background update task according to the user's setting
|
||||||
Workmanager().registerPeriodicTask('bg-update-check', 'bg-update-check',
|
Workmanager().registerPeriodicTask('bg-update-check', 'bg-update-check',
|
||||||
frequency: Duration(minutes: settingsProvider.updateInterval),
|
frequency: Duration(minutes: settingsProvider.updateInterval),
|
||||||
initialDelay: Duration(minutes: settingsProvider.updateInterval),
|
initialDelay: Duration(minutes: settingsProvider.updateInterval),
|
||||||
@@ -76,6 +84,7 @@ class MyApp extends StatelessWidget {
|
|||||||
existingWorkPolicy: ExistingWorkPolicy.replace);
|
existingWorkPolicy: ExistingWorkPolicy.replace);
|
||||||
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
bool isFirstRun = settingsProvider.checkAndFlipFirstRun();
|
||||||
if (isFirstRun) {
|
if (isFirstRun) {
|
||||||
|
// If this is the first run, ask for notification permissions and add Obtainium to the Apps list
|
||||||
Permission.notification.request();
|
Permission.notification.request();
|
||||||
appsProvider.saveApp(App(
|
appsProvider.saveApp(App(
|
||||||
'imranr98_obtainium_github',
|
'imranr98_obtainium_github',
|
||||||
@@ -85,12 +94,11 @@ class MyApp extends StatelessWidget {
|
|||||||
currentReleaseTag,
|
currentReleaseTag,
|
||||||
currentReleaseTag, []));
|
currentReleaseTag, []));
|
||||||
}
|
}
|
||||||
appsProvider.deleteSavedAPKs();
|
|
||||||
appsProvider.checkUpdates();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DynamicColorBuilder(
|
return DynamicColorBuilder(
|
||||||
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
builder: (ColorScheme? lightDynamic, ColorScheme? darkDynamic) {
|
||||||
|
// Decide on a colour/brightness scheme based on OS and user settings
|
||||||
ColorScheme lightColorScheme;
|
ColorScheme lightColorScheme;
|
||||||
ColorScheme darkColorScheme;
|
ColorScheme darkColorScheme;
|
||||||
if (lightDynamic != null &&
|
if (lightDynamic != null &&
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:obtainium/pages/app.dart';
|
import 'package:obtainium/pages/app.dart';
|
||||||
import 'package:obtainium/services/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/services/source_service.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class AddAppPage extends StatefulWidget {
|
class AddAppPage extends StatefulWidget {
|
||||||
@@ -52,20 +53,24 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
gettingAppInfo = true;
|
gettingAppInfo = true;
|
||||||
});
|
});
|
||||||
SourceService()
|
sourceProvider()
|
||||||
.getApp(urlInputController.value.text)
|
.getApp(urlInputController.value.text)
|
||||||
.then((app) {
|
.then((app) {
|
||||||
var appsProvider = context.read<AppsProvider>();
|
var appsProvider = context.read<AppsProvider>();
|
||||||
|
var settingsProvider =
|
||||||
|
context.read<SettingsProvider>();
|
||||||
if (appsProvider.apps.containsKey(app.id)) {
|
if (appsProvider.apps.containsKey(app.id)) {
|
||||||
throw 'App already added';
|
throw 'App already added';
|
||||||
}
|
}
|
||||||
appsProvider.saveApp(app).then((_) {
|
settingsProvider.getInstallPermission().then((_) {
|
||||||
urlInputController.clear();
|
appsProvider.saveApp(app).then((_) {
|
||||||
Navigator.push(
|
urlInputController.clear();
|
||||||
context,
|
Navigator.push(
|
||||||
MaterialPageRoute(
|
context,
|
||||||
builder: (context) =>
|
MaterialPageRoute(
|
||||||
AppPage(appId: app.id)));
|
builder: (context) =>
|
||||||
|
AppPage(appId: app.id)));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:obtainium/services/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ class _AppPageState extends State<AppPage> {
|
|||||||
),
|
),
|
||||||
body: WebView(
|
body: WebView(
|
||||||
initialUrl: app?.app.url,
|
initialUrl: app?.app.url,
|
||||||
|
javascriptMode: JavascriptMode.unrestricted,
|
||||||
),
|
),
|
||||||
bottomSheet: Padding(
|
bottomSheet: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:obtainium/pages/app.dart';
|
import 'package:obtainium/pages/app.dart';
|
||||||
import 'package:obtainium/services/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class AppsPage extends StatefulWidget {
|
class AppsPage extends StatefulWidget {
|
||||||
@@ -25,9 +26,15 @@ class _AppsPageState extends State<AppsPage> {
|
|||||||
.isNotEmpty
|
.isNotEmpty
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
for (var e in existingUpdateAppIds) {
|
context
|
||||||
appsProvider.downloadAndInstallLatestApp(e, context);
|
.read<SettingsProvider>()
|
||||||
}
|
.getInstallPermission()
|
||||||
|
.then((_) {
|
||||||
|
for (var e in existingUpdateAppIds) {
|
||||||
|
appsProvider.downloadAndInstallLatestApp(
|
||||||
|
e, context);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.update),
|
icon: const Icon(Icons.update),
|
||||||
label: const Text('Update All')),
|
label: const Text('Update All')),
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:obtainium/services/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/services/settings_provider.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
|
@@ -1,20 +1,18 @@
|
|||||||
// Provider that manages App-related state and provides functions to retrieve App info download/install Apps
|
// Manages state related to the list of Apps tracked by Obtainium,
|
||||||
|
// Exposes related functions such as those used to add, remove, download, and install Apps.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:ui';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:obtainium/services/notifications_provider.dart';
|
import 'package:obtainium/providers/notifications_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:flutter_fgbg/flutter_fgbg.dart';
|
import 'package:flutter_fgbg/flutter_fgbg.dart';
|
||||||
import 'package:obtainium/services/source_service.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
|
||||||
|
|
||||||
class AppInMemory {
|
class AppInMemory {
|
||||||
late App app;
|
late App app;
|
||||||
@@ -31,18 +29,22 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
// Variables to keep track of the app foreground status (installs can't run in the background)
|
// Variables to keep track of the app foreground status (installs can't run in the background)
|
||||||
bool isForeground = true;
|
bool isForeground = true;
|
||||||
StreamSubscription<FGBGType>? foregroundSubscription;
|
late Stream<FGBGType> foregroundStream;
|
||||||
|
late StreamSubscription<FGBGType> foregroundSubscription;
|
||||||
|
|
||||||
AppsProvider({bool bg = false}) {
|
AppsProvider({bool bg = false}) {
|
||||||
// Subscribe to changes in the app foreground status
|
// Subscribe to changes in the app foreground status
|
||||||
foregroundSubscription = FGBGEvents.stream.listen((event) async {
|
foregroundStream = FGBGEvents.stream.asBroadcastStream();
|
||||||
|
foregroundSubscription = foregroundStream.listen((event) async {
|
||||||
isForeground = event == FGBGType.foreground;
|
isForeground = event == FGBGType.foreground;
|
||||||
if (isForeground) await loadApps();
|
if (isForeground) await loadApps();
|
||||||
});
|
});
|
||||||
loadApps();
|
loadApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a App (assumed valid), initiate an APK download (will trigger install callback when complete)
|
// Given an AppId, uses stored info about the app to download an APK (with user input if needed) and install it
|
||||||
|
// Installs can only be done in the foreground, so a notification is sent to get the user's attention if needed
|
||||||
|
// Returns upon successful download, regardless of installation result
|
||||||
Future<void> downloadAndInstallLatestApp(
|
Future<void> downloadAndInstallLatestApp(
|
||||||
String appId, BuildContext context) async {
|
String appId, BuildContext context) async {
|
||||||
var notificationsProvider = context.read<NotificationsProvider>();
|
var notificationsProvider = context.read<NotificationsProvider>();
|
||||||
@@ -107,10 +109,12 @@ class AppsProvider with ChangeNotifier {
|
|||||||
throw response.reasonPhrase ?? 'Unknown Error';
|
throw response.reasonPhrase ?? 'Unknown Error';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isForeground) {
|
while (!isForeground) {
|
||||||
await notificationsProvider.notify(completeInstallationNotification,
|
await notificationsProvider.notify(completeInstallationNotification,
|
||||||
cancelExisting: true);
|
cancelExisting: true);
|
||||||
while (await FGBGEvents.stream.first != FGBGType.foreground) {
|
if (await FGBGEvents.stream.first == FGBGType.foreground ||
|
||||||
|
isForeground) {
|
||||||
|
break;
|
||||||
// We need to wait for the App to come to the foreground to install it
|
// We need to wait for the App to come to the foreground to install it
|
||||||
// Can't try to call install plugin in a background isolate (may not have worked anyways) because of:
|
// Can't try to call install plugin in a background isolate (may not have worked anyways) because of:
|
||||||
// https://github.com/flutter/flutter/issues/13937
|
// https://github.com/flutter/flutter/issues/13937
|
||||||
@@ -120,16 +124,6 @@ class AppsProvider with ChangeNotifier {
|
|||||||
// Unfortunately this 'await' does not actually wait for the APK to finish installing
|
// Unfortunately this 'await' does not actually wait for the APK to finish installing
|
||||||
// So we only know that the install prompt was shown, but the user could still cancel w/o us knowing
|
// So we only know that the install prompt was shown, but the user could still cancel w/o us knowing
|
||||||
// This also does not use the 'session-based' installer API, so background/silent updates are impossible
|
// This also does not use the 'session-based' installer API, so background/silent updates are impossible
|
||||||
while (!(await Permission.requestInstallPackages.isGranted)) {
|
|
||||||
// Explicit request as InstallPlugin request sometimes bugged
|
|
||||||
Fluttertoast.showToast(
|
|
||||||
msg: 'Please allow Obtainium to install Apps',
|
|
||||||
toastLength: Toast.LENGTH_LONG);
|
|
||||||
if ((await Permission.requestInstallPackages.request()) ==
|
|
||||||
PermissionStatus.granted) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await InstallPlugin.installApk(downloadFile.path, 'dev.imranr.obtainium');
|
await InstallPlugin.installApk(downloadFile.path, 'dev.imranr.obtainium');
|
||||||
|
|
||||||
apps[appId]!.app.installedVersion = apps[appId]!.app.latestVersion;
|
apps[appId]!.app.installedVersion = apps[appId]!.app.latestVersion;
|
||||||
@@ -199,7 +193,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<App?> getUpdate(String appId) async {
|
Future<App?> getUpdate(String appId) async {
|
||||||
App? currentApp = apps[appId]!.app;
|
App? currentApp = apps[appId]!.app;
|
||||||
App newApp = await SourceService().getApp(currentApp.url);
|
App newApp = await sourceProvider().getApp(currentApp.url);
|
||||||
if (newApp.latestVersion != currentApp.latestVersion) {
|
if (newApp.latestVersion != currentApp.latestVersion) {
|
||||||
newApp.installedVersion = currentApp.installedVersion;
|
newApp.installedVersion = currentApp.installedVersion;
|
||||||
await saveApp(newApp);
|
await saveApp(newApp);
|
||||||
@@ -252,10 +246,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<int> importApps(String appsJSON) async {
|
Future<int> importApps(String appsJSON) async {
|
||||||
// FilePickerResult? result = await FilePicker.platform.pickFiles(); // Does not work on Android 13
|
// File picker does not work in android 13, so the user must paste the JSON directly into Obtainium to import Apps
|
||||||
|
|
||||||
// if (result != null) {
|
|
||||||
// String appsJSON = File(result.files.single.path!).readAsStringSync();
|
|
||||||
List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>)
|
List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>)
|
||||||
.map((e) => App.fromJson(e))
|
.map((e) => App.fromJson(e))
|
||||||
.toList();
|
.toList();
|
||||||
@@ -266,15 +257,11 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
return importedApps.length;
|
return importedApps.length;
|
||||||
// } else {
|
|
||||||
// User canceled the picker
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
IsolateNameServer.removePortNameMapping('downloader_send_port');
|
foregroundSubscription.cancel();
|
||||||
foregroundSubscription?.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,8 @@
|
|||||||
|
// Exposes functions that can be used to send notifications to the user
|
||||||
|
// Contains a set of pre-defined ObtainiumNotification objects that should be used throughout the app
|
||||||
|
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:obtainium/services/source_service.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
class ObtainiumNotification {
|
class ObtainiumNotification {
|
||||||
late int id;
|
late int id;
|
@@ -1,4 +1,8 @@
|
|||||||
|
// Exposes functions used to save/load app settings
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
enum ThemeSettings { system, light, dark }
|
enum ThemeSettings { system, light, dark }
|
||||||
@@ -22,7 +26,6 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set theme(ThemeSettings t) {
|
set theme(ThemeSettings t) {
|
||||||
print(t);
|
|
||||||
prefs?.setInt('theme', t.index);
|
prefs?.setInt('theme', t.index);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@@ -46,11 +49,24 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAndFlipFirstRun() {
|
bool checkAndFlipFirstRun() {
|
||||||
bool result = prefs?.getBool('firstRun') ?? true;
|
bool result = prefs?.getBool('firstRun') ?? true;
|
||||||
if (result) {
|
if (result) {
|
||||||
prefs?.setBool('firstRun', false);
|
prefs?.setBool('firstRun', false);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> getInstallPermission() async {
|
||||||
|
while (!(await Permission.requestInstallPackages.isGranted)) {
|
||||||
|
// Explicit request as InstallPlugin request sometimes bugged
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: 'Please allow Obtainium to install Apps',
|
||||||
|
toastLength: Toast.LENGTH_LONG);
|
||||||
|
if ((await Permission.requestInstallPackages.request()) ==
|
||||||
|
PermissionStatus.granted) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
// Exposes functions related to interacting with App sources and retrieving App info
|
// Defines App sources and provides functions used to interact with them
|
||||||
// Stateless - not a provider
|
// AppSource is an abstract class with a concrete implementation for each source
|
||||||
|
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
@@ -7,8 +7,6 @@ import 'package:html/dom.dart';
|
|||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
|
|
||||||
// Sub-classes used in App Source
|
|
||||||
|
|
||||||
class AppNames {
|
class AppNames {
|
||||||
late String author;
|
late String author;
|
||||||
late String name;
|
late String name;
|
||||||
@@ -23,34 +21,6 @@ class APKDetails {
|
|||||||
APKDetails(this.version, this.apkUrls);
|
APKDetails(this.version, this.apkUrls);
|
||||||
}
|
}
|
||||||
|
|
||||||
// App Source abstract class (diff. implementations for GitHub, GitLab, etc.)
|
|
||||||
|
|
||||||
abstract class AppSource {
|
|
||||||
late String sourceId;
|
|
||||||
String standardizeURL(String url);
|
|
||||||
Future<APKDetails> getLatestAPKDetails(String standardUrl);
|
|
||||||
AppNames getAppNames(String standardUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
escapeRegEx(String s) {
|
|
||||||
return s.replaceAllMapped(RegExp(r'[.*+?^${}()|[\]\\]'), (x) {
|
|
||||||
return "\\${x[0]}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> getLinksFromParsedHTML(
|
|
||||||
Document dom, RegExp hrefPattern, String prependToLinks) =>
|
|
||||||
dom
|
|
||||||
.querySelectorAll('a')
|
|
||||||
.where((element) {
|
|
||||||
if (element.attributes['href'] == null) return false;
|
|
||||||
return hrefPattern.hasMatch(element.attributes['href']!);
|
|
||||||
})
|
|
||||||
.map((e) => '$prependToLinks${e.attributes['href']!}')
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
// App class
|
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
late String id;
|
late String id;
|
||||||
late String url;
|
late String url;
|
||||||
@@ -89,7 +59,29 @@ class App {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specific App Source classes
|
escapeRegEx(String s) {
|
||||||
|
return s.replaceAllMapped(RegExp(r'[.*+?^${}()|[\]\\]'), (x) {
|
||||||
|
return "\\${x[0]}";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> getLinksFromParsedHTML(
|
||||||
|
Document dom, RegExp hrefPattern, String prependToLinks) =>
|
||||||
|
dom
|
||||||
|
.querySelectorAll('a')
|
||||||
|
.where((element) {
|
||||||
|
if (element.attributes['href'] == null) return false;
|
||||||
|
return hrefPattern.hasMatch(element.attributes['href']!);
|
||||||
|
})
|
||||||
|
.map((e) => '$prependToLinks${e.attributes['href']!}')
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
abstract class AppSource {
|
||||||
|
late String sourceId;
|
||||||
|
String standardizeURL(String url);
|
||||||
|
Future<APKDetails> getLatestAPKDetails(String standardUrl);
|
||||||
|
AppNames getAppNames(String standardUrl);
|
||||||
|
}
|
||||||
|
|
||||||
class GitHub implements AppSource {
|
class GitHub implements AppSource {
|
||||||
@override
|
@override
|
||||||
@@ -203,7 +195,7 @@ class GitLab implements AppSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourceService {
|
class sourceProvider {
|
||||||
// Add more source classes here so they are available via the service
|
// Add more source classes here so they are available via the service
|
||||||
AppSource getSource(String url) {
|
AppSource getSource(String url) {
|
||||||
if (url.toLowerCase().contains('://github.com')) {
|
if (url.toLowerCase().contains('://github.com')) {
|
Reference in New Issue
Block a user