mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-22 14:09:29 +02:00
Added an (experimental) Source override option for URLs that work with an existing Source but use a custom host (#271, #393) (#502)
This commit is contained in:
@@ -17,7 +17,6 @@ Currently supported App sources:
|
|||||||
- [SourceForge](https://sourceforge.net/)
|
- [SourceForge](https://sourceforge.net/)
|
||||||
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
||||||
- Third Party F-Droid Repos
|
- 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)
|
- [Steam](https://store.steampowered.com/mobile)
|
||||||
- [Telegram App](https://telegram.org)
|
- [Telegram App](https://telegram.org)
|
||||||
- [VLC](https://www.videolan.org/vlc/download-android.html)
|
- [VLC](https://www.videolan.org/vlc/download-android.html)
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
|
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
|
||||||
"remove": "Entfernen",
|
"remove": "Entfernen",
|
||||||
"yesMarkUpdated": "Ja, als aktualisiert markieren",
|
"yesMarkUpdated": "Ja, als aktualisiert markieren",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "App ID oder Name",
|
"appIdOrName": "App ID oder Name",
|
||||||
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
|
"appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
|
||||||
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
|
"reposHaveMultipleApps": "Repos können mehrere Apps enthalten",
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "Last Update Check: {}",
|
"lastUpdateCheckX": "Last Update Check: {}",
|
||||||
"remove": "Remove",
|
"remove": "Remove",
|
||||||
"yesMarkUpdated": "Yes, Mark as Updated",
|
"yesMarkUpdated": "Yes, Mark as Updated",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "App ID or Name",
|
"appIdOrName": "App ID or Name",
|
||||||
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
||||||
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
||||||
@@ -224,6 +224,7 @@
|
|||||||
"standardVersionDetection": "Standard version detection",
|
"standardVersionDetection": "Standard version detection",
|
||||||
"groupByCategory": "Group by Category",
|
"groupByCategory": "Group by Category",
|
||||||
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
|
"autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
|
||||||
|
"overrideSource": "Override Source",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "بررسی آخرین بهروزرسانی: {}",
|
"lastUpdateCheckX": "بررسی آخرین بهروزرسانی: {}",
|
||||||
"remove": "حذف",
|
"remove": "حذف",
|
||||||
"yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده",
|
"yesMarkUpdated": "بله، علامت گذاری به عنوان به روز شده",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "شناسه یا نام برنامه",
|
"appIdOrName": "شناسه یا نام برنامه",
|
||||||
"appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد",
|
"appWithIdOrNameNotFound": "هیچ برنامه ای با آن شناسه یا نام یافت نشد",
|
||||||
"reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد",
|
"reposHaveMultipleApps": "مخازن ممکن است شامل چندین برنامه باشد",
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
|
"lastUpdateCheckX": "Vérification de la dernière mise à jour : {}",
|
||||||
"remove": "Retirer",
|
"remove": "Retirer",
|
||||||
"yesMarkUpdated": "Oui, marquer comme mis à jour",
|
"yesMarkUpdated": "Oui, marquer comme mis à jour",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "ID ou nom de l'application",
|
"appIdOrName": "ID ou nom de l'application",
|
||||||
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
|
"appWithIdOrNameNotFound": "Aucune application n'a été trouvée avec cet identifiant ou ce nom",
|
||||||
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
|
"reposHaveMultipleApps": "Les dépôts peuvent contenir plusieurs applications",
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "Frissítés ellenőrizve: {}",
|
"lastUpdateCheckX": "Frissítés ellenőrizve: {}",
|
||||||
"remove": "Eltávolítás",
|
"remove": "Eltávolítás",
|
||||||
"yesMarkUpdated": "Igen, megjelölés frissítettként",
|
"yesMarkUpdated": "Igen, megjelölés frissítettként",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "App ID vagy név",
|
"appIdOrName": "App ID vagy név",
|
||||||
"appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel",
|
"appWithIdOrNameNotFound": "Nem található app ezzel az azonosítóval vagy névvel",
|
||||||
"reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak",
|
"reposHaveMultipleApps": "A repók több alkalmazást is tartalmazhatnak",
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
|
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
|
||||||
"remove": "Rimuovi",
|
"remove": "Rimuovi",
|
||||||
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "ID o nome dell'App",
|
"appIdOrName": "ID o nome dell'App",
|
||||||
"appWithIdOrNameNotFound": "Non è stata trovata alcuna App con quell'ID o nome",
|
"appWithIdOrNameNotFound": "Non è stata trovata alcuna App con quell'ID o nome",
|
||||||
"reposHaveMultipleApps": "I repository possono contenere più App",
|
"reposHaveMultipleApps": "I repository possono contenere più App",
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "最終アップデート確認: {}",
|
"lastUpdateCheckX": "最終アップデート確認: {}",
|
||||||
"remove": "削除",
|
"remove": "削除",
|
||||||
"yesMarkUpdated": "はい、アップデート済みとしてマークします",
|
"yesMarkUpdated": "はい、アップデート済みとしてマークします",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "アプリのIDまたは名前",
|
"appIdOrName": "アプリのIDまたは名前",
|
||||||
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
|
"appWithIdOrNameNotFound": "そのIDや名前を持つアプリは見つかりませんでした",
|
||||||
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
|
"reposHaveMultipleApps": "リポジトリには複数のアプリが含まれることがあります",
|
||||||
|
@@ -179,7 +179,7 @@
|
|||||||
"lastUpdateCheckX": "上次更新检查:{}",
|
"lastUpdateCheckX": "上次更新检查:{}",
|
||||||
"remove": "删除",
|
"remove": "删除",
|
||||||
"yesMarkUpdated": "是的,标记为已更新",
|
"yesMarkUpdated": "是的,标记为已更新",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid Official",
|
||||||
"appIdOrName": "应用 ID 或名称",
|
"appIdOrName": "应用 ID 或名称",
|
||||||
"appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用",
|
"appWithIdOrNameNotFound": "未找到符合此 ID 或名称的应用",
|
||||||
"reposHaveMultipleApps": "存储库中可能包含多个应用",
|
"reposHaveMultipleApps": "存储库中可能包含多个应用",
|
||||||
|
@@ -31,7 +31,7 @@ class APKMirror extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/apk/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/apk/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -39,7 +39,7 @@ class Codeberg extends AppSource {
|
|||||||
var gh = GitHub();
|
var gh = GitHub();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -12,16 +12,15 @@ class FDroid extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB =
|
RegExp standardUrlRegExB =
|
||||||
RegExp('^https?://(cloudflare\\.)?$host/+[^/]+/+packages/+[^/]+');
|
RegExp('^https?://$host/+[^/]+/+packages/+[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url =
|
url =
|
||||||
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}';
|
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA =
|
RegExp standardUrlRegExA = RegExp('^https?://$host/+packages/+[^/]+');
|
||||||
RegExp('^https?://(cloudflare\\.)?$host/+packages/+[^/]+');
|
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@@ -19,17 +19,6 @@ class FDroidRepo extends AppSource {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
String standardizeURL(String url) {
|
|
||||||
RegExp standardUrlRegExp =
|
|
||||||
RegExp('^https?://.+/fdroid/([^/]+(/|\\?)|[^/]+\$)');
|
|
||||||
RegExpMatch? match = standardUrlRegExp.firstMatch(url.toLowerCase());
|
|
||||||
if (match == null) {
|
|
||||||
throw InvalidURLError(name);
|
|
||||||
}
|
|
||||||
return url.substring(0, match.end);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<APKDetails> getLatestAPKDetails(
|
Future<APKDetails> getLatestAPKDetails(
|
||||||
String standardUrl,
|
String standardUrl,
|
||||||
|
@@ -75,7 +75,7 @@ class GitHub extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -19,7 +19,7 @@ class GitLab extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -6,7 +6,7 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
|
|
||||||
class HTML extends AppSource {
|
class HTML extends AppSource {
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ class IzzyOnDroid extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/repo/apk/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/repo/apk/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -10,7 +10,7 @@ class Mullvad extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host');
|
RegExp standardUrlRegEx = RegExp('^https?://$host');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -9,7 +9,7 @@ class NeutronCode extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/downloads/file/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/downloads/file/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -9,7 +9,7 @@ class Signal extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://$host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ class SourceForge extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/projects/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://$host/projects/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
|
@@ -20,7 +20,7 @@ class SteamMobile extends AppSource {
|
|||||||
final apks = {'steam': tr('steamMobile'), 'steam-chat-app': tr('steamChat')};
|
final apks = {'steam': tr('steamMobile'), 'steam-chat-app': tr('steamChat')};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://$host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ class TelegramApp extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://$host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ class VLC extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://$host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ class WhatsApp extends AppSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String standardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
return 'https://$host';
|
return 'https://$host';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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.36';
|
const String currentVersion = '0.12.0';
|
||||||
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
|
||||||
|
|
||||||
|
@@ -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:obtainium/app_sources/html.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';
|
||||||
@@ -28,6 +29,7 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
|
|
||||||
String userInput = '';
|
String userInput = '';
|
||||||
String searchQuery = '';
|
String searchQuery = '';
|
||||||
|
String? pickedSourceOverride;
|
||||||
AppSource? pickedSource;
|
AppSource? pickedSource;
|
||||||
Map<String, dynamic> additionalSettings = {};
|
Map<String, dynamic> additionalSettings = {};
|
||||||
bool additionalSettingsValid = true;
|
bool additionalSettingsValid = true;
|
||||||
@@ -49,8 +51,13 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
if (isSearch) {
|
if (isSearch) {
|
||||||
searchnum++;
|
searchnum++;
|
||||||
}
|
}
|
||||||
var source = valid ? sourceProvider.getSource(userInput) : null;
|
var prevHost = pickedSource?.host;
|
||||||
if (pickedSource.runtimeType != source.runtimeType) {
|
var source = valid
|
||||||
|
? sourceProvider.getSource(userInput,
|
||||||
|
overrideSource: pickedSourceOverride)
|
||||||
|
: null;
|
||||||
|
if (pickedSource.runtimeType != source.runtimeType ||
|
||||||
|
(prevHost != null && prevHost != source?.host)) {
|
||||||
pickedSource = source;
|
pickedSource = source;
|
||||||
additionalSettings = source != null
|
additionalSettings = source != null
|
||||||
? getDefaultValuesFromFormItems(
|
? getDefaultValuesFromFormItems(
|
||||||
@@ -115,7 +122,8 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
var trackOnly = pickedSource!.enforceTrackOnly || userPickedTrackOnly;
|
var trackOnly = pickedSource!.enforceTrackOnly || userPickedTrackOnly;
|
||||||
app = await sourceProvider.getApp(
|
app = await sourceProvider.getApp(
|
||||||
pickedSource!, userInput, additionalSettings,
|
pickedSource!, userInput, additionalSettings,
|
||||||
trackOnlyOverride: trackOnly);
|
trackOnlyOverride: trackOnly,
|
||||||
|
overrideSource: pickedSourceOverride);
|
||||||
if (!trackOnly) {
|
if (!trackOnly) {
|
||||||
await settingsProvider.getInstallPermission();
|
await settingsProvider.getInstallPermission();
|
||||||
}
|
}
|
||||||
@@ -173,9 +181,9 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
(value) {
|
(value) {
|
||||||
try {
|
try {
|
||||||
sourceProvider
|
sourceProvider
|
||||||
.getSource(value ?? '')
|
.getSource(value ?? '',
|
||||||
.standardizeURL(
|
overrideSource: pickedSourceOverride)
|
||||||
preStandardizeUrl(value ?? ''));
|
.standardizeUrl(value ?? '');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return e is String
|
return e is String
|
||||||
? e
|
? e
|
||||||
@@ -260,6 +268,48 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget getHTMLSourceOverrideDropdown() => Column(children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: GeneratedForm(
|
||||||
|
items: [
|
||||||
|
[
|
||||||
|
GeneratedFormDropdown(
|
||||||
|
'overrideSource',
|
||||||
|
defaultValue: HTML().runtimeType.toString(),
|
||||||
|
[
|
||||||
|
...sourceProvider.sources.map(
|
||||||
|
(s) => MapEntry(s.runtimeType.toString(), s.name))
|
||||||
|
],
|
||||||
|
label: tr('overrideSource'))
|
||||||
|
]
|
||||||
|
],
|
||||||
|
onValueChanges: (values, valid, isBuilding) {
|
||||||
|
fn() {
|
||||||
|
pickedSourceOverride = (values['overrideSource'] == null ||
|
||||||
|
values['overrideSource'] == '')
|
||||||
|
? null
|
||||||
|
: values['overrideSource'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isBuilding) {
|
||||||
|
setState(() {
|
||||||
|
fn();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
changeUserInput(userInput, valid, isBuilding);
|
||||||
|
},
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 25,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
bool shouldShowSearchBar() =>
|
bool shouldShowSearchBar() =>
|
||||||
sourceProvider.sources.where((e) => e.canSearch).isNotEmpty &&
|
sourceProvider.sources.where((e) => e.canSearch).isNotEmpty &&
|
||||||
pickedSource == null &&
|
pickedSource == null &&
|
||||||
@@ -309,6 +359,10 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
|
if (pickedSourceOverride != null ||
|
||||||
|
pickedSource.runtimeType.toString() ==
|
||||||
|
HTML().runtimeType.toString())
|
||||||
|
getHTMLSourceOverrideDropdown(),
|
||||||
GeneratedForm(
|
GeneratedForm(
|
||||||
key: Key(pickedSource.runtimeType.toString()),
|
key: Key(pickedSource.runtimeType.toString()),
|
||||||
items: pickedSource!.combinedAppSpecificSettingFormItems,
|
items: pickedSource!.combinedAppSpecificSettingFormItems,
|
||||||
@@ -379,6 +433,9 @@ class _AddAppPageState extends State<AddAppPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
getUrlInputRow(),
|
getUrlInputRow(),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
if (shouldShowSearchBar())
|
if (shouldShowSearchBar())
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
|
@@ -39,7 +39,10 @@ class _AppPageState extends State<AppPage> {
|
|||||||
|
|
||||||
var sourceProvider = SourceProvider();
|
var sourceProvider = SourceProvider();
|
||||||
AppInMemory? app = appsProvider.apps[widget.appId]?.deepCopy();
|
AppInMemory? app = appsProvider.apps[widget.appId]?.deepCopy();
|
||||||
var source = app != null ? sourceProvider.getSource(app.app.url) : null;
|
var source = app != null
|
||||||
|
? sourceProvider.getSource(app.app.url,
|
||||||
|
overrideSource: app.app.overrideSource)
|
||||||
|
: null;
|
||||||
if (!areDownloadsRunning && prevApp == null && app != null) {
|
if (!areDownloadsRunning && prevApp == null && app != null) {
|
||||||
prevApp = app;
|
prevApp = app;
|
||||||
getUpdate(app.app.id);
|
getUpdate(app.app.id);
|
||||||
|
@@ -111,7 +111,11 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (filter.sourceFilter.isNotEmpty &&
|
if (filter.sourceFilter.isNotEmpty &&
|
||||||
sourceProvider.getSource(app.app.url).runtimeType.toString() !=
|
sourceProvider
|
||||||
|
.getSource(app.app.url,
|
||||||
|
overrideSource: app.app.overrideSource)
|
||||||
|
.runtimeType
|
||||||
|
.toString() !=
|
||||||
filter.sourceFilter) {
|
filter.sourceFilter) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -306,8 +310,9 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getChangeLogFn(int appIndex) {
|
getChangeLogFn(int appIndex) {
|
||||||
AppSource appSource =
|
AppSource appSource = SourceProvider().getSource(
|
||||||
SourceProvider().getSource(listedApps[appIndex].app.url);
|
listedApps[appIndex].app.url,
|
||||||
|
overrideSource: listedApps[appIndex].app.overrideSource);
|
||||||
String? changesUrl =
|
String? changesUrl =
|
||||||
appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
|
appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
|
||||||
String? changeLog = listedApps[appIndex].app.changeLog;
|
String? changeLog = listedApps[appIndex].app.changeLog;
|
||||||
|
@@ -172,7 +172,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String downloadUrl = await SourceProvider()
|
String downloadUrl = await SourceProvider()
|
||||||
.getSource(app.url)
|
.getSource(app.url, overrideSource: app.overrideSource)
|
||||||
.apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex].value);
|
.apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex].value);
|
||||||
var fileName = '${app.id}-${downloadUrl.hashCode}.apk';
|
var fileName = '${app.id}-${downloadUrl.hashCode}.apk';
|
||||||
var notif = DownloadNotification(app.finalName, 100);
|
var notif = DownloadNotification(app.finalName, 100);
|
||||||
@@ -647,7 +647,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
for (int i = 0; i < newApps.length; i++) {
|
for (int i = 0; i < newApps.length; i++) {
|
||||||
var info = await getInstalledInfo(newApps[i].id);
|
var info = await getInstalledInfo(newApps[i].id);
|
||||||
try {
|
try {
|
||||||
sp.getSource(newApps[i].url);
|
sp.getSource(newApps[i].url, overrideSource: newApps[i].overrideSource);
|
||||||
apps[newApps[i].id] = AppInMemory(newApps[i], null, info);
|
apps[newApps[i].id] = AppInMemory(newApps[i], null, info);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errors.add([newApps[i].id, newApps[i].finalName, e.toString()]);
|
errors.add([newApps[i].id, newApps[i].finalName, e.toString()]);
|
||||||
@@ -787,7 +787,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
App? currentApp = apps[appId]!.app;
|
App? currentApp = apps[appId]!.app;
|
||||||
SourceProvider sourceProvider = SourceProvider();
|
SourceProvider sourceProvider = SourceProvider();
|
||||||
App newApp = await sourceProvider.getApp(
|
App newApp = await sourceProvider.getApp(
|
||||||
sourceProvider.getSource(currentApp.url),
|
sourceProvider.getSource(currentApp.url,
|
||||||
|
overrideSource: currentApp.overrideSource),
|
||||||
currentApp.url,
|
currentApp.url,
|
||||||
currentApp.additionalSettings,
|
currentApp.additionalSettings,
|
||||||
currentApp: currentApp);
|
currentApp: currentApp);
|
||||||
|
@@ -44,69 +44,17 @@ class APKDetails {
|
|||||||
{this.releaseDate, this.changeLog});
|
{this.releaseDate, this.changeLog});
|
||||||
}
|
}
|
||||||
|
|
||||||
class App {
|
stringMapListTo2DList(List<MapEntry<String, String>> mapList) =>
|
||||||
late String id;
|
mapList.map((e) => [e.key, e.value]).toList();
|
||||||
late String url;
|
|
||||||
late String author;
|
|
||||||
late String name;
|
|
||||||
String? installedVersion;
|
|
||||||
late String latestVersion;
|
|
||||||
List<MapEntry<String, String>> apkUrls = [];
|
|
||||||
late int preferredApkIndex;
|
|
||||||
late Map<String, dynamic> additionalSettings;
|
|
||||||
late DateTime? lastUpdateCheck;
|
|
||||||
bool pinned = false;
|
|
||||||
List<String> categories;
|
|
||||||
late DateTime? releaseDate;
|
|
||||||
late String? changeLog;
|
|
||||||
App(
|
|
||||||
this.id,
|
|
||||||
this.url,
|
|
||||||
this.author,
|
|
||||||
this.name,
|
|
||||||
this.installedVersion,
|
|
||||||
this.latestVersion,
|
|
||||||
this.apkUrls,
|
|
||||||
this.preferredApkIndex,
|
|
||||||
this.additionalSettings,
|
|
||||||
this.lastUpdateCheck,
|
|
||||||
this.pinned,
|
|
||||||
{this.categories = const [],
|
|
||||||
this.releaseDate,
|
|
||||||
this.changeLog});
|
|
||||||
|
|
||||||
@override
|
assumed2DlistToStringMapList(List<dynamic> arr) =>
|
||||||
String toString() {
|
arr.map((e) => MapEntry(e[0] as String, e[1] as String)).toList();
|
||||||
return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALSETTINGS: ${additionalSettings.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned';
|
|
||||||
}
|
|
||||||
|
|
||||||
String? get overrideName =>
|
// App JSON schema has changed multiple times over the many versions of Obtainium
|
||||||
additionalSettings['appName']?.toString().trim().isNotEmpty == true
|
// This function takes an App JSON and modifies it if needed to conform to the latest (current) version
|
||||||
? additionalSettings['appName']
|
appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
||||||
: null;
|
var source = SourceProvider()
|
||||||
|
.getSource(json['url'], overrideSource: json['overrideSource']);
|
||||||
String get finalName {
|
|
||||||
return overrideName ?? name;
|
|
||||||
}
|
|
||||||
|
|
||||||
App deepCopy() => App(
|
|
||||||
id,
|
|
||||||
url,
|
|
||||||
author,
|
|
||||||
name,
|
|
||||||
installedVersion,
|
|
||||||
latestVersion,
|
|
||||||
apkUrls,
|
|
||||||
preferredApkIndex,
|
|
||||||
Map.from(additionalSettings),
|
|
||||||
lastUpdateCheck,
|
|
||||||
pinned,
|
|
||||||
categories: categories,
|
|
||||||
changeLog: changeLog,
|
|
||||||
releaseDate: releaseDate);
|
|
||||||
|
|
||||||
factory App.fromJson(Map<String, dynamic> json) {
|
|
||||||
var source = SourceProvider().getSource(json['url']);
|
|
||||||
var formItems = source.combinedAppSpecificSettingFormItems
|
var formItems = source.combinedAppSpecificSettingFormItems
|
||||||
.reduce((value, element) => [...value, ...element]);
|
.reduce((value, element) => [...value, ...element]);
|
||||||
Map<String, dynamic> additionalSettings =
|
Map<String, dynamic> additionalSettings =
|
||||||
@@ -154,12 +102,12 @@ class App {
|
|||||||
item.ensureType(additionalSettings[item.key]);
|
item.ensureType(additionalSettings[item.key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int preferredApkIndex = json['preferredApkIndex'] == null
|
int preferredApkIndex =
|
||||||
? 0
|
json['preferredApkIndex'] == null ? 0 : json['preferredApkIndex'] as int;
|
||||||
: json['preferredApkIndex'] as int;
|
|
||||||
if (preferredApkIndex < 0) {
|
if (preferredApkIndex < 0) {
|
||||||
preferredApkIndex = 0;
|
preferredApkIndex = 0;
|
||||||
}
|
}
|
||||||
|
json['preferredApkIndex'] = preferredApkIndex;
|
||||||
// apkUrls can either be old list or new named list apkUrls
|
// apkUrls can either be old list or new named list apkUrls
|
||||||
List<MapEntry<String, String>> apkUrls = [];
|
List<MapEntry<String, String>> apkUrls = [];
|
||||||
if (json['apkUrls'] != null) {
|
if (json['apkUrls'] != null) {
|
||||||
@@ -167,16 +115,101 @@ class App {
|
|||||||
try {
|
try {
|
||||||
apkUrls = getApkUrlsFromUrls(List<String>.from(apkUrlJson));
|
apkUrls = getApkUrlsFromUrls(List<String>.from(apkUrlJson));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
apkUrls = assumed2DlistToStringMapList(List<dynamic>.from(apkUrlJson));
|
||||||
apkUrls = List<dynamic>.from(apkUrlJson)
|
apkUrls = List<dynamic>.from(apkUrlJson)
|
||||||
.map((e) => MapEntry(e[0] as String, e[1] as String))
|
.map((e) => MapEntry(e[0] as String, e[1] as String))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
json['apkUrls'] = jsonEncode(stringMapListTo2DList(apkUrls));
|
||||||
}
|
}
|
||||||
// Arch based APK filter option should be disabled if it previously did not exist
|
// Arch based APK filter option should be disabled if it previously did not exist
|
||||||
if (json['additionalSettings'] != null &&
|
if (additionalSettings['autoApkFilterByArch'] == null) {
|
||||||
jsonDecode(json['additionalSettings'])['autoApkFilterByArch'] == null) {
|
|
||||||
additionalSettings['autoApkFilterByArch'] = false;
|
additionalSettings['autoApkFilterByArch'] = false;
|
||||||
}
|
}
|
||||||
|
json['additionalSettings'] = jsonEncode(additionalSettings);
|
||||||
|
// F-Droid no longer needs cloudflare exception since override can be used - migrate apps appropriately
|
||||||
|
// This allows us to reverse the changes made for issue #418 (support cloudflare.f-droid)
|
||||||
|
// While not causing problems for existing apps from that source that were added in a previous version
|
||||||
|
var overrideSourceWasUndefined = !json.keys.contains('overrideSource');
|
||||||
|
if ((json['url'] as String).startsWith('https://cloudflare.f-droid.org')) {
|
||||||
|
json['overrideSource'] = FDroid().runtimeType.toString();
|
||||||
|
} else if (overrideSourceWasUndefined) {
|
||||||
|
// Similar to above, but for third-party F-Droid repos
|
||||||
|
RegExpMatch? match = RegExp('^https?://.+/fdroid/([^/]+(/|\\?)|[^/]+\$)')
|
||||||
|
.firstMatch(json['url'] as String);
|
||||||
|
if (match != null) {
|
||||||
|
json['overrideSource'] = FDroidRepo().runtimeType.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
class App {
|
||||||
|
late String id;
|
||||||
|
late String url;
|
||||||
|
late String author;
|
||||||
|
late String name;
|
||||||
|
String? installedVersion;
|
||||||
|
late String latestVersion;
|
||||||
|
List<MapEntry<String, String>> apkUrls = [];
|
||||||
|
late int preferredApkIndex;
|
||||||
|
late Map<String, dynamic> additionalSettings;
|
||||||
|
late DateTime? lastUpdateCheck;
|
||||||
|
bool pinned = false;
|
||||||
|
List<String> categories;
|
||||||
|
late DateTime? releaseDate;
|
||||||
|
late String? changeLog;
|
||||||
|
late String? overrideSource;
|
||||||
|
App(
|
||||||
|
this.id,
|
||||||
|
this.url,
|
||||||
|
this.author,
|
||||||
|
this.name,
|
||||||
|
this.installedVersion,
|
||||||
|
this.latestVersion,
|
||||||
|
this.apkUrls,
|
||||||
|
this.preferredApkIndex,
|
||||||
|
this.additionalSettings,
|
||||||
|
this.lastUpdateCheck,
|
||||||
|
this.pinned,
|
||||||
|
{this.categories = const [],
|
||||||
|
this.releaseDate,
|
||||||
|
this.changeLog,
|
||||||
|
this.overrideSource});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALSETTINGS: ${additionalSettings.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned';
|
||||||
|
}
|
||||||
|
|
||||||
|
String? get overrideName =>
|
||||||
|
additionalSettings['appName']?.toString().trim().isNotEmpty == true
|
||||||
|
? additionalSettings['appName']
|
||||||
|
: null;
|
||||||
|
|
||||||
|
String get finalName {
|
||||||
|
return overrideName ?? name;
|
||||||
|
}
|
||||||
|
|
||||||
|
App deepCopy() => App(
|
||||||
|
id,
|
||||||
|
url,
|
||||||
|
author,
|
||||||
|
name,
|
||||||
|
installedVersion,
|
||||||
|
latestVersion,
|
||||||
|
apkUrls,
|
||||||
|
preferredApkIndex,
|
||||||
|
Map.from(additionalSettings),
|
||||||
|
lastUpdateCheck,
|
||||||
|
pinned,
|
||||||
|
categories: categories,
|
||||||
|
changeLog: changeLog,
|
||||||
|
releaseDate: releaseDate,
|
||||||
|
overrideSource: overrideSource);
|
||||||
|
|
||||||
|
factory App.fromJson(Map<String, dynamic> json) {
|
||||||
|
json = appJSONCompatibilityModifiers(json);
|
||||||
return App(
|
return App(
|
||||||
json['id'] as String,
|
json['id'] as String,
|
||||||
json['url'] as String,
|
json['url'] as String,
|
||||||
@@ -186,9 +219,9 @@ class App {
|
|||||||
? null
|
? null
|
||||||
: json['installedVersion'] as String,
|
: json['installedVersion'] as String,
|
||||||
json['latestVersion'] as String,
|
json['latestVersion'] as String,
|
||||||
apkUrls,
|
assumed2DlistToStringMapList(jsonDecode(json['apkUrls'])),
|
||||||
preferredApkIndex,
|
json['preferredApkIndex'] as int,
|
||||||
additionalSettings,
|
jsonDecode(json['additionalSettings']) as Map<String, dynamic>,
|
||||||
json['lastUpdateCheck'] == null
|
json['lastUpdateCheck'] == null
|
||||||
? null
|
? null
|
||||||
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
|
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
|
||||||
@@ -204,7 +237,8 @@ class App {
|
|||||||
? null
|
? null
|
||||||
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
|
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
|
||||||
changeLog:
|
changeLog:
|
||||||
json['changeLog'] == null ? null : json['changeLog'] as String);
|
json['changeLog'] == null ? null : json['changeLog'] as String,
|
||||||
|
overrideSource: json['overrideSource']);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
@@ -214,14 +248,15 @@ class App {
|
|||||||
'name': name,
|
'name': name,
|
||||||
'installedVersion': installedVersion,
|
'installedVersion': installedVersion,
|
||||||
'latestVersion': latestVersion,
|
'latestVersion': latestVersion,
|
||||||
'apkUrls': jsonEncode(apkUrls.map((e) => [e.key, e.value]).toList()),
|
'apkUrls': jsonEncode(stringMapListTo2DList(apkUrls)),
|
||||||
'preferredApkIndex': preferredApkIndex,
|
'preferredApkIndex': preferredApkIndex,
|
||||||
'additionalSettings': jsonEncode(additionalSettings),
|
'additionalSettings': jsonEncode(additionalSettings),
|
||||||
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
|
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
|
||||||
'pinned': pinned,
|
'pinned': pinned,
|
||||||
'categories': categories,
|
'categories': categories,
|
||||||
'releaseDate': releaseDate?.microsecondsSinceEpoch,
|
'releaseDate': releaseDate?.microsecondsSinceEpoch,
|
||||||
'changeLog': changeLog
|
'changeLog': changeLog,
|
||||||
|
'overrideSource': overrideSource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,8 +308,9 @@ List<MapEntry<String, String>> getApkUrlsFromUrls(List<String> urls) =>
|
|||||||
return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e);
|
return MapEntry(apkSegs.isNotEmpty ? apkSegs.last : segments.last, e);
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
class AppSource {
|
abstract class AppSource {
|
||||||
String? host;
|
String? host;
|
||||||
|
bool hostChanged = false;
|
||||||
late String name;
|
late String name;
|
||||||
bool enforceTrackOnly = false;
|
bool enforceTrackOnly = false;
|
||||||
bool changeLogIfAnyIsMarkDown = true;
|
bool changeLogIfAnyIsMarkDown = true;
|
||||||
@@ -283,7 +319,15 @@ class AppSource {
|
|||||||
name = runtimeType.toString();
|
name = runtimeType.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String standardizeURL(String url) {
|
String standardizeUrl(String url) {
|
||||||
|
url = preStandardizeUrl(url);
|
||||||
|
if (!hostChanged) {
|
||||||
|
url = sourceSpecificStandardizeURL(url);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
throw NotImplementedError();
|
throw NotImplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,7 +433,7 @@ regExValidator(String? value) {
|
|||||||
|
|
||||||
class SourceProvider {
|
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
|
||||||
List<AppSource> sources = [
|
List<AppSource> get sources => [
|
||||||
GitHub(),
|
GitHub(),
|
||||||
GitLab(),
|
GitLab(),
|
||||||
Codeberg(),
|
Codeberg(),
|
||||||
@@ -411,11 +455,22 @@ class SourceProvider {
|
|||||||
// Add more mass url source classes here so they are available via the service
|
// Add more mass url source classes here so they are available via the service
|
||||||
List<MassAppUrlSource> massUrlSources = [GitHubStars()];
|
List<MassAppUrlSource> massUrlSources = [GitHubStars()];
|
||||||
|
|
||||||
AppSource getSource(String url) {
|
AppSource getSource(String url, {String? overrideSource}) {
|
||||||
url = preStandardizeUrl(url);
|
url = preStandardizeUrl(url);
|
||||||
|
if (overrideSource != null) {
|
||||||
|
var srcs =
|
||||||
|
sources.where((e) => e.runtimeType.toString() == overrideSource);
|
||||||
|
if (srcs.isEmpty) {
|
||||||
|
throw UnsupportedURLError();
|
||||||
|
}
|
||||||
|
var res = srcs.first;
|
||||||
|
res.host = Uri.parse(url).host;
|
||||||
|
res.hostChanged = true;
|
||||||
|
return srcs.first;
|
||||||
|
}
|
||||||
AppSource? source;
|
AppSource? source;
|
||||||
for (var s in sources.where((element) => element.host != null)) {
|
for (var s in sources.where((element) => element.host != null)) {
|
||||||
if (RegExp('://(.+\\.)?${s.host}').hasMatch(url)) {
|
if (RegExp('://${s.host}').hasMatch(url)) {
|
||||||
source = s;
|
source = s;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -423,7 +478,7 @@ class SourceProvider {
|
|||||||
if (source == null) {
|
if (source == null) {
|
||||||
for (var s in sources.where((element) => element.host == null)) {
|
for (var s in sources.where((element) => element.host == null)) {
|
||||||
try {
|
try {
|
||||||
s.standardizeURL(url);
|
s.sourceSpecificStandardizeURL(url);
|
||||||
source = s;
|
source = s;
|
||||||
break;
|
break;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -459,12 +514,14 @@ class SourceProvider {
|
|||||||
|
|
||||||
Future<App> getApp(
|
Future<App> getApp(
|
||||||
AppSource source, String url, Map<String, dynamic> additionalSettings,
|
AppSource source, String url, Map<String, dynamic> additionalSettings,
|
||||||
{App? currentApp, bool trackOnlyOverride = false}) async {
|
{App? currentApp,
|
||||||
|
bool trackOnlyOverride = false,
|
||||||
|
String? overrideSource}) async {
|
||||||
if (trackOnlyOverride || source.enforceTrackOnly) {
|
if (trackOnlyOverride || source.enforceTrackOnly) {
|
||||||
additionalSettings['trackOnly'] = true;
|
additionalSettings['trackOnly'] = true;
|
||||||
}
|
}
|
||||||
var trackOnly = additionalSettings['trackOnly'] == true;
|
var trackOnly = additionalSettings['trackOnly'] == true;
|
||||||
String standardUrl = source.standardizeURL(preStandardizeUrl(url));
|
String standardUrl = source.standardizeUrl(url);
|
||||||
APKDetails apk =
|
APKDetails apk =
|
||||||
await source.getLatestAPKDetails(standardUrl, additionalSettings);
|
await source.getLatestAPKDetails(standardUrl, additionalSettings);
|
||||||
if (additionalSettings['versionDetection'] == 'releaseDateAsVersion' &&
|
if (additionalSettings['versionDetection'] == 'releaseDateAsVersion' &&
|
||||||
@@ -514,7 +571,8 @@ class SourceProvider {
|
|||||||
currentApp?.pinned ?? false,
|
currentApp?.pinned ?? false,
|
||||||
categories: currentApp?.categories ?? const [],
|
categories: currentApp?.categories ?? const [],
|
||||||
releaseDate: apk.releaseDate,
|
releaseDate: apk.releaseDate,
|
||||||
changeLog: apk.changeLog);
|
changeLog: apk.changeLog,
|
||||||
|
overrideSource: overrideSource ?? currentApp?.overrideSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns errors in [results, errors] instead of throwing them
|
// Returns errors in [results, errors] instead of throwing them
|
||||||
|
@@ -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.36+158 # When changing this, update the tag in main() accordingly
|
version: 0.12.0+159 # 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'
|
||||||
|
Reference in New Issue
Block a user