Compare commits

...

21 Commits

Author SHA1 Message Date
ce89d456e1 Merge pull request #788 from ImranR98/dev
Pub update
2023-08-19 01:44:36 -04:00
a0c48fcca6 Pub update 2023-08-19 01:44:02 -04:00
d0cba6d6bc Merge pull request #787 from ImranR98/dev
Enable Android TV 'OK' Button (#281), Add Huawei AppGallery (#756), Fix VLC Source (#758)
2023-08-19 01:41:31 -04:00
6baf6ccf4b Slightly better error reporting for failed xapk install 2023-08-19 01:40:14 -04:00
a2571e61a8 Fix VLC Source (#758) 2023-08-19 01:28:25 -04:00
f61824ff0d Add Huawei AppGallery (#756) 2023-08-19 00:19:52 -04:00
734a1aeb01 Enable Android TV 'OK' Button (#281) 2023-08-18 22:12:16 -04:00
5269aad90d Merge pull request #786 from ImranR98/dev
Don't Send "Foreground" Notification if Not Needed + Small UI Fix
2023-08-18 20:11:34 -04:00
eaeee188eb Upgrade packages, increment version 2023-08-18 19:58:54 -04:00
8c850e06ca Merge pull request #774 from iDazai/main
Update de.json
2023-08-18 19:56:59 -04:00
0f754a8da8 Merge pull request #775 from bluefly000/japanese-translation
Update ja.json
2023-08-18 19:56:31 -04:00
81c4d4f393 Fix default multi-"app change" selection bug 2023-08-18 19:55:31 -04:00
5317aee18d Merge pull request #776 from Daviteusz/weblate-obtainium-translate
locale(pl): Update Polish translation
2023-08-18 19:54:44 -04:00
b66eeba3b5 Merge pull request #781 from mehdeej/main
Update fa.json
2023-08-18 19:54:22 -04:00
522ff1ddf7 Merge pull request #784 from Nriver/task/update-chinese-translation
update chinese translation
2023-08-18 19:54:06 -04:00
7ef9c43ee3 Don't wait for foreground if install is silent 2023-08-18 19:36:56 -04:00
05ac76e3e9 update chinese translation 2023-08-18 16:56:20 +08:00
4838402797 Update fa.json 2023-08-16 18:54:58 +00:00
0de12c7c07 locale(pl): Update Polish translations
Polish translation has been updated

Signed-off-by: Daviteusz <daviteusz0@gmail.com>
2023-08-14 23:24:16 +02:00
0dadd8bffe Update ja.json 2023-08-15 02:57:31 +09:00
75a0cb1189 Update de.json
translated newly added line
2023-08-14 14:38:38 +02:00
15 changed files with 237 additions and 98 deletions

View File

@ -18,6 +18,7 @@ Currently supported App sources:
- [SourceHut](https://git.sr.ht/)
- [APKMirror](https://apkmirror.com/) (Track-Only)
- [APKPure](https://apkpure.com/)
- [Huawei AppGallery](https://appgallery.huawei.com/)
- Third Party F-Droid Repos
- Jenkins Jobs
- [Steam](https://store.steampowered.com/mobile)

View File

@ -49,7 +49,6 @@ android {
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "dev.imranr.obtainium"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.

View File

@ -246,7 +246,7 @@
"gitlabSourceNote": "GitLab APK-Extraktion funktioniert möglicherweise nicht ohne API-Schlüssel",
"sortByFileNamesNotLinks": "Sortiere nach Dateinamen, anstelle von ganzen Links",
"filterReleaseNotesByRegEx": "Versionshinweise nach regulärem Ausdruck filtern",
"customLinkFilterRegex": "Custom Link Filter by Regular Expression (Default '.apk$')",
"customLinkFilterRegex": "Benutzerdefinierter Link Filter nach Regulärem Ausdruck (Standard '.apk$')",
"removeAppQuestion": {
"one": "App entfernen?",
"other": "Apps entfernen?"

View File

@ -229,24 +229,24 @@
"dontShowTrackOnlyWarnings": "هشدار 'فقط ردیابی' را نشان ندهید",
"dontShowAPKOriginWarnings": "هشدارهای منبع APK را نشان ندهید",
"moveNonInstalledAppsToBottom": "برنامه های نصب نشده را به نمای پایین برنامه ها منتقل کنید",
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو را فعال می کند and Better APK Discovery)",
"gitlabPATLabel": "رمز دسترسی شخصی GitLab\n(جستجو و کشف بهتر APK را فعال میکند)",
"about": "درباره",
"requiresCredentialsInSettings": "این به اعتبارنامه های اضافی نیاز دارد (در تنظیمات)",
"checkOnStart": "بررسی در شروع",
"tryInferAppIdFromCode": "شناسه برنامه را از کد منبع استنباط کنید",
"removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
"pickHighestVersionCode": "Auto-select highest version code APK",
"checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
"disablePageTransitions": "Disable page transition animations",
"reversePageTransitions": "Reverse page transition animations",
"minStarCount": "Minimum Star Count",
"addInfoBelow": "Add this info below.",
"addInfoInSettings": "Add this info in the Settings.",
"githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
"gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom Link Filter by Regular Expression (Default '.apk$')",
"removeOnExternalUninstall": "حذف خودکار برنامه های حذف نصب شده خارجی",
"pickHighestVersionCode": "انتخاب خودکار بالاترین کد نسخه APK",
"checkUpdateOnDetailPage": "برای باز کردن صفحه جزئیات برنامه، به‌روزرسانی‌ها را بررسی کنید",
"disablePageTransitions": "غیرفعال کردن انیمیشن های انتقال صفحه",
"reversePageTransitions": "انیمیشن های انتقال معکوس صفحه",
"minStarCount": "حداقل تعداد ستاره",
"addInfoBelow": "این اطلاعات را در زیر اضافه کنید",
"addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.",
"githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.",
"gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.",
"sortByFileNamesNotLinks": "مرتب سازی بر اساس نام فایل به جای پیوندهای کامل",
"filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
"customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیش‌فرض '.apk$')",
"removeAppQuestion": {
"one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟"

View File

@ -22,7 +22,7 @@
"githubPATFormat": "ユーザー名:トークン",
"includePrereleases": "プレリリースを含む",
"fallbackToOlderReleases": "旧リリースへのフォールバック",
"filterReleaseTitlesByRegEx": "正規表現でリリースタイトルを絞り込む",
"filterReleaseTitlesByRegEx": "正規表現でリリースタイトルをフィルタリングする",
"invalidRegEx": "無効な正規表現",
"noDescription": "説明はありません",
"cancel": "キャンセル",
@ -88,7 +88,7 @@
"showOutdatedOnly": "アップデートが存在するアプリのみ表示する",
"filter": "フィルター",
"filterActive": "フィルター *",
"filterApps": "アプリを絞り込む",
"filterApps": "アプリをフィルタリングする",
"appName": "アプリ名",
"author": "作者",
"upToDateApps": "最新のアプリ",
@ -211,7 +211,7 @@
"copiedToClipboard": "クリップボードにコピーしました",
"storagePermissionDenied": "ストレージ権限が拒否されました",
"selectedCategorizeWarning": "これにより、選択したアプリの既存のカテゴリ設定がすべて置き換えられます。",
"filterAPKsByRegEx": "正規表現でAPKを絞り込む",
"filterAPKsByRegEx": "正規表現でAPKをフィルタリングする",
"removeFromObtainium": "Obtainiumから削除する",
"uninstallFromDevice": "デバイスからアンインストールする",
"onlyWorksWithNonVersionDetectApps": "バージョン検出を無効にしているアプリにのみ動作します。",
@ -244,9 +244,9 @@
"addInfoInSettings": "設定でこの情報を追加してください。",
"githubSourceNote": "GitHubのレート制限はAPIキーを使うことで回避できます。",
"gitlabSourceNote": "GitLabのAPK抽出はAPIキーがないと動作しない場合があります。",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom Link Filter by Regular Expression (Default '.apk$')",
"sortByFileNamesNotLinks": "フルのリンクではなくファイル名でソートする",
"filterReleaseNotesByRegEx": "正規表現でリリースノートをフィルタリングする",
"customLinkFilterRegex": "正規表現によるカスタムリンクフィルター (デフォルト '.apk$')",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"

View File

@ -250,7 +250,7 @@
"gitlabSourceNote": "Pozyskiwanie pliku APK z GitLab może nie działać bez klucza API.",
"sortByFileNamesNotLinks": "Sortuj wg nazw plików zamiast pełnych linków",
"filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
"customLinkFilterRegex": "Custom Link Filter by Regular Expression (Default '.apk$')",
"customLinkFilterRegex": "Niestandardowy filtr linków wg. wyrażenia regularnego (domyślnie \".apk$\")",
"removeAppQuestion": {
"one": "Usunąć aplikację?",
"other": "Usunąć aplikacje?"

View File

@ -244,9 +244,9 @@
"addInfoInSettings": "在“设置”中添加此凭据。",
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
"sortByFileNamesNotLinks": "Sort by file names instead of full links",
"filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
"customLinkFilterRegex": "Custom Link Filter by Regular Expression (Default '.apk$')",
"sortByFileNamesNotLinks": "根据文件名而不是完整链接来排序",
"filterReleaseNotesByRegEx": "用正则表达式筛选发布说明",
"customLinkFilterRegex": "用正则表达式自定义链接筛选(默认 '.apk$'",
"removeAppQuestion": {
"one": "是否删除应用?",
"other": "是否删除应用?"

View File

@ -0,0 +1,90 @@
import 'package:easy_localization/easy_localization.dart';
import 'package:http/http.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/source_provider.dart';
class HuaweiAppGallery extends AppSource {
HuaweiAppGallery() {
name = 'Huawei AppGallery';
host = 'appgallery.huawei.com';
overrideVersionDetectionFormDefault('releaseDateAsVersion', true);
}
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://$host/app/[^/]+');
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
if (match == null) {
throw InvalidURLError(name);
}
return url.substring(0, match.end);
}
getDlUrl(String standardUrl) =>
'https://${host!.replaceAll('appgallery.', 'appgallery.cloud.')}/appdl/${standardUrl.split('/').last}';
requestAppdlRedirect(String dlUrl) async {
Response res = await sourceRequest(dlUrl, followRedirects: false);
if (res.statusCode == 200 ||
res.statusCode == 302 ||
res.statusCode == 304) {
return res;
} else {
throw getObtainiumHttpError(res);
}
}
appIdFromRedirectDlUrl(String redirectDlUrl) {
var parts = redirectDlUrl
.split('?')[0]
.split('/')
.last
.split('.')
.reversed
.toList();
parts.removeAt(0);
parts.removeAt(0);
return parts.reversed.join('.');
}
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
String dlUrl = getDlUrl(standardUrl);
Response res = await requestAppdlRedirect(dlUrl);
return res.headers['location'] != null
? appIdFromRedirectDlUrl(res.headers['location']!)
: null;
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
String dlUrl = getDlUrl(standardUrl);
Response res = await requestAppdlRedirect(dlUrl);
if (res.headers['location'] == null) {
throw NoReleasesError();
}
String appId = appIdFromRedirectDlUrl(res.headers['location']!);
var relDateStr =
res.headers['location']?.split('?')[0].split('.').reversed.toList()[1];
var relDateStrAdj = relDateStr?.split('');
var tempLen = relDateStrAdj?.length ?? 0;
var i = 2;
while (i < tempLen) {
relDateStrAdj?.insert((i + i ~/ 2 - 1), '-');
i += 2;
}
var relDate = relDateStrAdj == null
? null
: DateFormat('yy-MM-dd-HH-mm').parse(relDateStrAdj.join(''));
if (relDateStr == null) {
throw NoVersionError();
}
return APKDetails(
relDateStr, [MapEntry('$appId.apk', dlUrl)], AppNames(name, appId),
releaseDate: relDate);
}
}

View File

@ -1,5 +1,6 @@
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';
@ -7,55 +8,60 @@ class VLC extends AppSource {
VLC() {
host = 'videolan.org';
}
get dwUrlBase => 'https://get.$host/vlc-android/';
@override
Map<String, String>? get requestHeaders => HTML().requestHeaders;
@override
String sourceSpecificStandardizeURL(String url) {
return 'https://$host';
}
Future<String?> getLatestVersion(String standardUrl) async {
Response res = await sourceRequest(dwUrlBase);
if (res.statusCode == 200) {
var dwLinks = parse(res.body)
.querySelectorAll('a')
.where((element) => element.attributes['href'] != 'last/')
.map((e) => e.attributes['href']?.split('/')[0])
.toList();
String? version = dwLinks.isNotEmpty ? dwLinks.last : null;
if (version == null) {
throw NoVersionError();
}
return version;
} else {
throw getObtainiumHttpError(res);
}
}
@override
Future<APKDetails> getLatestAPKDetails(
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
Response res = await sourceRequest(
'https://www.videolan.org/vlc/download-android.html');
String? version = await getLatestVersion(standardUrl);
if (version == null) {
throw NoVersionError();
}
String? targetUrl = '$dwUrlBase$version/';
Response res = await sourceRequest(targetUrl);
List<String> apkUrls = [];
if (res.statusCode == 200) {
var dwUrlBase = 'get.videolan.org/vlc-android';
var dwLinks = parse(res.body)
apkUrls = parse(res.body)
.querySelectorAll('a')
.where((element) =>
element.attributes['href']?.contains(dwUrlBase) ?? false)
.map((e) => e.attributes['href']?.split('/').last)
.where((h) =>
h != null && h.isNotEmpty && h.toLowerCase().endsWith('.apk'))
.map((e) => targetUrl + e!)
.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 sourceRequest(targetUrl);
List<String> apkUrls = [];
if (res2.statusCode == 200) {
apkUrls = parse(res2.body)
.querySelectorAll('a')
.map((e) => e.attributes['href']?.split('/').last)
.where((h) =>
h != null && h.isNotEmpty && h.toLowerCase().endsWith('.apk'))
.map((e) => targetUrl + e!)
.toList();
} else {
throw getObtainiumHttpError(res2);
}
return APKDetails(
version, getApkUrlsFromUrls(apkUrls), AppNames('VideoLAN', 'VLC'));
} else {
throw getObtainiumHttpError(res);
}
return APKDetails(
version, getApkUrlsFromUrls(apkUrls), AppNames('VideoLAN', 'VLC'));
}
@override

View File

@ -22,7 +22,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.13.24';
const String currentVersion = '0.13.26';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
@ -299,7 +299,9 @@ class _ObtainiumState extends State<Obtainium> {
? lightColorScheme
: darkColorScheme,
fontFamily: 'Metropolis'),
home: const HomePage());
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
}, child: const HomePage()));
});
}
}

View File

@ -643,15 +643,15 @@ class AppsPageState extends State<AppsPage> {
label: tr('installX', args: [
plural('apps', newInstallIdsAllOrSelected.length)
]),
defaultValue: existingUpdateIdsAllOrSelected.isNotEmpty));
defaultValue: existingUpdateIdsAllOrSelected.isEmpty));
}
if (trackOnlyUpdateIdsAllOrSelected.isNotEmpty) {
formItems.add(GeneratedFormSwitch('trackonlies',
label: tr('markXTrackOnlyAsUpdated', args: [
plural('apps', trackOnlyUpdateIdsAllOrSelected.length)
]),
defaultValue: existingUpdateIdsAllOrSelected.isNotEmpty ||
newInstallIdsAllOrSelected.isNotEmpty));
defaultValue: existingUpdateIdsAllOrSelected.isEmpty &&
newInstallIdsAllOrSelected.isEmpty));
}
showDialog<Map<String, dynamic>?>(
context: context,

View File

@ -7,6 +7,7 @@ import 'dart:io';
import 'package:android_intent_plus/flag.dart';
import 'package:android_package_installer/android_package_installer.dart';
import 'package:android_package_manager/android_package_manager.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@ -29,6 +30,8 @@ import 'package:http/http.dart';
import 'package:android_intent_plus/android_intent.dart';
import 'package:flutter_archive/flutter_archive.dart';
final pm = AndroidPackageManager();
class AppInMemory {
late App app;
double? downloadProgress;
@ -322,16 +325,39 @@ class AppsProvider with ChangeNotifier {
.isNotEmpty;
Future<bool> canInstallSilently(App app) async {
return false;
// TODO: Uncomment the below if silent updates are ever figured out
// // NOTE: This is unreliable - try to get from OS in the future
// if (app.apkUrls.length > 1) {
// return false;
// }
// var osInfo = await DeviceInfoPlugin().androidInfo;
// return app.installedVersion != null &&
// osInfo.version.sdkInt >= 30 &&
// osInfo.version.release.compareTo('12') >= 0;
if (app.apkUrls.length > 1) {
// Manual API selection means silent install is not possible
return false;
}
var osInfo = await DeviceInfoPlugin().androidInfo;
String? installerPackageName;
try {
installerPackageName = osInfo.version.sdkInt >= 30
? (await pm.getInstallSourceInfo(packageName: app.id))
?.installingPackageName
: (await pm.getInstallerPackageName(packageName: app.id));
} catch (e) {
// Probably not installed - ignore
}
if (installerPackageName != obtainiumId) {
// If we did not install the app (or it isn't installed), silent install is not possible
return false;
}
int? targetSDK;
try {
targetSDK = (await pm.getPackageInfo(packageName: app.id))
?.applicationInfo
?.targetSdkVersion;
} catch (e) {
// Weird if you get here - ignore
}
// The OS must also be new enough and the APK should target a new enough API
return osInfo.version.sdkInt >= 30 &&
targetSDK != null &&
targetSDK >= // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int)
(osInfo.version.sdkInt - 3);
}
Future<void> waitForUserToReturnToForeground(BuildContext context) async {
@ -359,26 +385,24 @@ class AppsProvider with ChangeNotifier {
zipFile: File(filePath), destinationDir: Directory(destinationPath));
}
Future<void> installXApkDir(DownloadedXApkDir dir,
{bool silent = false}) async {
Future<void> installXApkDir(DownloadedXApkDir dir) async {
// We don't know which APKs in an XAPK are supported by the user's device
// So we try installing all of them and assume success if at least one installed
// If 0 APKs installed, throw the first install error encountered
try {
var somethingInstalled = false;
Object? firstError;
MultiAppMultiError errors = MultiAppMultiError();
for (var file in dir.extracted
.listSync(recursive: true, followLinks: false)
.whereType<File>()) {
if (file.path.toLowerCase().endsWith('.apk')) {
try {
somethingInstalled = somethingInstalled ||
await installApk(DownloadedApk(dir.appId, file),
silent: silent);
await installApk(DownloadedApk(dir.appId, file));
} catch (e) {
logs.add(
'Could not install APK from XAPK \'${file.path}\': ${e.toString()}');
firstError ??= e;
errors.add(dir.appId, e.toString());
}
} else if (file.path.toLowerCase().endsWith('.obb')) {
await moveObbFile(file, dir.appId);
@ -386,16 +410,15 @@ class AppsProvider with ChangeNotifier {
}
if (somethingInstalled) {
dir.file.delete(recursive: true);
} else if (firstError != null) {
throw firstError;
} else if (errors.content.isNotEmpty) {
throw errors;
}
} finally {
dir.extracted.delete(recursive: true);
}
}
Future<bool> installApk(DownloadedApk file, {bool silent = false}) async {
// TODO: Use 'silent' when/if ever possible
Future<bool> installApk(DownloadedApk file) async {
var newInfo = await PackageArchiveInfo.fromPath(file.file.path);
AppInfo? appInfo;
try {
@ -571,7 +594,6 @@ class AppsProvider with ChangeNotifier {
}
bool willBeSilent = await canInstallSilently(
apps[downloadedFile?.appId ?? downloadedDir!.appId]!.app);
willBeSilent = false; // TODO: Remove this when silent updates work
if (!(await settingsProvider?.getInstallPermission(enforce: false) ??
true)) {
throw ObtainiumError(tr('cancelled'));
@ -584,9 +606,9 @@ class AppsProvider with ChangeNotifier {
notifyListeners();
try {
if (downloadedFile != null) {
await installApk(downloadedFile, silent: willBeSilent);
await installApk(downloadedFile);
} else {
await installXApkDir(downloadedDir!, silent: willBeSilent);
await installXApkDir(downloadedDir!);
}
} finally {
apps[id]?.downloadProgress = null;

View File

@ -14,6 +14,7 @@ import 'package:obtainium/app_sources/fdroid.dart';
import 'package:obtainium/app_sources/fdroidrepo.dart';
import 'package:obtainium/app_sources/github.dart';
import 'package:obtainium/app_sources/gitlab.dart';
import 'package:obtainium/app_sources/huaweiappgallery.dart';
import 'package:obtainium/app_sources/izzyondroid.dart';
import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/app_sources/jenkins.dart';
@ -355,10 +356,14 @@ abstract class AppSource {
Map<String, String>? get requestHeaders => null;
Future<Response> sourceRequest(String url) async {
if (requestHeaders != null) {
Future<Response> sourceRequest(String url,
{bool followRedirects = true}) async {
if (requestHeaders != null || followRedirects == false) {
var req = Request('GET', Uri.parse(url));
req.headers.addAll(requestHeaders!);
req.followRedirects = followRedirects;
if (requestHeaders != null) {
req.headers.addAll(requestHeaders!);
}
return Response.fromStream(await Client().send(req));
} else {
return get(Uri.parse(url));
@ -508,6 +513,7 @@ class SourceProvider {
SourceHut(),
APKMirror(),
APKPure(),
HuaweiAppGallery(),
// APKCombo(), // Can't get past their scraping blocking yet (get 403 Forbidden)
Mullvad(),
Signal(),

View File

@ -22,10 +22,19 @@ packages:
description:
path: "."
ref: main
resolved-ref: "2edf5dbbfeeb33257d526861f2a992aee5d97bb4"
resolved-ref: ba2aa7a11edc2649d1d80c25ed9291521262f714
url: "https://github.com/ImranR98/android_package_installer"
source: git
version: "0.0.1"
android_package_manager:
dependency: "direct main"
description:
path: "."
ref: master
resolved-ref: "6e68991ef9c6232695abce2eef345d3cca2f52ac"
url: "https://github.com/ImranR98/android_package_manager"
source: git
version: "0.5.4"
animations:
dependency: "direct main"
description:
@ -578,10 +587,10 @@ packages:
dependency: transitive
description:
name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
sha256: "57c07bf82207aee366dfaa3867b3164e4f03a238a461a11b0e8a3a510d51203d"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
version: "3.1.1"
plugin_platform_interface:
dependency: transitive
description:
@ -871,10 +880,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: "564ef378cafc1a0e29f1d76ce175ef517a0a6115875dff7b43fccbef2b0aeb30"
sha256: "0ca3cfcc6781a7de701d580917af4a9efc4e3e129f8ead95a80587f0a749480a"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.5.0"
webview_flutter_wkwebview:
dependency: transitive
description:

View File

@ -1,5 +1,5 @@
name: obtainium
description: A new Flutter project.
description: Get Android App Updates Directly From the Source.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 0.13.24+188 # When changing this, update the tag in main() accordingly
version: 0.13.26+190 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=2.18.2 <3.0.0'
@ -55,9 +55,13 @@ dependencies:
git:
url: https://github.com/ImranR98/android_package_installer
ref: main
android_package_manager:
git:
url: https://github.com/ImranR98/android_package_manager
ref: master
share_plus: ^7.0.0
installed_apps: ^1.3.1
package_archive_info: ^0.1.0
installed_apps: ^1.3.1 # TODO: Remove when android_package_manager supports getting icon as UInt8List and versionCode
package_archive_info: ^0.1.0 # TODO: Remove when android_package_manager supports getting versionCode
android_alarm_manager_plus: ^3.0.0
sqflite: ^2.2.0+3
easy_localization: ^3.0.1