mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-27 19:39:42 +02:00
Compare commits
8 Commits
v0.8.13-be
...
v0.8.14-be
Author | SHA1 | Date | |
---|---|---|---|
|
d435481f0b | ||
|
a68d49c71c | ||
|
2b6a16637e | ||
|
e46e4e5dbc | ||
|
848c8eaf5e | ||
|
ebc48169a1 | ||
|
54c37641d5 | ||
|
05ad01bf85 |
@@ -16,6 +16,7 @@ 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 (URLs ending with `/fdroid/repo`)
|
- Third Party F-Droid Repos (URLs ending with `/fdroid/repo`)
|
||||||
|
- [Steam](https://store.steampowered.com/mobile)
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
- App installs happen asynchronously and the success/failure of an install cannot be determined directly. This results in install statuses and versions sometimes being out of sync with the OS until the next launch or until the problem is manually corrected.
|
- App installs happen asynchronously and the success/failure of an install cannot be determined directly. This results in install statuses and versions sometimes being out of sync with the OS until the next launch or until the problem is manually corrected.
|
||||||
|
@@ -185,6 +185,9 @@
|
|||||||
"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",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
||||||
|
"steam": "Steam",
|
||||||
|
"steamMobile": "Steam Mobile",
|
||||||
|
"steamChat": "Steam Chat",
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Too many requests (rate limited) - try again in {} minute",
|
"one": "Too many requests (rate limited) - try again in {} minute",
|
||||||
"other": "Too many requests (rate limited) - try again in {} minutes"
|
"other": "Too many requests (rate limited) - try again in {} minutes"
|
||||||
|
@@ -52,7 +52,7 @@
|
|||||||
"additionalOptsFor": "Opzioni aggiuntive per {}",
|
"additionalOptsFor": "Opzioni aggiuntive per {}",
|
||||||
"supportedSourcesBelow": "Fonti supportate:",
|
"supportedSourcesBelow": "Fonti supportate:",
|
||||||
"trackOnlyInBrackets": "(Solo-Monitoraggio)",
|
"trackOnlyInBrackets": "(Solo-Monitoraggio)",
|
||||||
"searchableInBrackets": "(Ricercabile)",
|
"searchableInBrackets": "(ricercabile)",
|
||||||
"appsString": "App",
|
"appsString": "App",
|
||||||
"noApps": "Nessuna App",
|
"noApps": "Nessuna App",
|
||||||
"noAppsForFilter": "Nessuna App per i filtri selezionati",
|
"noAppsForFilter": "Nessuna App per i filtri selezionati",
|
||||||
@@ -181,10 +181,13 @@
|
|||||||
"removeAppQuestion": "Rimuovere App?",
|
"removeAppQuestion": "Rimuovere App?",
|
||||||
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
"yesMarkUpdated": "Sì, contrassegna come aggiornato",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "App ID or Name",
|
"appIdOrName": "ID o nome dell'App",
|
||||||
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
"appWithIdOrNameNotFound": "Non è stata trovata alcuna App con quell'ID o nome",
|
||||||
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
"reposHaveMultipleApps": "I repository possono contenere più App",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "Repository di terze parti di F-Droid",
|
||||||
|
"steam": "Steam",
|
||||||
|
"steamMobile": "Steam Mobile",
|
||||||
|
"steamChat": "Steam Chat",
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "Troppe richieste (traffico limitato) - riprova tra {} minuto",
|
"one": "Troppe richieste (traffico limitato) - riprova tra {} minuto",
|
||||||
"other": "Troppe richieste (traffico limitato) - riprova tra {} minuti"
|
"other": "Troppe richieste (traffico limitato) - riprova tra {} minuti"
|
||||||
|
@@ -39,10 +39,10 @@
|
|||||||
"app": "应用程序",
|
"app": "应用程序",
|
||||||
"appsFromSourceAreTrackOnly": "来自此来源的应用为仅追踪",
|
"appsFromSourceAreTrackOnly": "来自此来源的应用为仅追踪",
|
||||||
"youPickedTrackOnly": "你已选择仅追踪选项",
|
"youPickedTrackOnly": "你已选择仅追踪选项",
|
||||||
"trackOnlyAppDescription": "The App will be tracked for updates, but Obtainium will not be able to download or install it.",
|
"trackOnlyAppDescription": "该应用程序将被跟踪更新,但 Obtainium 无法下载或安装它",
|
||||||
"cancelled": "已取消",
|
"cancelled": "已取消",
|
||||||
"appAlreadyAdded": "此应用程序已被添加",
|
"appAlreadyAdded": "此应用程序已被添加",
|
||||||
"alreadyUpToDateQuestion": "App Already up to Date?",
|
"alreadyUpToDateQuestion": "应用已是最新?",
|
||||||
"addApp": "添加应用",
|
"addApp": "添加应用",
|
||||||
"appSourceURL": "应用来源 URL",
|
"appSourceURL": "应用来源 URL",
|
||||||
"error": "错误",
|
"error": "错误",
|
||||||
@@ -123,21 +123,21 @@
|
|||||||
"followSystem": "跟随系统",
|
"followSystem": "跟随系统",
|
||||||
"obtainium": "Obtainium",
|
"obtainium": "Obtainium",
|
||||||
"materialYou": "Material You",
|
"materialYou": "Material You",
|
||||||
"appSortBy": "应用排列方式",
|
"appSortBy": "排列方式",
|
||||||
"authorName": "作者/名字",
|
"authorName": "作者 / 名字",
|
||||||
"nameAuthor": "名字/作者",
|
"nameAuthor": "名字 / 作者",
|
||||||
"asAdded": "以添加顺序",
|
"asAdded": "添加顺序",
|
||||||
"appSortOrder": "以排列顺序",
|
"appSortOrder": "排列顺序",
|
||||||
"ascending": "升序",
|
"ascending": "升序",
|
||||||
"descending": "降序",
|
"descending": "降序",
|
||||||
"bgUpdateCheckInterval": "后台更新检查间隔",
|
"bgUpdateCheckInterval": "后台更新检查间隔",
|
||||||
"neverManualOnly": "从不 - 仅手动",
|
"neverManualOnly": "手动",
|
||||||
"appearance": "外观",
|
"appearance": "外观",
|
||||||
"showWebInAppView": "在应用来源页显示网页",
|
"showWebInAppView": "在应用来源页显示网页",
|
||||||
"pinUpdates": "将需要更新的应用固定到顶部",
|
"pinUpdates": "将需要更新的应用固定到顶部",
|
||||||
"updates": "已更新",
|
"updates": "检查间隔",
|
||||||
"sourceSpecific": "指定源",
|
"sourceSpecific": "Github 访问令牌",
|
||||||
"appSource": "应用源",
|
"appSource": "源代码",
|
||||||
"noLogs": "无日志",
|
"noLogs": "无日志",
|
||||||
"appLogs": "应用日志",
|
"appLogs": "应用日志",
|
||||||
"close": "关闭",
|
"close": "关闭",
|
||||||
@@ -170,21 +170,24 @@
|
|||||||
"pleaseAllowInstallPerm": "请允许 Obtainium 安装应用程序",
|
"pleaseAllowInstallPerm": "请允许 Obtainium 安装应用程序",
|
||||||
"trackOnly": "仅追踪",
|
"trackOnly": "仅追踪",
|
||||||
"errorWithHttpStatusCode": "错误 {}",
|
"errorWithHttpStatusCode": "错误 {}",
|
||||||
"versionCorrectionDisabled": "Version correction disabled (plugin doesn't seem to work)",
|
"versionCorrectionDisabled": "禁用版本更正(插件似乎未起作用)",
|
||||||
"unknown": "Unknown",
|
"unknown": "未知",
|
||||||
"none": "None",
|
"none": "无",
|
||||||
"never": "Never",
|
"never": "从不",
|
||||||
"latestVersionX": "Latest Version: {}",
|
"latestVersionX": "最新: {}",
|
||||||
"installedVersionX": "Installed Version: {}",
|
"installedVersionX": "已安装: {}",
|
||||||
"lastUpdateCheckX": "Last Update Check: {}",
|
"lastUpdateCheckX": "最后检查: {}",
|
||||||
"remove": "Remove",
|
"remove": "删除",
|
||||||
"removeAppQuestion": "Remove App?",
|
"removeAppQuestion": "删除应用?",
|
||||||
"yesMarkUpdated": "Yes, Mark as Updated",
|
"yesMarkUpdated": "'是的,标为已更新",
|
||||||
"fdroid": "F-Droid",
|
"fdroid": "F-Droid",
|
||||||
"appIdOrName": "App ID or Name",
|
"appIdOrName": "应用 ID 或名称",
|
||||||
"appWithIdOrNameNotFound": "No App was found with that ID or Name",
|
"appWithIdOrNameNotFound": "没有发现具有此 ID 或名称的应用",
|
||||||
"reposHaveMultipleApps": "Repos may contain multiple Apps",
|
"reposHaveMultipleApps": "来源可能包含多个应用",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "F-Droid 第三方源",
|
||||||
|
"steam": "Steam",
|
||||||
|
"steamMobile": "Steam Mobile",
|
||||||
|
"steamChat": "Steam Chat",
|
||||||
"tooManyRequestsTryAgainInMinutes": {
|
"tooManyRequestsTryAgainInMinutes": {
|
||||||
"one": "请求过多 (API 限制) - 在 {} 分钟后重试",
|
"one": "请求过多 (API 限制) - 在 {} 分钟后重试",
|
||||||
"other": "请求过多 (API 限制) - 在 {} 分钟后重试"
|
"other": "请求过多 (API 限制) - 在 {} 分钟后重试"
|
||||||
|
69
lib/app_sources/steammobile.dart
Normal file
69
lib/app_sources/steammobile.dart
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:html/parser.dart';
|
||||||
|
import 'package:http/http.dart';
|
||||||
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
|
import 'package:obtainium/custom_errors.dart';
|
||||||
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
|
|
||||||
|
class SteamMobile extends AppSource {
|
||||||
|
SteamMobile() {
|
||||||
|
host = 'store.steampowered.com';
|
||||||
|
name = tr('steam');
|
||||||
|
additionalSourceAppSpecificFormItems = [
|
||||||
|
[
|
||||||
|
GeneratedFormItem(
|
||||||
|
label: tr('app'),
|
||||||
|
key: 'app',
|
||||||
|
required: true,
|
||||||
|
opts: apks.entries.toList())
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
final apks = {'steam': tr('steamMobile'), 'steam-chat-app': tr('steamChat')};
|
||||||
|
|
||||||
|
@override
|
||||||
|
String standardizeURL(String url) {
|
||||||
|
return 'https://$host';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? changeLogPageFromStandardUrl(String standardUrl) => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<APKDetails> getLatestAPKDetails(
|
||||||
|
String standardUrl, List<String> additionalData,
|
||||||
|
{bool trackOnly = false}) async {
|
||||||
|
Response res = await get(Uri.parse('https://$host/mobile'));
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
var apkNamePrefix = findGeneratedFormValueByKey(
|
||||||
|
additionalSourceAppSpecificFormItems
|
||||||
|
.reduce((value, element) => [...value, ...element]),
|
||||||
|
additionalData,
|
||||||
|
'app');
|
||||||
|
if (apkNamePrefix == null) {
|
||||||
|
throw NoReleasesError();
|
||||||
|
}
|
||||||
|
String apkInURLRegexPattern = '/$apkNamePrefix-[^/]+\\.apk\$';
|
||||||
|
var links = parse(res.body)
|
||||||
|
.querySelectorAll('a')
|
||||||
|
.map((e) => e.attributes['href'] ?? '')
|
||||||
|
.where((e) => RegExp('https://.*$apkInURLRegexPattern').hasMatch(e))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (links.isEmpty) {
|
||||||
|
throw NoReleasesError();
|
||||||
|
}
|
||||||
|
var versionMatch = RegExp(apkInURLRegexPattern).firstMatch(links[0]);
|
||||||
|
if (versionMatch == null) {
|
||||||
|
throw NoVersionError();
|
||||||
|
}
|
||||||
|
var version = links[0].substring(
|
||||||
|
versionMatch.start + apkNamePrefix.length + 2, versionMatch.end - 4);
|
||||||
|
var apkUrls = [links[0]];
|
||||||
|
return APKDetails(version, apkUrls, AppNames(name, apks[apkNamePrefix]!));
|
||||||
|
} else {
|
||||||
|
throw NoReleasesError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,7 @@ class GeneratedFormItem {
|
|||||||
late String id;
|
late String id;
|
||||||
late List<Widget> belowWidgets;
|
late List<Widget> belowWidgets;
|
||||||
late String? hint;
|
late String? hint;
|
||||||
late List<String>? opts;
|
late List<MapEntry<String, String>>? opts;
|
||||||
|
|
||||||
GeneratedFormItem(
|
GeneratedFormItem(
|
||||||
{this.label = 'Input',
|
{this.label = 'Input',
|
||||||
@@ -86,7 +86,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
return j < widget.defaultValues.length
|
return j < widget.defaultValues.length
|
||||||
? widget.defaultValues[j++]
|
? widget.defaultValues[j++]
|
||||||
: e.opts != null
|
: e.opts != null
|
||||||
? e.opts!.first
|
? e.opts!.first.key
|
||||||
: '';
|
: '';
|
||||||
}).toList())
|
}).toList())
|
||||||
.toList();
|
.toList();
|
||||||
@@ -130,14 +130,15 @@ class _GeneratedFormState extends State<GeneratedForm> {
|
|||||||
return Text(tr('dropdownNoOptsError'));
|
return Text(tr('dropdownNoOptsError'));
|
||||||
}
|
}
|
||||||
return DropdownButtonFormField(
|
return DropdownButtonFormField(
|
||||||
decoration: InputDecoration(labelText: tr('colour')),
|
decoration: InputDecoration(labelText: e.value.label),
|
||||||
value: values[row.key][e.key],
|
value: values[row.key][e.key],
|
||||||
items: e.value.opts!
|
items: e.value.opts!
|
||||||
.map((e) => DropdownMenuItem(value: e, child: Text(e)))
|
.map((e) =>
|
||||||
|
DropdownMenuItem(value: e.key, child: Text(e.value)))
|
||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
values[row.key][e.key] = value ?? e.value.opts!.first;
|
values[row.key][e.key] = value ?? e.value.opts!.first.key;
|
||||||
someValueChanged();
|
someValueChanged();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -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.8.13';
|
const String currentVersion = '0.8.14';
|
||||||
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
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
|||||||
if (e is RateLimitError || e is SocketException) {
|
if (e is RateLimitError || e is SocketException) {
|
||||||
var remainingMinutes = e is RateLimitError ? e.remainingMinutes : 15;
|
var remainingMinutes = e is RateLimitError ? e.remainingMinutes : 15;
|
||||||
logs.add(plural('bgUpdateGotErrorRetryInMinutes', remainingMinutes,
|
logs.add(plural('bgUpdateGotErrorRetryInMinutes', remainingMinutes,
|
||||||
args: [(e as AppSource).name, remainingMinutes.toString()]));
|
args: [e.toString(), remainingMinutes.toString()]));
|
||||||
AndroidAlarmManager.oneShot(Duration(minutes: remainingMinutes),
|
AndroidAlarmManager.oneShot(Duration(minutes: remainingMinutes),
|
||||||
Random().nextInt(pow(2, 31) as int), bgUpdateCheck, params: {
|
Random().nextInt(pow(2, 31) as int), bgUpdateCheck, params: {
|
||||||
'ignoreAfterMicroseconds': nextIgnoreAfter.microsecondsSinceEpoch
|
'ignoreAfterMicroseconds': nextIgnoreAfter.microsecondsSinceEpoch
|
||||||
|
@@ -274,9 +274,14 @@ class AppsProvider with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
getHost(String url) {
|
||||||
|
var temp = Uri.parse(url).host.split('.');
|
||||||
|
return temp.sublist(temp.length - 2).join('.');
|
||||||
|
}
|
||||||
|
|
||||||
// If the picked APK comes from an origin different from the source, get user confirmation (if context provided)
|
// If the picked APK comes from an origin different from the source, get user confirmation (if context provided)
|
||||||
if (apkUrl != null &&
|
if (apkUrl != null &&
|
||||||
Uri.parse(apkUrl).origin != Uri.parse(app.url).origin &&
|
getHost(apkUrl) != getHost(app.url) &&
|
||||||
context != null) {
|
context != null) {
|
||||||
if (await showDialog(
|
if (await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@@ -8,13 +8,14 @@ import 'package:html/dom.dart';
|
|||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:obtainium/app_sources/apkmirror.dart';
|
import 'package:obtainium/app_sources/apkmirror.dart';
|
||||||
import 'package:obtainium/app_sources/fdroid.dart';
|
import 'package:obtainium/app_sources/fdroid.dart';
|
||||||
import 'package:obtainium/app_sources/fdroidRepo.dart';
|
import 'package:obtainium/app_sources/fdroidrepo.dart';
|
||||||
import 'package:obtainium/app_sources/github.dart';
|
import 'package:obtainium/app_sources/github.dart';
|
||||||
import 'package:obtainium/app_sources/gitlab.dart';
|
import 'package:obtainium/app_sources/gitlab.dart';
|
||||||
import 'package:obtainium/app_sources/izzyondroid.dart';
|
import 'package:obtainium/app_sources/izzyondroid.dart';
|
||||||
import 'package:obtainium/app_sources/mullvad.dart';
|
import 'package:obtainium/app_sources/mullvad.dart';
|
||||||
import 'package:obtainium/app_sources/signal.dart';
|
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/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';
|
||||||
@@ -212,7 +213,8 @@ class SourceProvider {
|
|||||||
Signal(),
|
Signal(),
|
||||||
SourceForge(),
|
SourceForge(),
|
||||||
APKMirror(),
|
APKMirror(),
|
||||||
FDroidRepo()
|
FDroidRepo(),
|
||||||
|
SteamMobile()
|
||||||
];
|
];
|
||||||
|
|
||||||
// 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
|
||||||
@@ -247,7 +249,7 @@ class SourceProvider {
|
|||||||
bool ifSourceAppsRequireAdditionalData(AppSource source) {
|
bool ifSourceAppsRequireAdditionalData(AppSource source) {
|
||||||
for (var row in source.additionalSourceAppSpecificFormItems) {
|
for (var row in source.additionalSourceAppSpecificFormItems) {
|
||||||
for (var element in row) {
|
for (var element in row) {
|
||||||
if (element.required) {
|
if (element.required && element.opts == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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.8.13+77 # When changing this, update the tag in main() accordingly
|
version: 0.8.14+78 # 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