mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-04 07:13:28 +01:00 
			
		
		
		
	Compare commits
	
		
			7 Commits
		
	
	
		
			v0.7.3-bet
			...
			v0.7.5-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					602f0c3bb2 | ||
| 
						 | 
					00721e8ac4 | ||
| 
						 | 
					d19f9101d6 | ||
| 
						 | 
					a4bc278e4c | ||
| 
						 | 
					b04986622b | ||
| 
						 | 
					2059e4fd44 | ||
| 
						 | 
					618a1523cf | 
@@ -1,4 +1,5 @@
 | 
			
		||||
import 'package:html/parser.dart';
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
 | 
			
		||||
import 'package:http/http.dart';
 | 
			
		||||
import 'package:obtainium/custom_errors.dart';
 | 
			
		||||
import 'package:obtainium/providers/source_provider.dart';
 | 
			
		||||
@@ -28,41 +29,24 @@ class FDroid extends AppSource {
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) => null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
  String? tryInferringAppId(String standardUrl) {
 | 
			
		||||
    return Uri.parse(standardUrl).pathSegments.last;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
    Response res = await get(Uri.parse(standardUrl));
 | 
			
		||||
  APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
			
		||||
      Response res, String apkUrlPrefix) {
 | 
			
		||||
    if (res.statusCode == 200) {
 | 
			
		||||
      var releases = parse(res.body).querySelectorAll('.package-version');
 | 
			
		||||
      List<dynamic> releases = jsonDecode(res.body)['packages'] ?? [];
 | 
			
		||||
      if (releases.isEmpty) {
 | 
			
		||||
        throw NoReleasesError();
 | 
			
		||||
      }
 | 
			
		||||
      String? latestVersion = releases[0]
 | 
			
		||||
          .querySelector('.package-version-header b')
 | 
			
		||||
          ?.innerHtml
 | 
			
		||||
          .split(' ')
 | 
			
		||||
          .sublist(1)
 | 
			
		||||
          .join(' ');
 | 
			
		||||
      String? latestVersion = releases[0]['versionName'];
 | 
			
		||||
      if (latestVersion == null) {
 | 
			
		||||
        throw NoVersionError();
 | 
			
		||||
      }
 | 
			
		||||
      List<String> apkUrls = releases
 | 
			
		||||
          .where((element) =>
 | 
			
		||||
              element
 | 
			
		||||
                  .querySelector('.package-version-header b')
 | 
			
		||||
                  ?.innerHtml
 | 
			
		||||
                  .split(' ')
 | 
			
		||||
                  .sublist(1)
 | 
			
		||||
                  .join(' ') ==
 | 
			
		||||
              latestVersion)
 | 
			
		||||
          .map((e) =>
 | 
			
		||||
              e
 | 
			
		||||
                  .querySelector('.package-version-download a')
 | 
			
		||||
                  ?.attributes['href'] ??
 | 
			
		||||
              '')
 | 
			
		||||
          .where((element) => element.isNotEmpty)
 | 
			
		||||
          .where((element) => element['versionName'] == latestVersion)
 | 
			
		||||
          .map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
 | 
			
		||||
          .toList();
 | 
			
		||||
      if (apkUrls.isEmpty) {
 | 
			
		||||
        throw NoAPKError();
 | 
			
		||||
@@ -73,6 +57,15 @@ class FDroid extends AppSource {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
    String? appId = tryInferringAppId(standardUrl);
 | 
			
		||||
    return getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
			
		||||
        await get(Uri.parse('https://f-droid.org/api/v1/packages/$appId')),
 | 
			
		||||
        'https://f-droid.org/repo/$appId');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  AppNames getAppNames(String standardUrl) {
 | 
			
		||||
    return AppNames('F-Droid', Uri.parse(standardUrl).pathSegments.last);
 | 
			
		||||
 
 | 
			
		||||
@@ -105,9 +105,6 @@ class GitHub extends AppSource {
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) =>
 | 
			
		||||
      '$standardUrl/releases';
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,6 @@ class GitLab extends AppSource {
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) =>
 | 
			
		||||
      '$standardUrl/-/releases';
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import 'package:html/parser.dart';
 | 
			
		||||
import 'package:http/http.dart';
 | 
			
		||||
import 'package:obtainium/app_sources/fdroid.dart';
 | 
			
		||||
import 'package:obtainium/custom_errors.dart';
 | 
			
		||||
import 'package:obtainium/providers/source_provider.dart';
 | 
			
		||||
 | 
			
		||||
@@ -22,41 +22,18 @@ class IzzyOnDroid extends AppSource {
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) => null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
  String? tryInferringAppId(String standardUrl) {
 | 
			
		||||
    return FDroid().tryInferringAppId(standardUrl);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
    Response res = await get(Uri.parse(standardUrl));
 | 
			
		||||
    if (res.statusCode == 200) {
 | 
			
		||||
      var parsedHtml = parse(res.body);
 | 
			
		||||
      var multipleVersionApkUrls = parsedHtml
 | 
			
		||||
          .querySelectorAll('a')
 | 
			
		||||
          .where((element) =>
 | 
			
		||||
              element.attributes['href']?.toLowerCase().endsWith('.apk') ??
 | 
			
		||||
              false)
 | 
			
		||||
          .map((e) => 'https://$host${e.attributes['href'] ?? ''}')
 | 
			
		||||
          .toList();
 | 
			
		||||
      if (multipleVersionApkUrls.isEmpty) {
 | 
			
		||||
        throw NoAPKError();
 | 
			
		||||
      }
 | 
			
		||||
      var version = parsedHtml
 | 
			
		||||
          .querySelector('#keydata')
 | 
			
		||||
          ?.querySelectorAll('b')
 | 
			
		||||
          .where(
 | 
			
		||||
              (element) => element.innerHtml.toLowerCase().contains('version'))
 | 
			
		||||
          .toList()[0]
 | 
			
		||||
          .parentNode
 | 
			
		||||
          ?.parentNode
 | 
			
		||||
          ?.children[1]
 | 
			
		||||
          .innerHtml;
 | 
			
		||||
      if (version == null) {
 | 
			
		||||
        throw NoVersionError();
 | 
			
		||||
      }
 | 
			
		||||
      return APKDetails(version, [multipleVersionApkUrls[0]]);
 | 
			
		||||
    } else {
 | 
			
		||||
      throw NoReleasesError();
 | 
			
		||||
    }
 | 
			
		||||
    String? appId = tryInferringAppId(standardUrl);
 | 
			
		||||
    return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
			
		||||
        await get(
 | 
			
		||||
            Uri.parse('https://apt.izzysoft.de/fdroid/api/v1/packages/$appId')),
 | 
			
		||||
        'https://android.izzysoft.de/frepo/$appId');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
 
 | 
			
		||||
@@ -22,9 +22,6 @@ class Mullvad extends AppSource {
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) =>
 | 
			
		||||
      'https://github.com/mullvad/mullvadvpn-app/blob/master/CHANGELOG.md';
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,6 @@ class Signal extends AppSource {
 | 
			
		||||
  @override
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) => null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,6 @@ class SourceForge extends AppSource {
 | 
			
		||||
  @override
 | 
			
		||||
  String? changeLogPageFromStandardUrl(String standardUrl) => null;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async => apkUrl;
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
      String standardUrl, List<String> additionalData) async {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
 | 
			
		||||
 | 
			
		||||
enum FormItemType { string, bool }
 | 
			
		||||
 | 
			
		||||
typedef OnValueChanges = void Function(List<String> values, bool valid);
 | 
			
		||||
typedef OnValueChanges = void Function(
 | 
			
		||||
    List<String> values, bool valid, bool isBuilding);
 | 
			
		||||
 | 
			
		||||
class GeneratedFormItem {
 | 
			
		||||
  late String label;
 | 
			
		||||
@@ -13,6 +14,7 @@ class GeneratedFormItem {
 | 
			
		||||
  late String id;
 | 
			
		||||
  late List<Widget> belowWidgets;
 | 
			
		||||
  late String? hint;
 | 
			
		||||
  late List<String>? opts;
 | 
			
		||||
 | 
			
		||||
  GeneratedFormItem(
 | 
			
		||||
      {this.label = 'Input',
 | 
			
		||||
@@ -22,7 +24,8 @@ class GeneratedFormItem {
 | 
			
		||||
      this.additionalValidators = const [],
 | 
			
		||||
      this.id = 'input',
 | 
			
		||||
      this.belowWidgets = const [],
 | 
			
		||||
      this.hint});
 | 
			
		||||
      this.hint,
 | 
			
		||||
      this.opts});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class GeneratedForm extends StatefulWidget {
 | 
			
		||||
@@ -47,7 +50,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
 | 
			
		||||
  List<List<Widget>> rows = [];
 | 
			
		||||
 | 
			
		||||
  // If any value changes, call this to update the parent with value and validity
 | 
			
		||||
  void someValueChanged() {
 | 
			
		||||
  void someValueChanged({bool isBuilding = false}) {
 | 
			
		||||
    List<String> returnValues = [];
 | 
			
		||||
    var valid = true;
 | 
			
		||||
    for (int r = 0; r < values.length; r++) {
 | 
			
		||||
@@ -62,7 +65,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    widget.onValueChanges(returnValues, valid);
 | 
			
		||||
    widget.onValueChanges(returnValues, valid, isBuilding);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -75,6 +78,8 @@ class _GeneratedFormState extends State<GeneratedForm> {
 | 
			
		||||
        .map((row) => row.map((e) {
 | 
			
		||||
              return j < widget.defaultValues.length
 | 
			
		||||
                  ? widget.defaultValues[j++]
 | 
			
		||||
                  : e.opts != null
 | 
			
		||||
                      ? e.opts!.first
 | 
			
		||||
                      : '';
 | 
			
		||||
            }).toList())
 | 
			
		||||
        .toList();
 | 
			
		||||
@@ -82,7 +87,7 @@ class _GeneratedFormState extends State<GeneratedForm> {
 | 
			
		||||
    // Dynamically create form inputs
 | 
			
		||||
    formInputs = widget.items.asMap().entries.map((row) {
 | 
			
		||||
      return row.value.asMap().entries.map((e) {
 | 
			
		||||
        if (e.value.type == FormItemType.string) {
 | 
			
		||||
        if (e.value.type == FormItemType.string && e.value.opts == null) {
 | 
			
		||||
          final formFieldKey = GlobalKey<FormFieldState>();
 | 
			
		||||
          return TextFormField(
 | 
			
		||||
            key: formFieldKey,
 | 
			
		||||
@@ -112,11 +117,29 @@ class _GeneratedFormState extends State<GeneratedForm> {
 | 
			
		||||
              return null;
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
        } else if (e.value.type == FormItemType.string &&
 | 
			
		||||
            e.value.opts != null) {
 | 
			
		||||
          if (e.value.opts!.isEmpty) {
 | 
			
		||||
            return const Text('ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT.');
 | 
			
		||||
          }
 | 
			
		||||
          return DropdownButtonFormField(
 | 
			
		||||
              decoration: const InputDecoration(labelText: 'Colour'),
 | 
			
		||||
              value: values[row.key][e.key],
 | 
			
		||||
              items: e.value.opts!
 | 
			
		||||
                  .map((e) => DropdownMenuItem(value: e, child: Text(e)))
 | 
			
		||||
                  .toList(),
 | 
			
		||||
              onChanged: (value) {
 | 
			
		||||
                setState(() {
 | 
			
		||||
                  values[row.key][e.key] = value ?? e.value.opts!.first;
 | 
			
		||||
                  someValueChanged();
 | 
			
		||||
                });
 | 
			
		||||
              });
 | 
			
		||||
        } else {
 | 
			
		||||
          return Container(); // Some input types added in build
 | 
			
		||||
        }
 | 
			
		||||
      }).toList();
 | 
			
		||||
    }).toList();
 | 
			
		||||
    someValueChanged(isBuilding: true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
 
 | 
			
		||||
@@ -46,11 +46,16 @@ class _GeneratedFormModalState extends State<GeneratedFormModal> {
 | 
			
		||||
          ),
 | 
			
		||||
        GeneratedForm(
 | 
			
		||||
            items: widget.items,
 | 
			
		||||
            onValueChanges: (values, valid) {
 | 
			
		||||
            onValueChanges: (values, valid, isBuilding) {
 | 
			
		||||
              if (isBuilding) {
 | 
			
		||||
                this.values = values;
 | 
			
		||||
                this.valid = valid;
 | 
			
		||||
              } else {
 | 
			
		||||
                setState(() {
 | 
			
		||||
                  this.values = values;
 | 
			
		||||
                  this.valid = valid;
 | 
			
		||||
                });
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
            defaultValues: widget.defaultValues)
 | 
			
		||||
      ]),
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ import 'package:dynamic_color/dynamic_color.dart';
 | 
			
		||||
import 'package:device_info_plus/device_info_plus.dart';
 | 
			
		||||
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
 | 
			
		||||
 | 
			
		||||
const String currentVersion = '0.7.3';
 | 
			
		||||
const String currentVersion = '0.7.5';
 | 
			
		||||
const String currentReleaseTag =
 | 
			
		||||
    'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -66,7 +66,7 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
			
		||||
                                          ])
 | 
			
		||||
                                    ]
 | 
			
		||||
                                  ],
 | 
			
		||||
                                  onValueChanges: (values, valid) {
 | 
			
		||||
                                  onValueChanges: (values, valid, isBuilding) {
 | 
			
		||||
                                    setState(() {
 | 
			
		||||
                                      userInput = values[0];
 | 
			
		||||
                                      var source = valid
 | 
			
		||||
@@ -115,11 +115,15 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
			
		||||
                                                    additionalData);
 | 
			
		||||
                                            await settingsProvider
 | 
			
		||||
                                                .getInstallPermission();
 | 
			
		||||
                                            // Only download the APK here if you need to for the package ID
 | 
			
		||||
                                            if (sourceProvider
 | 
			
		||||
                                                .isTempId(app.id)) {
 | 
			
		||||
                                              // ignore: use_build_context_synchronously
 | 
			
		||||
                                              var apkUrl = await appsProvider
 | 
			
		||||
                                                  .confirmApkUrl(app, context);
 | 
			
		||||
                                              if (apkUrl == null) {
 | 
			
		||||
                                              throw ObtainiumError('Cancelled');
 | 
			
		||||
                                                throw ObtainiumError(
 | 
			
		||||
                                                    'Cancelled');
 | 
			
		||||
                                              }
 | 
			
		||||
                                              app.preferredApkIndex =
 | 
			
		||||
                                                  app.apkUrls.indexOf(apkUrl);
 | 
			
		||||
@@ -127,6 +131,7 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
			
		||||
                                                  await appsProvider
 | 
			
		||||
                                                      .downloadApp(app);
 | 
			
		||||
                                              app.id = downloadedApk.appId;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            if (appsProvider.apps
 | 
			
		||||
                                                .containsKey(app.id)) {
 | 
			
		||||
                                              throw ObtainiumError(
 | 
			
		||||
@@ -174,7 +179,7 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
			
		||||
                                .additionalDataFormItems.isNotEmpty)
 | 
			
		||||
                              GeneratedForm(
 | 
			
		||||
                                  items: pickedSource!.additionalDataFormItems,
 | 
			
		||||
                                  onValueChanges: (values, valid) {
 | 
			
		||||
                                  onValueChanges: (values, valid, isBuilding) {
 | 
			
		||||
                                    setState(() {
 | 
			
		||||
                                      additionalData = values;
 | 
			
		||||
                                      validAdditionalData = valid;
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ class _SettingsPageState extends State<SettingsPage> {
 | 
			
		||||
      if (e.moreSourceSettingsFormItems.isNotEmpty) {
 | 
			
		||||
        return GeneratedForm(
 | 
			
		||||
            items: e.moreSourceSettingsFormItems.map((e) => [e]).toList(),
 | 
			
		||||
            onValueChanges: (values, valid) {
 | 
			
		||||
            onValueChanges: (values, valid, isBuilding) {
 | 
			
		||||
              if (valid) {
 | 
			
		||||
                for (var i = 0; i < values.length; i++) {
 | 
			
		||||
                  settingsProvider.setSettingString(
 | 
			
		||||
@@ -264,26 +264,10 @@ class _SettingsPageState extends State<SettingsPage> {
 | 
			
		||||
                            if (logs.isEmpty) {
 | 
			
		||||
                              showError(ObtainiumError('No Logs'), context);
 | 
			
		||||
                            } else {
 | 
			
		||||
                              String logString =
 | 
			
		||||
                                  logs.map((e) => e.toString()).join('\n\n');
 | 
			
		||||
                              showDialog(
 | 
			
		||||
                                  context: context,
 | 
			
		||||
                                  builder: (BuildContext ctx) {
 | 
			
		||||
                                    return GeneratedFormModal(
 | 
			
		||||
                                      title: 'Obtainium App Logs',
 | 
			
		||||
                                      items: const [],
 | 
			
		||||
                                      defaultValues: const [],
 | 
			
		||||
                                      message: logString,
 | 
			
		||||
                                      initValid: true,
 | 
			
		||||
                                    );
 | 
			
		||||
                                  }).then((value) {
 | 
			
		||||
                                if (value != null) {
 | 
			
		||||
                                  Share.share(
 | 
			
		||||
                                      logs
 | 
			
		||||
                                          .map((e) => e.toString())
 | 
			
		||||
                                          .join('\n\n'),
 | 
			
		||||
                                      subject: 'Obtainium App Logs');
 | 
			
		||||
                                }
 | 
			
		||||
                                    return const LogsDialog();
 | 
			
		||||
                                  });
 | 
			
		||||
                            }
 | 
			
		||||
                          });
 | 
			
		||||
@@ -299,3 +283,71 @@ class _SettingsPageState extends State<SettingsPage> {
 | 
			
		||||
        ]));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class LogsDialog extends StatefulWidget {
 | 
			
		||||
  const LogsDialog({super.key});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  State<LogsDialog> createState() => _LogsDialogState();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _LogsDialogState extends State<LogsDialog> {
 | 
			
		||||
  String? logString;
 | 
			
		||||
  List<int> days = [7, 5, 4, 3, 2, 1];
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    var logsProvider = context.read<LogsProvider>();
 | 
			
		||||
    void filterLogs(int days) {
 | 
			
		||||
      logsProvider
 | 
			
		||||
          .get(after: DateTime.now().subtract(Duration(days: days)))
 | 
			
		||||
          .then((value) {
 | 
			
		||||
        setState(() {
 | 
			
		||||
          String l = value.map((e) => e.toString()).join('\n\n');
 | 
			
		||||
          logString = l.isNotEmpty ? l : 'No Logs';
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (logString == null) {
 | 
			
		||||
      filterLogs(days.first);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return AlertDialog(
 | 
			
		||||
      scrollable: true,
 | 
			
		||||
      title: const Text('Obtainium App Logs'),
 | 
			
		||||
      content: Column(
 | 
			
		||||
        children: [
 | 
			
		||||
          DropdownButtonFormField(
 | 
			
		||||
              value: days.first,
 | 
			
		||||
              items: days
 | 
			
		||||
                  .map((e) => DropdownMenuItem(
 | 
			
		||||
                        value: e,
 | 
			
		||||
                        child: Text('$e Day${e == 1 ? '' : 's'}'),
 | 
			
		||||
                      ))
 | 
			
		||||
                  .toList(),
 | 
			
		||||
              onChanged: (d) {
 | 
			
		||||
                filterLogs(d ?? 7);
 | 
			
		||||
              }),
 | 
			
		||||
          const SizedBox(
 | 
			
		||||
            height: 32,
 | 
			
		||||
          ),
 | 
			
		||||
          Text(logString ?? '')
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      actions: [
 | 
			
		||||
        TextButton(
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              Navigator.of(context).pop();
 | 
			
		||||
            },
 | 
			
		||||
            child: const Text('Close')),
 | 
			
		||||
        TextButton(
 | 
			
		||||
            onPressed: () {
 | 
			
		||||
              Share.share(logString ?? '', subject: 'Obtainium App Logs');
 | 
			
		||||
              Navigator.of(context).pop();
 | 
			
		||||
            },
 | 
			
		||||
            child: const Text('Share'))
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -155,14 +155,18 @@ class AppSource {
 | 
			
		||||
    throw NotImplementedError();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) {
 | 
			
		||||
    throw NotImplementedError();
 | 
			
		||||
  Future<String> apkUrlPrefetchModifier(String apkUrl) async {
 | 
			
		||||
    return apkUrl;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool canSearch = false;
 | 
			
		||||
  Future<Map<String, String>> search(String query) {
 | 
			
		||||
    throw NotImplementedError();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String? tryInferringAppId(String standardUrl) {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ObtainiumError getObtainiumHttpError(Response res) {
 | 
			
		||||
@@ -240,7 +244,9 @@ class SourceProvider {
 | 
			
		||||
    APKDetails apk =
 | 
			
		||||
        await source.getLatestAPKDetails(standardUrl, additionalData);
 | 
			
		||||
    return App(
 | 
			
		||||
        id ?? generateTempID(names, source),
 | 
			
		||||
        id ??
 | 
			
		||||
            source.tryInferringAppId(standardUrl) ??
 | 
			
		||||
            generateTempID(names, source),
 | 
			
		||||
        standardUrl,
 | 
			
		||||
        names.author[0].toUpperCase() + names.author.substring(1),
 | 
			
		||||
        name.trim().isNotEmpty
 | 
			
		||||
 
 | 
			
		||||
@@ -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.7.3+59 # When changing this, update the tag in main() accordingly
 | 
			
		||||
version: 0.7.5+61 # When changing this, update the tag in main() accordingly
 | 
			
		||||
 | 
			
		||||
environment:
 | 
			
		||||
  sdk: '>=2.18.2 <3.0.0'
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user