mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-04 07:13:28 +01:00 
			
		
		
		
	Merge pull request #1478 from ImranR98/dev
Resume failed downloads when possible instead of starting again (#634), Typo: 'Installed' not 'Updated' (#1469) + Bugfix: Don't incorrectly show message when user cancel, Add changelog button to app page (#1474)
This commit is contained in:
		
							
								
								
									
										1
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							@@ -42,7 +42,6 @@ jobs:
 | 
				
			|||||||
          if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi
 | 
					          if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi
 | 
				
			||||||
          echo "beta=$BETA" >> $GITHUB_OUTPUT
 | 
					          echo "beta=$BETA" >> $GITHUB_OUTPUT
 | 
				
			||||||
          TAG="v$VERSION"
 | 
					          TAG="v$VERSION"
 | 
				
			||||||
          if [ $BETA == true ]; then TAG="$TAG"-beta; fi
 | 
					 | 
				
			||||||
          echo "tag=$TAG" >> $GITHUB_OUTPUT
 | 
					          echo "tag=$TAG" >> $GITHUB_OUTPUT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      - name: Build APKs
 | 
					      - name: Build APKs
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,9 @@
 | 
				
			|||||||
 | 
					plugins {
 | 
				
			||||||
 | 
					    id "com.android.application"
 | 
				
			||||||
 | 
					    id "kotlin-android"
 | 
				
			||||||
 | 
					    id "dev.flutter.flutter-gradle-plugin"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def localProperties = new Properties()
 | 
					def localProperties = new Properties()
 | 
				
			||||||
def localPropertiesFile = rootProject.file('local.properties')
 | 
					def localPropertiesFile = rootProject.file('local.properties')
 | 
				
			||||||
if (localPropertiesFile.exists()) {
 | 
					if (localPropertiesFile.exists()) {
 | 
				
			||||||
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def flutterRoot = localProperties.getProperty('flutter.sdk')
 | 
					 | 
				
			||||||
if (flutterRoot == null) {
 | 
					 | 
				
			||||||
    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
 | 
					def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
 | 
				
			||||||
if (flutterVersionCode == null) {
 | 
					if (flutterVersionCode == null) {
 | 
				
			||||||
    flutterVersionCode = '1'
 | 
					    flutterVersionCode = '1'
 | 
				
			||||||
@@ -21,11 +22,6 @@ if (flutterVersionName == null) {
 | 
				
			|||||||
    flutterVersionName = '1.0'
 | 
					    flutterVersionName = '1.0'
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
apply plugin: 'com.android.application'
 | 
					 | 
				
			||||||
apply plugin: 'kotlin-android'
 | 
					 | 
				
			||||||
apply plugin: 'dev.rikka.tools.refine'
 | 
					 | 
				
			||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def keystoreProperties = new Properties()
 | 
					def keystoreProperties = new Properties()
 | 
				
			||||||
def keystorePropertiesFile = rootProject.file('key.properties')
 | 
					def keystorePropertiesFile = rootProject.file('key.properties')
 | 
				
			||||||
if (keystorePropertiesFile.exists()) {
 | 
					if (keystorePropertiesFile.exists()) {
 | 
				
			||||||
@@ -33,7 +29,8 @@ if (keystorePropertiesFile.exists()) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
android {
 | 
					android {
 | 
				
			||||||
    compileSdkVersion rootProject.ext.compileSdkVersion
 | 
					    namespace "dev.imranr.obtainium"
 | 
				
			||||||
 | 
					    compileSdk flutter.compileSdkVersion
 | 
				
			||||||
    ndkVersion flutter.ndkVersion
 | 
					    ndkVersion flutter.ndkVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    compileOptions {
 | 
					    compileOptions {
 | 
				
			||||||
@@ -54,7 +51,7 @@ android {
 | 
				
			|||||||
        // You can update the following values to match your application needs.
 | 
					        // 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.
 | 
					        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
 | 
				
			||||||
        minSdkVersion 24
 | 
					        minSdkVersion 24
 | 
				
			||||||
        targetSdkVersion rootProject.ext.targetSdkVersion
 | 
					        targetSdkVersion flutter.targetSdkVersion
 | 
				
			||||||
        versionCode flutterVersionCode.toInteger()
 | 
					        versionCode flutterVersionCode.toInteger()
 | 
				
			||||||
        versionName flutterVersionName
 | 
					        versionName flutterVersionName
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,3 @@
 | 
				
			|||||||
buildscript {
 | 
					 | 
				
			||||||
    ext.kotlin_version = '1.8.10'
 | 
					 | 
				
			||||||
    ext {
 | 
					 | 
				
			||||||
        compileSdkVersion   = 34                // or latest
 | 
					 | 
				
			||||||
        targetSdkVersion    = 34                // or latest
 | 
					 | 
				
			||||||
        appCompatVersion    = "1.4.2"           // or latest
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    repositories {
 | 
					 | 
				
			||||||
        google()
 | 
					 | 
				
			||||||
        mavenCentral()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dependencies {
 | 
					 | 
				
			||||||
        classpath "com.android.tools.build:gradle:7.4.2"
 | 
					 | 
				
			||||||
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 | 
					 | 
				
			||||||
        classpath "dev.rikka.tools.refine:gradle-plugin:4.3.1"
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
allprojects {
 | 
					allprojects {
 | 
				
			||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
        google()
 | 
					        google()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,25 @@
 | 
				
			|||||||
include ':app'
 | 
					pluginManagement {
 | 
				
			||||||
 | 
					    def flutterSdkPath = {
 | 
				
			||||||
 | 
					        def properties = new Properties()
 | 
				
			||||||
 | 
					        file("local.properties").withInputStream { properties.load(it) }
 | 
				
			||||||
 | 
					        def flutterSdkPath = properties.getProperty("flutter.sdk")
 | 
				
			||||||
 | 
					        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
 | 
				
			||||||
 | 
					        return flutterSdkPath
 | 
				
			||||||
 | 
					    }()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
 | 
					    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
 | 
				
			||||||
def properties = new Properties()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
assert localPropertiesFile.exists()
 | 
					    repositories {
 | 
				
			||||||
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
 | 
					        google()
 | 
				
			||||||
 | 
					        mavenCentral()
 | 
				
			||||||
 | 
					        gradlePluginPortal()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def flutterSdkPath = properties.getProperty("flutter.sdk")
 | 
					plugins {
 | 
				
			||||||
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
 | 
					    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
 | 
				
			||||||
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
 | 
					    id "com.android.application" version "7.4.2" apply false
 | 
				
			||||||
 | 
					    id "org.jetbrains.kotlin.android" version "1.8.10" apply false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include ":app"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
 | 
				
			|||||||
import 'package:obtainium/components/generated_form_modal.dart';
 | 
					import 'package:obtainium/components/generated_form_modal.dart';
 | 
				
			||||||
import 'package:obtainium/custom_errors.dart';
 | 
					import 'package:obtainium/custom_errors.dart';
 | 
				
			||||||
import 'package:obtainium/main.dart';
 | 
					import 'package:obtainium/main.dart';
 | 
				
			||||||
 | 
					import 'package:obtainium/pages/apps.dart';
 | 
				
			||||||
import 'package:obtainium/pages/settings.dart';
 | 
					import 'package:obtainium/pages/settings.dart';
 | 
				
			||||||
import 'package:obtainium/providers/apps_provider.dart';
 | 
					import 'package:obtainium/providers/apps_provider.dart';
 | 
				
			||||||
import 'package:obtainium/providers/settings_provider.dart';
 | 
					import 'package:obtainium/providers/settings_provider.dart';
 | 
				
			||||||
@@ -108,6 +109,7 @@ class _AppPageState extends State<AppPage> {
 | 
				
			|||||||
        infoLines =
 | 
					        infoLines =
 | 
				
			||||||
            '$infoLines\n${app?.app.apkUrls.length == 1 ? app?.app.apkUrls[0].key : plural('apk', app?.app.apkUrls.length ?? 0)}';
 | 
					            '$infoLines\n${app?.app.apkUrls.length == 1 ? app?.app.apkUrls[0].key : plural('apk', app?.app.apkUrls.length ?? 0)}';
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      var changeLogFn = app != null ? getChangeLogFn(context, app.app) : null;
 | 
				
			||||||
      return Column(
 | 
					      return Column(
 | 
				
			||||||
        mainAxisAlignment: MainAxisAlignment.center,
 | 
					        mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
					        crossAxisAlignment: CrossAxisAlignment.stretch,
 | 
				
			||||||
@@ -125,13 +127,26 @@ class _AppPageState extends State<AppPage> {
 | 
				
			|||||||
                        .textTheme
 | 
					                        .textTheme
 | 
				
			||||||
                        .bodyLarge!
 | 
					                        .bodyLarge!
 | 
				
			||||||
                        .copyWith(fontWeight: FontWeight.bold)),
 | 
					                        .copyWith(fontWeight: FontWeight.bold)),
 | 
				
			||||||
 | 
					                changeLogFn != null || app?.app.releaseDate != null
 | 
				
			||||||
 | 
					                    ? GestureDetector(
 | 
				
			||||||
 | 
					                        onTap: changeLogFn,
 | 
				
			||||||
 | 
					                        child: Text(
 | 
				
			||||||
                          app?.app.releaseDate == null
 | 
					                          app?.app.releaseDate == null
 | 
				
			||||||
                    ? const SizedBox.shrink()
 | 
					                              ? tr('changes')
 | 
				
			||||||
                    : Text(
 | 
					                              : app!.app.releaseDate.toString(),
 | 
				
			||||||
                        app!.app.releaseDate.toString(),
 | 
					 | 
				
			||||||
                          textAlign: TextAlign.center,
 | 
					                          textAlign: TextAlign.center,
 | 
				
			||||||
                        style: Theme.of(context).textTheme.labelSmall,
 | 
					                          style:
 | 
				
			||||||
 | 
					                              Theme.of(context).textTheme.labelSmall!.copyWith(
 | 
				
			||||||
 | 
					                                    decoration: changeLogFn != null
 | 
				
			||||||
 | 
					                                        ? TextDecoration.underline
 | 
				
			||||||
 | 
					                                        : null,
 | 
				
			||||||
 | 
					                                    fontStyle: changeLogFn != null
 | 
				
			||||||
 | 
					                                        ? FontStyle.italic
 | 
				
			||||||
 | 
					                                        : null,
 | 
				
			||||||
                                  ),
 | 
					                                  ),
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                      )
 | 
				
			||||||
 | 
					                    : const SizedBox.shrink(),
 | 
				
			||||||
                const SizedBox(
 | 
					                const SizedBox(
 | 
				
			||||||
                  height: 8,
 | 
					                  height: 8,
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
@@ -361,6 +376,9 @@ class _AppPageState extends State<AppPage> {
 | 
				
			|||||||
                !areDownloadsRunning
 | 
					                !areDownloadsRunning
 | 
				
			||||||
            ? () async {
 | 
					            ? () async {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
 | 
					                  var successMessage = app?.app.installedVersion == null
 | 
				
			||||||
 | 
					                      ? tr('installed')
 | 
				
			||||||
 | 
					                      : tr('appsUpdated');
 | 
				
			||||||
                  HapticFeedback.heavyImpact();
 | 
					                  HapticFeedback.heavyImpact();
 | 
				
			||||||
                  var res = await appsProvider.downloadAndInstallLatestApps(
 | 
					                  var res = await appsProvider.downloadAndInstallLatestApps(
 | 
				
			||||||
                    app?.app.id != null ? [app!.app.id] : [],
 | 
					                    app?.app.id != null ? [app!.app.id] : [],
 | 
				
			||||||
@@ -368,7 +386,7 @@ class _AppPageState extends State<AppPage> {
 | 
				
			|||||||
                  );
 | 
					                  );
 | 
				
			||||||
                  if (res.isNotEmpty && !trackOnly) {
 | 
					                  if (res.isNotEmpty && !trackOnly) {
 | 
				
			||||||
                    // ignore: use_build_context_synchronously
 | 
					                    // ignore: use_build_context_synchronously
 | 
				
			||||||
                    showMessage(tr('appsUpdated'), context);
 | 
					                    showMessage(successMessage, context);
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                  if (res.isNotEmpty && mounted) {
 | 
					                  if (res.isNotEmpty && mounted) {
 | 
				
			||||||
                    Navigator.of(context).pop();
 | 
					                    Navigator.of(context).pop();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,92 @@ class AppsPage extends StatefulWidget {
 | 
				
			|||||||
  State<AppsPage> createState() => AppsPageState();
 | 
					  State<AppsPage> createState() => AppsPageState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					showChangeLogDialog(BuildContext context, App app, String? changesUrl,
 | 
				
			||||||
 | 
					    AppSource appSource, String changeLog) {
 | 
				
			||||||
 | 
					  showDialog(
 | 
				
			||||||
 | 
					      context: context,
 | 
				
			||||||
 | 
					      builder: (BuildContext context) {
 | 
				
			||||||
 | 
					        return GeneratedFormModal(
 | 
				
			||||||
 | 
					          title: tr('changes'),
 | 
				
			||||||
 | 
					          items: const [],
 | 
				
			||||||
 | 
					          message: app.latestVersion,
 | 
				
			||||||
 | 
					          additionalWidgets: [
 | 
				
			||||||
 | 
					            changesUrl != null
 | 
				
			||||||
 | 
					                ? GestureDetector(
 | 
				
			||||||
 | 
					                    child: Text(
 | 
				
			||||||
 | 
					                      changesUrl,
 | 
				
			||||||
 | 
					                      style: const TextStyle(
 | 
				
			||||||
 | 
					                          decoration: TextDecoration.underline,
 | 
				
			||||||
 | 
					                          fontStyle: FontStyle.italic),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                    onTap: () {
 | 
				
			||||||
 | 
					                      launchUrlString(changesUrl,
 | 
				
			||||||
 | 
					                          mode: LaunchMode.externalApplication);
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                : const SizedBox.shrink(),
 | 
				
			||||||
 | 
					            changesUrl != null
 | 
				
			||||||
 | 
					                ? const SizedBox(
 | 
				
			||||||
 | 
					                    height: 16,
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                : const SizedBox.shrink(),
 | 
				
			||||||
 | 
					            appSource.changeLogIfAnyIsMarkDown
 | 
				
			||||||
 | 
					                ? SizedBox(
 | 
				
			||||||
 | 
					                    width: MediaQuery.of(context).size.width,
 | 
				
			||||||
 | 
					                    height: MediaQuery.of(context).size.height - 350,
 | 
				
			||||||
 | 
					                    child: Markdown(
 | 
				
			||||||
 | 
					                      data: changeLog,
 | 
				
			||||||
 | 
					                      onTapLink: (text, href, title) {
 | 
				
			||||||
 | 
					                        if (href != null) {
 | 
				
			||||||
 | 
					                          launchUrlString(
 | 
				
			||||||
 | 
					                              href.startsWith('http://') ||
 | 
				
			||||||
 | 
					                                      href.startsWith('https://')
 | 
				
			||||||
 | 
					                                  ? href
 | 
				
			||||||
 | 
					                                  : '${Uri.parse(app.url).origin}/$href',
 | 
				
			||||||
 | 
					                              mode: LaunchMode.externalApplication);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                      },
 | 
				
			||||||
 | 
					                      extensionSet: md.ExtensionSet(
 | 
				
			||||||
 | 
					                        md.ExtensionSet.gitHubFlavored.blockSyntaxes,
 | 
				
			||||||
 | 
					                        [
 | 
				
			||||||
 | 
					                          md.EmojiSyntax(),
 | 
				
			||||||
 | 
					                          ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                      ),
 | 
				
			||||||
 | 
					                    ))
 | 
				
			||||||
 | 
					                : Text(changeLog),
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          singleNullReturnButton: tr('ok'),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					getChangeLogFn(BuildContext context, App app) {
 | 
				
			||||||
 | 
					  AppSource appSource =
 | 
				
			||||||
 | 
					      SourceProvider().getSource(app.url, overrideSource: app.overrideSource);
 | 
				
			||||||
 | 
					  String? changesUrl = appSource.changeLogPageFromStandardUrl(app.url);
 | 
				
			||||||
 | 
					  String? changeLog = app.changeLog;
 | 
				
			||||||
 | 
					  if (changeLog?.split('\n').length == 1) {
 | 
				
			||||||
 | 
					    if (RegExp(
 | 
				
			||||||
 | 
					            '(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?')
 | 
				
			||||||
 | 
					        .hasMatch(changeLog!)) {
 | 
				
			||||||
 | 
					      if (changesUrl == null) {
 | 
				
			||||||
 | 
					        changesUrl = changeLog;
 | 
				
			||||||
 | 
					        changeLog = null;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (changeLog == null && changesUrl == null)
 | 
				
			||||||
 | 
					      ? null
 | 
				
			||||||
 | 
					      : () {
 | 
				
			||||||
 | 
					          if (changeLog != null) {
 | 
				
			||||||
 | 
					            showChangeLogDialog(context, app, changesUrl, appSource, changeLog);
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            launchUrlString(changesUrl!, mode: LaunchMode.externalApplication);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AppsPageState extends State<AppsPage> {
 | 
					class AppsPageState extends State<AppsPage> {
 | 
				
			||||||
  AppsFilter filter = AppsFilter();
 | 
					  AppsFilter filter = AppsFilter();
 | 
				
			||||||
  final AppsFilter neutralFilter = AppsFilter();
 | 
					  final AppsFilter neutralFilter = AppsFilter();
 | 
				
			||||||
@@ -262,66 +348,6 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
        .where((a) => selectedAppIds.contains(a.id))
 | 
					        .where((a) => selectedAppIds.contains(a.id))
 | 
				
			||||||
        .toSet();
 | 
					        .toSet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    showChangeLogDialog(
 | 
					 | 
				
			||||||
        String? changesUrl, AppSource appSource, String changeLog, int index) {
 | 
					 | 
				
			||||||
      showDialog(
 | 
					 | 
				
			||||||
          context: context,
 | 
					 | 
				
			||||||
          builder: (BuildContext context) {
 | 
					 | 
				
			||||||
            return GeneratedFormModal(
 | 
					 | 
				
			||||||
              title: tr('changes'),
 | 
					 | 
				
			||||||
              items: const [],
 | 
					 | 
				
			||||||
              message: listedApps[index].app.latestVersion,
 | 
					 | 
				
			||||||
              additionalWidgets: [
 | 
					 | 
				
			||||||
                changesUrl != null
 | 
					 | 
				
			||||||
                    ? GestureDetector(
 | 
					 | 
				
			||||||
                        child: Text(
 | 
					 | 
				
			||||||
                          changesUrl,
 | 
					 | 
				
			||||||
                          style: const TextStyle(
 | 
					 | 
				
			||||||
                              decoration: TextDecoration.underline,
 | 
					 | 
				
			||||||
                              fontStyle: FontStyle.italic),
 | 
					 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                        onTap: () {
 | 
					 | 
				
			||||||
                          launchUrlString(changesUrl,
 | 
					 | 
				
			||||||
                              mode: LaunchMode.externalApplication);
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                      )
 | 
					 | 
				
			||||||
                    : const SizedBox.shrink(),
 | 
					 | 
				
			||||||
                changesUrl != null
 | 
					 | 
				
			||||||
                    ? const SizedBox(
 | 
					 | 
				
			||||||
                        height: 16,
 | 
					 | 
				
			||||||
                      )
 | 
					 | 
				
			||||||
                    : const SizedBox.shrink(),
 | 
					 | 
				
			||||||
                appSource.changeLogIfAnyIsMarkDown
 | 
					 | 
				
			||||||
                    ? SizedBox(
 | 
					 | 
				
			||||||
                        width: MediaQuery.of(context).size.width,
 | 
					 | 
				
			||||||
                        height: MediaQuery.of(context).size.height - 350,
 | 
					 | 
				
			||||||
                        child: Markdown(
 | 
					 | 
				
			||||||
                          data: changeLog,
 | 
					 | 
				
			||||||
                          onTapLink: (text, href, title) {
 | 
					 | 
				
			||||||
                            if (href != null) {
 | 
					 | 
				
			||||||
                              launchUrlString(
 | 
					 | 
				
			||||||
                                  href.startsWith('http://') ||
 | 
					 | 
				
			||||||
                                          href.startsWith('https://')
 | 
					 | 
				
			||||||
                                      ? href
 | 
					 | 
				
			||||||
                                      : '${Uri.parse(listedApps[index].app.url).origin}/$href',
 | 
					 | 
				
			||||||
                                  mode: LaunchMode.externalApplication);
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                          },
 | 
					 | 
				
			||||||
                          extensionSet: md.ExtensionSet(
 | 
					 | 
				
			||||||
                            md.ExtensionSet.gitHubFlavored.blockSyntaxes,
 | 
					 | 
				
			||||||
                            [
 | 
					 | 
				
			||||||
                              md.EmojiSyntax(),
 | 
					 | 
				
			||||||
                              ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes
 | 
					 | 
				
			||||||
                            ],
 | 
					 | 
				
			||||||
                          ),
 | 
					 | 
				
			||||||
                        ))
 | 
					 | 
				
			||||||
                    : Text(changeLog),
 | 
					 | 
				
			||||||
              ],
 | 
					 | 
				
			||||||
              singleNullReturnButton: tr('ok'),
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getLoadingWidgets() {
 | 
					    getLoadingWidgets() {
 | 
				
			||||||
      return [
 | 
					      return [
 | 
				
			||||||
        if (listedApps.isEmpty)
 | 
					        if (listedApps.isEmpty)
 | 
				
			||||||
@@ -351,35 +377,6 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
      ];
 | 
					      ];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getChangeLogFn(int appIndex) {
 | 
					 | 
				
			||||||
      AppSource appSource = SourceProvider().getSource(
 | 
					 | 
				
			||||||
          listedApps[appIndex].app.url,
 | 
					 | 
				
			||||||
          overrideSource: listedApps[appIndex].app.overrideSource);
 | 
					 | 
				
			||||||
      String? changesUrl =
 | 
					 | 
				
			||||||
          appSource.changeLogPageFromStandardUrl(listedApps[appIndex].app.url);
 | 
					 | 
				
			||||||
      String? changeLog = listedApps[appIndex].app.changeLog;
 | 
					 | 
				
			||||||
      if (changeLog?.split('\n').length == 1) {
 | 
					 | 
				
			||||||
        if (RegExp(
 | 
					 | 
				
			||||||
                '(http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?')
 | 
					 | 
				
			||||||
            .hasMatch(changeLog!)) {
 | 
					 | 
				
			||||||
          if (changesUrl == null) {
 | 
					 | 
				
			||||||
            changesUrl = changeLog;
 | 
					 | 
				
			||||||
            changeLog = null;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return (changeLog == null && changesUrl == null)
 | 
					 | 
				
			||||||
          ? null
 | 
					 | 
				
			||||||
          : () {
 | 
					 | 
				
			||||||
              if (changeLog != null) {
 | 
					 | 
				
			||||||
                showChangeLogDialog(changesUrl, appSource, changeLog, appIndex);
 | 
					 | 
				
			||||||
              } else {
 | 
					 | 
				
			||||||
                launchUrlString(changesUrl!,
 | 
					 | 
				
			||||||
                    mode: LaunchMode.externalApplication);
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    getUpdateButton(int appIndex) {
 | 
					    getUpdateButton(int appIndex) {
 | 
				
			||||||
      return IconButton(
 | 
					      return IconButton(
 | 
				
			||||||
          visualDensity: VisualDensity.compact,
 | 
					          visualDensity: VisualDensity.compact,
 | 
				
			||||||
@@ -444,7 +441,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getSingleAppHorizTile(int index) {
 | 
					    getSingleAppHorizTile(int index) {
 | 
				
			||||||
      var showChangesFn = getChangeLogFn(index);
 | 
					      var showChangesFn = getChangeLogFn(context, listedApps[index].app);
 | 
				
			||||||
      var hasUpdate = listedApps[index].app.installedVersion != null &&
 | 
					      var hasUpdate = listedApps[index].app.installedVersion != null &&
 | 
				
			||||||
          listedApps[index].app.installedVersion !=
 | 
					          listedApps[index].app.installedVersion !=
 | 
				
			||||||
              listedApps[index].app.latestVersion;
 | 
					              listedApps[index].app.latestVersion;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -202,14 +202,18 @@ Future<String> checkPartialDownloadHash(String url, int bytesToGrab,
 | 
				
			|||||||
Future<File> downloadFile(
 | 
					Future<File> downloadFile(
 | 
				
			||||||
    String url, String fileNameNoExt, Function? onProgress, String destDir,
 | 
					    String url, String fileNameNoExt, Function? onProgress, String destDir,
 | 
				
			||||||
    {bool useExisting = true, Map<String, String>? headers}) async {
 | 
					    {bool useExisting = true, Map<String, String>? headers}) async {
 | 
				
			||||||
 | 
					  // Send the initial request but cancel it as soon as you have the headers
 | 
				
			||||||
 | 
					  var reqHeaders = headers ?? {};
 | 
				
			||||||
  var req = Request('GET', Uri.parse(url));
 | 
					  var req = Request('GET', Uri.parse(url));
 | 
				
			||||||
  if (headers != null) {
 | 
					  req.headers.addAll(reqHeaders);
 | 
				
			||||||
    req.headers.addAll(headers);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  var client = http.Client();
 | 
					  var client = http.Client();
 | 
				
			||||||
  StreamedResponse response = await client.send(req);
 | 
					  StreamedResponse response = await client.send(req);
 | 
				
			||||||
  String ext =
 | 
					  var resHeaders = response.headers;
 | 
				
			||||||
      response.headers['content-disposition']?.split('.').last ?? 'apk';
 | 
					
 | 
				
			||||||
 | 
					  // Use the headers to decide what the file extension is, and
 | 
				
			||||||
 | 
					  // whether it supports partial downloads (range request), and
 | 
				
			||||||
 | 
					  // what the total size of the file is (if provided)
 | 
				
			||||||
 | 
					  String ext = resHeaders['content-disposition']?.split('.').last ?? 'apk';
 | 
				
			||||||
  if (ext.endsWith('"') || ext.endsWith("other")) {
 | 
					  if (ext.endsWith('"') || ext.endsWith("other")) {
 | 
				
			||||||
    ext = ext.substring(0, ext.length - 1);
 | 
					    ext = ext.substring(0, ext.length - 1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -217,18 +221,68 @@ Future<File> downloadFile(
 | 
				
			|||||||
    ext = 'apk';
 | 
					    ext = 'apk';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  File downloadedFile = File('$destDir/$fileNameNoExt.$ext');
 | 
					  File downloadedFile = File('$destDir/$fileNameNoExt.$ext');
 | 
				
			||||||
  if (!(downloadedFile.existsSync() && useExisting)) {
 | 
					
 | 
				
			||||||
 | 
					  bool rangeFeatureEnabled = false;
 | 
				
			||||||
 | 
					  if (resHeaders['accept-ranges']?.isNotEmpty == true) {
 | 
				
			||||||
 | 
					    rangeFeatureEnabled =
 | 
				
			||||||
 | 
					        resHeaders['accept-ranges']?.trim().toLowerCase() == 'bytes';
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // If you have an existing file that is usable,
 | 
				
			||||||
 | 
					  // decide whether you can use it (either return full or resume partial)
 | 
				
			||||||
 | 
					  var fullContentLength = response.contentLength;
 | 
				
			||||||
 | 
					  if (useExisting && downloadedFile.existsSync()) {
 | 
				
			||||||
 | 
					    var length = downloadedFile.lengthSync();
 | 
				
			||||||
 | 
					    if (fullContentLength == null) {
 | 
				
			||||||
 | 
					      // Assume full
 | 
				
			||||||
 | 
					      client.close();
 | 
				
			||||||
 | 
					      return downloadedFile;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      // Check if resume needed/possible
 | 
				
			||||||
 | 
					      if (length == fullContentLength) {
 | 
				
			||||||
 | 
					        client.close();
 | 
				
			||||||
 | 
					        return downloadedFile;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (length > fullContentLength) {
 | 
				
			||||||
 | 
					        useExisting = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Download to a '.temp' file (to distinguish btn. complete/incomplete files)
 | 
				
			||||||
  File tempDownloadedFile = File('${downloadedFile.path}.part');
 | 
					  File tempDownloadedFile = File('${downloadedFile.path}.part');
 | 
				
			||||||
    if (tempDownloadedFile.existsSync()) {
 | 
					
 | 
				
			||||||
 | 
					  // If the range feature is not available (or you need to start a ranged req from 0),
 | 
				
			||||||
 | 
					  // complete the already-started request, else cancel it and start a ranged request,
 | 
				
			||||||
 | 
					  // and open the file for writing in the appropriate mode
 | 
				
			||||||
 | 
					  var targetFileLength = useExisting && tempDownloadedFile.existsSync()
 | 
				
			||||||
 | 
					      ? tempDownloadedFile.lengthSync()
 | 
				
			||||||
 | 
					      : null;
 | 
				
			||||||
 | 
					  int rangeStart = targetFileLength ?? 0;
 | 
				
			||||||
 | 
					  IOSink? sink;
 | 
				
			||||||
 | 
					  if (rangeFeatureEnabled && fullContentLength != null && rangeStart > 0) {
 | 
				
			||||||
 | 
					    client.close();
 | 
				
			||||||
 | 
					    client = http.Client();
 | 
				
			||||||
 | 
					    req = Request('GET', Uri.parse(url));
 | 
				
			||||||
 | 
					    req.headers.addAll(reqHeaders);
 | 
				
			||||||
 | 
					    req.headers.addAll({'range': 'bytes=$rangeStart-${fullContentLength - 1}'});
 | 
				
			||||||
 | 
					    response = await client.send(req);
 | 
				
			||||||
 | 
					    sink = tempDownloadedFile.openWrite(mode: FileMode.writeOnlyAppend);
 | 
				
			||||||
 | 
					  } else if (tempDownloadedFile.existsSync()) {
 | 
				
			||||||
    tempDownloadedFile.deleteSync(recursive: true);
 | 
					    tempDownloadedFile.deleteSync(recursive: true);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    var length = response.contentLength;
 | 
					  sink ??= tempDownloadedFile.openWrite(mode: FileMode.writeOnly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Perform the download
 | 
				
			||||||
  var received = 0;
 | 
					  var received = 0;
 | 
				
			||||||
  double? progress;
 | 
					  double? progress;
 | 
				
			||||||
    var sink = tempDownloadedFile.openWrite();
 | 
					  if (rangeStart > 0 && fullContentLength != null) {
 | 
				
			||||||
 | 
					    received = rangeStart;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  await response.stream.map((s) {
 | 
					  await response.stream.map((s) {
 | 
				
			||||||
    received += s.length;
 | 
					    received += s.length;
 | 
				
			||||||
      progress = (length != null ? received / length * 100 : 30);
 | 
					    progress =
 | 
				
			||||||
 | 
					        (fullContentLength != null ? (received / fullContentLength) * 100 : 30);
 | 
				
			||||||
    if (onProgress != null) {
 | 
					    if (onProgress != null) {
 | 
				
			||||||
      onProgress(progress);
 | 
					      onProgress(progress);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -239,19 +293,35 @@ Future<File> downloadFile(
 | 
				
			|||||||
  if (onProgress != null) {
 | 
					  if (onProgress != null) {
 | 
				
			||||||
    onProgress(progress);
 | 
					    onProgress(progress);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    if (response.statusCode != 200) {
 | 
					  if (response.statusCode < 200 || response.statusCode > 299) {
 | 
				
			||||||
    tempDownloadedFile.deleteSync(recursive: true);
 | 
					    tempDownloadedFile.deleteSync(recursive: true);
 | 
				
			||||||
    throw response.reasonPhrase ?? tr('unexpectedError');
 | 
					    throw response.reasonPhrase ?? tr('unexpectedError');
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  print(tempDownloadedFile.lengthSync());
 | 
				
			||||||
 | 
					  print(fullContentLength);
 | 
				
			||||||
  if (tempDownloadedFile.existsSync()) {
 | 
					  if (tempDownloadedFile.existsSync()) {
 | 
				
			||||||
    tempDownloadedFile.renameSync(downloadedFile.path);
 | 
					    tempDownloadedFile.renameSync(downloadedFile.path);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
  client.close();
 | 
					  client.close();
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return downloadedFile;
 | 
					  return downloadedFile;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Future<Map<String, String>> getHeaders(String url,
 | 
				
			||||||
 | 
					    {Map<String, String>? headers}) async {
 | 
				
			||||||
 | 
					  var req = http.Request('GET', Uri.parse(url));
 | 
				
			||||||
 | 
					  if (headers != null) {
 | 
				
			||||||
 | 
					    req.headers.addAll(headers);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  var client = http.Client();
 | 
				
			||||||
 | 
					  var response = await client.send(req);
 | 
				
			||||||
 | 
					  if (response.statusCode < 200 || response.statusCode > 299) {
 | 
				
			||||||
 | 
					    throw ObtainiumError(response.reasonPhrase ?? tr('unexpectedError'));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  var returnHeaders = response.headers;
 | 
				
			||||||
 | 
					  client.close();
 | 
				
			||||||
 | 
					  return returnHeaders;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Future<PackageInfo?> getInstalledInfo(String? packageName,
 | 
					Future<PackageInfo?> getInstalledInfo(String? packageName,
 | 
				
			||||||
    {bool printErr = true}) async {
 | 
					    {bool printErr = true}) async {
 | 
				
			||||||
  if (packageName != null) {
 | 
					  if (packageName != null) {
 | 
				
			||||||
@@ -493,13 +563,13 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
        zipFile: File(filePath), destinationDir: Directory(destinationPath));
 | 
					        zipFile: File(filePath), destinationDir: Directory(destinationPath));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> installXApkDir(DownloadedXApkDir dir,
 | 
					  Future<bool> installXApkDir(DownloadedXApkDir dir,
 | 
				
			||||||
      {bool needsBGWorkaround = false}) async {
 | 
					      {bool needsBGWorkaround = false}) async {
 | 
				
			||||||
    // We don't know which APKs in an XAPK are supported by the user's device
 | 
					    // 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
 | 
					    // 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
 | 
					    // If 0 APKs installed, throw the first install error encountered
 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
    var somethingInstalled = false;
 | 
					    var somethingInstalled = false;
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
      MultiAppMultiError errors = MultiAppMultiError();
 | 
					      MultiAppMultiError errors = MultiAppMultiError();
 | 
				
			||||||
      for (var file in dir.extracted
 | 
					      for (var file in dir.extracted
 | 
				
			||||||
          .listSync(recursive: true, followLinks: false)
 | 
					          .listSync(recursive: true, followLinks: false)
 | 
				
			||||||
@@ -526,6 +596,7 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      dir.extracted.delete(recursive: true);
 | 
					      dir.extracted.delete(recursive: true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return somethingInstalled;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<bool> installApk(DownloadedApk file,
 | 
					  Future<bool> installApk(DownloadedApk file,
 | 
				
			||||||
@@ -758,17 +829,18 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
        notifyListeners();
 | 
					        notifyListeners();
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          if (!skipInstalls) {
 | 
					          if (!skipInstalls) {
 | 
				
			||||||
 | 
					            bool sayInstalled = true;
 | 
				
			||||||
            if (downloadedFile != null) {
 | 
					            if (downloadedFile != null) {
 | 
				
			||||||
              if (willBeSilent && context == null) {
 | 
					              if (willBeSilent && context == null) {
 | 
				
			||||||
                installApk(downloadedFile, needsBGWorkaround: true);
 | 
					                installApk(downloadedFile, needsBGWorkaround: true);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                await installApk(downloadedFile);
 | 
					                sayInstalled = await installApk(downloadedFile);
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
              if (willBeSilent && context == null) {
 | 
					              if (willBeSilent && context == null) {
 | 
				
			||||||
                installXApkDir(downloadedDir!, needsBGWorkaround: true);
 | 
					                installXApkDir(downloadedDir!, needsBGWorkaround: true);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                await installXApkDir(downloadedDir!);
 | 
					                sayInstalled = await installXApkDir(downloadedDir!);
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (willBeSilent && context == null) {
 | 
					            if (willBeSilent && context == null) {
 | 
				
			||||||
@@ -776,8 +848,10 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
                  [apps[id]!.app],
 | 
					                  [apps[id]!.app],
 | 
				
			||||||
                  id: id.hashCode));
 | 
					                  id: id.hashCode));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (sayInstalled) {
 | 
				
			||||||
              installedIds.add(id);
 | 
					              installedIds.add(id);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
          apps[id]?.downloadProgress = null;
 | 
					          apps[id]?.downloadProgress = null;
 | 
				
			||||||
          notifyListeners();
 | 
					          notifyListeners();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										20
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -38,10 +38,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: app_links
 | 
					      name: app_links
 | 
				
			||||||
      sha256: "3ced568a5d9e309e99af71285666f1f3117bddd0bd5b3317979dccc1a40cada4"
 | 
					      sha256: fd7fc1569870b4b0d90d17a9f36661a6ff92400fecb6e4adab4abe0f0488bb5f
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.5.1"
 | 
					    version: "4.0.0"
 | 
				
			||||||
  archive:
 | 
					  archive:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -307,10 +307,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: flutter_local_notifications
 | 
					      name: flutter_local_notifications
 | 
				
			||||||
      sha256: "55b9b229307a10974b26296ff29f2e132256ba4bd74266939118eaefa941cb00"
 | 
					      sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "16.3.3"
 | 
					    version: "17.0.0"
 | 
				
			||||||
  flutter_local_notifications_linux:
 | 
					  flutter_local_notifications_linux:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -783,10 +783,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: sqflite_common
 | 
					      name: sqflite_common
 | 
				
			||||||
      sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5"
 | 
					      sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.5.3"
 | 
					    version: "2.5.4"
 | 
				
			||||||
  stack_trace:
 | 
					  stack_trace:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -959,10 +959,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: webview_flutter_android
 | 
					      name: webview_flutter_android
 | 
				
			||||||
      sha256: "3e5f4e9d818086b0d01a66fb1ff9cc72ab0cc58c71980e3d3661c5685ea0efb0"
 | 
					      sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.15.0"
 | 
					    version: "3.16.0"
 | 
				
			||||||
  webview_flutter_platform_interface:
 | 
					  webview_flutter_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -975,10 +975,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: webview_flutter_wkwebview
 | 
					      name: webview_flutter_wkwebview
 | 
				
			||||||
      sha256: "9bf168bccdf179ce90450b5f37e36fe263f591c9338828d6bf09b6f8d0f57f86"
 | 
					      sha256: f12f8d8a99784b863e8b85e4a9a5e3cf1839d6803d2c0c3e0533a8f3c5a992a7
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.12.0"
 | 
					    version: "3.13.0"
 | 
				
			||||||
  win32:
 | 
					  win32:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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: 1.0.5+2255 # When changing this, update the tag in main() accordingly
 | 
					version: 1.0.6+2256 # When changing this, update the tag in main() accordingly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: '>=3.0.0 <4.0.0'
 | 
					  sdk: '>=3.0.0 <4.0.0'
 | 
				
			||||||
@@ -38,7 +38,7 @@ dependencies:
 | 
				
			|||||||
  cupertino_icons: ^1.0.5
 | 
					  cupertino_icons: ^1.0.5
 | 
				
			||||||
  path_provider: ^2.0.11
 | 
					  path_provider: ^2.0.11
 | 
				
			||||||
  flutter_fgbg: ^0.3.0 # Try removing reliance on this
 | 
					  flutter_fgbg: ^0.3.0 # Try removing reliance on this
 | 
				
			||||||
  flutter_local_notifications: ^16.1.0
 | 
					  flutter_local_notifications: ^17.0.0
 | 
				
			||||||
  provider: ^6.0.3
 | 
					  provider: ^6.0.3
 | 
				
			||||||
  http: ^1.0.0
 | 
					  http: ^1.0.0
 | 
				
			||||||
  webview_flutter: ^4.0.0
 | 
					  webview_flutter: ^4.0.0
 | 
				
			||||||
@@ -66,7 +66,7 @@ dependencies:
 | 
				
			|||||||
  connectivity_plus: ^5.0.0
 | 
					  connectivity_plus: ^5.0.0
 | 
				
			||||||
  shared_storage: ^0.8.0
 | 
					  shared_storage: ^0.8.0
 | 
				
			||||||
  crypto: ^3.0.3
 | 
					  crypto: ^3.0.3
 | 
				
			||||||
  app_links: ^3.5.0
 | 
					  app_links: ^4.0.0
 | 
				
			||||||
  background_fetch: ^1.2.1
 | 
					  background_fetch: ^1.2.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user