Compare commits

...

7 Commits

Author SHA1 Message Date
6169915e63 Merge pull request #590 from ImranR98/dev
Infer GitHub App ID where possible (#588)
2023-05-27 21:02:20 -04:00
a0d466a074 Add toggle for App ID inferring where optional 2023-05-27 21:01:16 -04:00
6f9ef6d51e Merge remote-tracking branch 'origin/main' into dev 2023-05-27 20:38:39 -04:00
feb4c2eabc Increment version 2023-05-27 20:38:20 -04:00
c2cf39125d Merge pull request #589 from gidano/main
Update hu.json
2023-05-27 20:37:44 -04:00
833ece1ef5 Infer GitHub App ID where possible 2023-05-27 20:36:29 -04:00
fee23cadfa Update hu.json 2023-05-26 18:58:23 +02:00
18 changed files with 107 additions and 27 deletions

View File

@ -233,6 +233,7 @@
"about": "Über",
"requiresCredentialsInSettings": "Benötigt zusätzliche Anmeldedaten (in den Einstellungen)",
"checkOnStart": "Überprüfe einmalig beim Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "App entfernen?",
"other": "Apps entfernen?"

View File

@ -233,6 +233,7 @@
"about": "About",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Check Once on Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "Remove App?",
"other": "Remove Apps?"

View File

@ -233,6 +233,7 @@
"about": "About",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Check Once on Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "¿Eliminar Aplicación?",
"other": "¿Eliminar Aplicaciones?"

View File

@ -233,6 +233,7 @@
"about": "About",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Check Once on Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟"

View File

@ -233,6 +233,7 @@
"about": "About",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Check Once on Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "Supprimer l'application ?",
"other": "Supprimer les applications ?"

View File

@ -227,11 +227,12 @@
"dontShowAgain": "Ne mutassa ezt újra",
"dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
"dontShowAPKOriginWarnings": "Ne jelenítsen meg az APK eredetére vonatkozó figyelmeztetéseket",
"moveNonInstalledAppsToBottom": "Move Non-Installed Apps to Bottom of Apps View",
"gitlabPATLabel": "GitLab Personal Access Token (Enables Search)",
"about": "About",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Check Once on Start",
"moveNonInstalledAppsToBottom": "Helyezze át a nem telepített appokat az App nézet aljára",
"gitlabPATLabel": "GitLab Personal Access Token (Engedélyezi a Keresést)",
"about": "Rólunk",
"requiresCredentialsInSettings": "Ehhez további hitelesítő adatokra van szükség (a Beállításokban)",
"checkOnStart": "Egyszer az indításkor",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?"

View File

@ -233,6 +233,7 @@
"about": "About",
"requiresCredentialsInSettings": "This needs additional credentials (in Settings)",
"checkOnStart": "Check Once on Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "Rimuovere l'App?",
"other": "Rimuovere le App?"

View File

@ -233,6 +233,7 @@
"about": "概要",
"requiresCredentialsInSettings": "これには追加の認証が必要です (設定にて)",
"checkOnStart": "Check Once on Start",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"

View File

@ -233,6 +233,7 @@
"about": "相关文档",
"requiresCredentialsInSettings": "此功能需要额外的凭据(在“设置”中添加)",
"checkOnStart": "启动时进行一次检查",
"tryInferAppIdFromCode": "Try inferring App ID from source code",
"removeAppQuestion": {
"one": "是否删除应用?",
"other": "是否删除应用?"

View File

@ -19,8 +19,8 @@ class APKCombo extends AppSource {
}
@override
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return Uri.parse(standardUrl).pathSegments.last;
}
@ -83,7 +83,7 @@ class APKCombo extends AppSource {
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
String appId = tryInferringAppId(standardUrl)!;
String appId = (await tryInferringAppId(standardUrl))!;
var preres = await sourceRequest(standardUrl);
if (preres.statusCode != 200) {
throw getObtainiumHttpError(preres);

View File

@ -24,8 +24,8 @@ class APKPure extends AppSource {
}
@override
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return Uri.parse(standardUrl).pathSegments.last;
}
@ -34,7 +34,7 @@ class APKPure extends AppSource {
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
String appId = tryInferringAppId(standardUrl)!;
String appId = (await tryInferringAppId(standardUrl))!;
String host = Uri.parse(standardUrl).host;
var res = await sourceRequest('$standardUrl/download');
if (res.statusCode == 200) {

View File

@ -31,8 +31,8 @@ class FDroid extends AppSource {
}
@override
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return Uri.parse(standardUrl).pathSegments.last;
}
@ -63,7 +63,7 @@ class FDroid extends AppSource {
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
String? appId = tryInferringAppId(standardUrl);
String? appId = await tryInferringAppId(standardUrl);
String host = Uri.parse(standardUrl).host;
return getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest('https://$host/api/v1/packages/$appId'),

View File

@ -6,6 +6,7 @@ import 'package:obtainium/app_sources/html.dart';
import 'package:obtainium/components/generated_form.dart';
import 'package:obtainium/custom_errors.dart';
import 'package:obtainium/providers/apps_provider.dart';
import 'package:obtainium/providers/logs_provider.dart';
import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
@ -13,6 +14,7 @@ import 'package:url_launcher/url_launcher_string.dart';
class GitHub extends AppSource {
GitHub() {
host = 'github.com';
appIdInferIsOptional = true;
additionalSourceSpecificSettingFormItems = [
GeneratedFormTextField('github-creds',
@ -79,6 +81,44 @@ class GitHub extends AppSource {
canSearch = true;
}
@override
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
const possibleBuildGradleLocations = [
'/app/build.gradle',
'android/app/build.gradle',
'src/app/build.gradle'
];
for (var path in possibleBuildGradleLocations) {
try {
var res = await sourceRequest(
'${await convertStandardUrlToAPIUrl(standardUrl)}/contents/$path');
if (res.statusCode == 200) {
try {
var body = jsonDecode(res.body);
var appId = utf8
.decode(base64
.decode(body['content'].toString().split('\n').join('')))
.split('\n')
.map((e) => e.trim())
.where((l) => l.startsWith('applicationId "'))
.first
.split('"')[1];
if (appId.isNotEmpty) {
return appId;
}
} catch (err) {
LogsProvider().add(
'Error parsing build.gradle from ${res.request!.url.toString()}: ${err.toString()}');
}
}
} catch (err) {
// Ignore - ID will be extracted from the APK
}
}
return null;
}
@override
String sourceSpecificStandardizeURL(String url) {
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
@ -97,6 +137,12 @@ class GitHub extends AppSource {
return creds != null && creds.isNotEmpty ? '$creds@' : '';
}
Future<String> getAPIHost() async =>
'https://${await getCredentialPrefixIfAny()}api.$host';
Future<String> convertStandardUrlToAPIUrl(String standardUrl) async =>
'${await getAPIHost()}/repos${standardUrl.substring('https://$host'.length)}';
@override
String? changeLogPageFromStandardUrl(String standardUrl) =>
'$standardUrl/releases';
@ -239,7 +285,7 @@ class GitHub extends AppSource {
) async {
return await getLatestAPKDetailsCommon2(standardUrl, additionalSettings,
(bool useTagUrl) async {
return 'https://${await getCredentialPrefixIfAny()}api.$host/repos${standardUrl.substring('https://$host'.length)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
return '${await convertStandardUrlToAPIUrl(standardUrl)}/${useTagUrl ? 'tags' : 'releases'}?per_page=100';
}, (Response res) {
rateLimitErrorCheck(res);
});
@ -281,7 +327,7 @@ class GitHub extends AppSource {
Future<Map<String, List<String>>> search(String query) async {
return searchCommon(
query,
'https://${await getCredentialPrefixIfAny()}api.$host/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100',
'${await getAPIHost()}/search/repositories?q=${Uri.encodeQueryComponent(query)}&per_page=100',
'items', onHttpErrorCode: (Response res) {
rateLimitErrorCheck(res);
});

View File

@ -18,8 +18,8 @@ class IzzyOnDroid extends AppSource {
}
@override
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return FDroid().tryInferringAppId(standardUrl);
}
@ -28,7 +28,7 @@ class IzzyOnDroid extends AppSource {
String standardUrl,
Map<String, dynamic> additionalSettings,
) async {
String? appId = tryInferringAppId(standardUrl);
String? appId = await tryInferringAppId(standardUrl);
return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse(
await sourceRequest(
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),

View File

@ -21,7 +21,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.5';
const String currentVersion = '0.13.6';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES

View File

@ -33,6 +33,7 @@ class _AddAppPageState extends State<AddAppPage> {
AppSource? pickedSource;
Map<String, dynamic> additionalSettings = {};
bool additionalSettingsValid = true;
bool inferAppIdIfOptional = true;
List<String> pickedCategories = [];
int searchnum = 0;
SourceProvider sourceProvider = SourceProvider();
@ -78,6 +79,7 @@ class _AddAppPageState extends State<AddAppPage> {
additionalSettingsValid = source != null
? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
: true;
inferAppIdIfOptional = true;
}
});
}
@ -147,7 +149,8 @@ class _AddAppPageState extends State<AddAppPage> {
app = await sourceProvider.getApp(
pickedSource!, userInput, additionalSettings,
trackOnlyOverride: trackOnly,
overrideSource: pickedSourceOverride);
overrideSource: pickedSourceOverride,
inferAppIdIfOptional: inferAppIdIfOptional);
// Only download the APK here if you need to for the package ID
if (sourceProvider.isTempId(app) &&
app.additionalSettings['trackOnly'] != true) {
@ -428,6 +431,23 @@ class _AddAppPageState extends State<AddAppPage> {
}),
],
),
if (pickedSource != null && pickedSource!.appIdInferIsOptional)
GeneratedForm(
key: const Key('inferAppIdIfOptional'),
items: [
[
GeneratedFormSwitch('inferAppIdIfOptional',
label: tr('tryInferAppIdFromCode'),
defaultValue: inferAppIdIfOptional)
]
],
onValueChanges: (values, valid, isBuilding) {
if (!isBuilding) {
setState(() {
inferAppIdIfOptional = values['inferAppIdIfOptional'];
});
}
}),
],
);

View File

@ -317,6 +317,7 @@ abstract class AppSource {
late String name;
bool enforceTrackOnly = false;
bool changeLogIfAnyIsMarkDown = true;
bool appIdInferIsOptional = false;
AppSource() {
name = runtimeType.toString();
@ -434,8 +435,8 @@ abstract class AppSource {
throw NotImplementedError();
}
String? tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) {
Future<String?> tryInferringAppId(String standardUrl,
{Map<String, dynamic> additionalSettings = const {}}) async {
return null;
}
}
@ -552,7 +553,8 @@ class SourceProvider {
AppSource source, String url, Map<String, dynamic> additionalSettings,
{App? currentApp,
bool trackOnlyOverride = false,
String? overrideSource}) async {
String? overrideSource,
bool inferAppIdIfOptional = false}) async {
if (trackOnlyOverride || source.enforceTrackOnly) {
additionalSettings['trackOnly'] = true;
}
@ -592,8 +594,11 @@ class SourceProvider {
: apk.names.name[0].toUpperCase() + apk.names.name.substring(1);
return App(
currentApp?.id ??
source.tryInferringAppId(standardUrl,
additionalSettings: additionalSettings) ??
((!source.appIdInferIsOptional ||
(source.appIdInferIsOptional && inferAppIdIfOptional))
? await source.tryInferringAppId(standardUrl,
additionalSettings: additionalSettings)
: null) ??
generateTempID(standardUrl, additionalSettings),
standardUrl,
apk.names.author[0].toUpperCase() + apk.names.author.substring(1),

View File

@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# 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.5+169 # When changing this, update the tag in main() accordingly
version: 0.13.6+170 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=2.18.2 <3.0.0'