mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-04 07:13:28 +01:00 
			
		
		
		
	Compare commits
	
		
			55 Commits
		
	
	
		
			v0.14.35-b
			...
			v0.14.40-b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b63a798d86 | ||
| 
						 | 
					eacf3777a4 | ||
| 
						 | 
					a5a7436bb1 | ||
| 
						 | 
					2a4cc35df7 | ||
| 
						 | 
					cdccf58b76 | ||
| 
						 | 
					27300383a1 | ||
| 
						 | 
					13066b3b4a | ||
| 
						 | 
					ccbe9d00c8 | ||
| 
						 | 
					ce291582cb | ||
| 
						 | 
					bb37bc3b51 | ||
| 
						 | 
					5a7747acd1 | ||
| 
						 | 
					1bc2ec9461 | ||
| 
						 | 
					2b977fc2b0 | ||
| 
						 | 
					cc4b016c64 | ||
| 
						 | 
					f64f561d6f | ||
| 
						 | 
					80bddf8a6b | ||
| 
						 | 
					cbaaec961c | ||
| 
						 | 
					5477b3f936 | ||
| 
						 | 
					fd59a93ede | ||
| 
						 | 
					cd316b7138 | ||
| 
						 | 
					d1955192ed | ||
| 
						 | 
					9beb839bf4 | ||
| 
						 | 
					29ea303093 | ||
| 
						 | 
					feff6751ca | ||
| 
						 | 
					ca33fdf752 | ||
| 
						 | 
					fdcdfe89d6 | ||
| 
						 | 
					48ed2115a7 | ||
| 
						 | 
					65988f4e08 | ||
| 
						 | 
					ede65eda6c | ||
| 
						 | 
					5da56acac8 | ||
| 
						 | 
					5720c55301 | ||
| 
						 | 
					ffefa4b30e | ||
| 
						 | 
					80e4986b23 | ||
| 
						 | 
					dc92ccda0a | ||
| 
						 | 
					f9bab18076 | ||
| 
						 | 
					2dec52e221 | ||
| 
						 | 
					7413f693d7 | ||
| 
						 | 
					415460df75 | ||
| 
						 | 
					125a194468 | ||
| 
						 | 
					32e9afbf36 | ||
| 
						 | 
					3eca704f4a | ||
| 
						 | 
					9c95129311 | ||
| 
						 | 
					bf34c1bcdb | ||
| 
						 | 
					284c687d77 | ||
| 
						 | 
					09afb5a3f5 | ||
| 
						 | 
					0138721451 | ||
| 
						 | 
					2d5f610941 | ||
| 
						 | 
					864fa7762b | ||
| 
						 | 
					4fde38ee6a | ||
| 
						 | 
					6cdf0f10d4 | ||
| 
						 | 
					b66592c25f | ||
| 
						 | 
					43616c566d | ||
| 
						 | 
					62f1dc17a0 | ||
| 
						 | 
					0e9a8a937a | ||
| 
						 | 
					9a86b245ce | 
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/bug_report.md
									
									
									
									
										vendored
									
									
								
							@@ -9,6 +9,7 @@ assignees: ''
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
**Prerequisites**
 | 
					**Prerequisites**
 | 
				
			||||||
<!-- Please ensure your request is not part of an existing issue. -->
 | 
					<!-- Please ensure your request is not part of an existing issue. -->
 | 
				
			||||||
 | 
					<!-- Please ensure you have checked the Obtainium Wiki. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Describe the bug**
 | 
					**Describe the bug**
 | 
				
			||||||
<!-- A clear and concise description of what the bug is. -->
 | 
					<!-- A clear and concise description of what the bug is. -->
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/ISSUE_TEMPLATE/feature_request.md
									
									
									
									
										vendored
									
									
								
							@@ -9,6 +9,7 @@ assignees: ''
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
**Prerequisites**
 | 
					**Prerequisites**
 | 
				
			||||||
<!-- Please ensure your request is not part of an existing issue. -->
 | 
					<!-- Please ensure your request is not part of an existing issue. -->
 | 
				
			||||||
 | 
					<!-- Please ensure you have checked the Obtainium Wiki. -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Describe the feature**
 | 
					**Describe the feature**
 | 
				
			||||||
<!-- A clear and concise description of what you want to happen.
 | 
					<!-- A clear and concise description of what you want to happen.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,8 @@ Obtainium allows you to install and update Apps directly from their releases pag
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
 | 
					Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wiki: [https://github.com/ImranR98/Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Currently supported App sources:
 | 
					Currently supported App sources:
 | 
				
			||||||
- Open Source - General:
 | 
					- Open Source - General:
 | 
				
			||||||
  - [GitHub](https://github.com/)
 | 
					  - [GitHub](https://github.com/)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,8 +28,15 @@
 | 
				
			|||||||
            <intent-filter>
 | 
					            <intent-filter>
 | 
				
			||||||
                <action
 | 
					                <action
 | 
				
			||||||
                    android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
 | 
					                    android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED"
 | 
				
			||||||
                    android:exported="false"/>
 | 
					                    android:exported="false" />
 | 
				
			||||||
            </intent-filter>
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					            <intent-filter>
 | 
				
			||||||
 | 
					                <action android:name="android.intent.action.VIEW" />
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.DEFAULT" />
 | 
				
			||||||
 | 
					                <category android:name="android.intent.category.BROWSABLE" />
 | 
				
			||||||
 | 
					                <data android:scheme="obtainium" />
 | 
				
			||||||
 | 
					            </intent-filter>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </activity>
 | 
					        </activity>
 | 
				
			||||||
        <!-- Don't delete the meta-data below.
 | 
					        <!-- Don't delete the meta-data below.
 | 
				
			||||||
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
 | 
					             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
 | 
				
			||||||
@@ -39,10 +46,10 @@
 | 
				
			|||||||
        <service
 | 
					        <service
 | 
				
			||||||
            android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
 | 
					            android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
 | 
				
			||||||
            android:permission="android.permission.BIND_JOB_SERVICE"
 | 
					            android:permission="android.permission.BIND_JOB_SERVICE"
 | 
				
			||||||
            android:exported="false"/>
 | 
					            android:exported="false" />
 | 
				
			||||||
        <receiver
 | 
					        <receiver
 | 
				
			||||||
            android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
 | 
					            android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
 | 
				
			||||||
            android:exported="false"/>
 | 
					            android:exported="false" />
 | 
				
			||||||
        <receiver
 | 
					        <receiver
 | 
				
			||||||
            android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
 | 
					            android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
 | 
				
			||||||
            android:enabled="false"
 | 
					            android:enabled="false"
 | 
				
			||||||
@@ -52,24 +59,24 @@
 | 
				
			|||||||
            </intent-filter>
 | 
					            </intent-filter>
 | 
				
			||||||
        </receiver>
 | 
					        </receiver>
 | 
				
			||||||
        <provider
 | 
					        <provider
 | 
				
			||||||
        android:name="androidx.core.content.FileProvider"
 | 
					            android:name="androidx.core.content.FileProvider"
 | 
				
			||||||
        android:authorities="dev.imranr.obtainium"
 | 
					            android:authorities="dev.imranr.obtainium"
 | 
				
			||||||
        android:grantUriPermissions="true">
 | 
					            android:grantUriPermissions="true">
 | 
				
			||||||
        <meta-data
 | 
					            <meta-data
 | 
				
			||||||
            android:name="android.support.FILE_PROVIDER_PATHS"
 | 
					                android:name="android.support.FILE_PROVIDER_PATHS"
 | 
				
			||||||
            android:resource="@xml/file_paths"/>
 | 
					                android:resource="@xml/file_paths" />
 | 
				
			||||||
        </provider>
 | 
					        </provider>
 | 
				
			||||||
    </application>
 | 
					    </application>
 | 
				
			||||||
    <uses-permission android:name="android.permission.INTERNET" />
 | 
					    <uses-permission android:name="android.permission.INTERNET" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
 | 
					    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
 | 
					    <uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
 | 
					    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 | 
					    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
 | 
					    <uses-permission android:name="android.permission.WAKE_LOCK" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
 | 
					    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
 | 
					    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
 | 
				
			||||||
    <uses-permission
 | 
					    <uses-permission
 | 
				
			||||||
        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
 | 
					        android:name="android.permission.WRITE_EXTERNAL_STORAGE"
 | 
				
			||||||
        android:maxSdkVersion="29"/>
 | 
					        android:maxSdkVersion="29" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 | 
					    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 | 
				
			||||||
</manifest>
 | 
					</manifest>
 | 
				
			||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
 | 
					<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
 | 
				
			||||||
    android:viewportWidth="142.129"
 | 
					    android:viewportWidth="142.129"
 | 
				
			||||||
    android:viewportHeight="142.129"
 | 
					    android:viewportHeight="142.129"
 | 
				
			||||||
    android:width="503.6066dp"
 | 
					    android:width="108dp"
 | 
				
			||||||
    android:height="503.6066dp">
 | 
					    android:height="108dp">
 | 
				
			||||||
    <group
 | 
					    <group
 | 
				
			||||||
        android:translateX="-30.39437"
 | 
					        android:translateX="-30.39437"
 | 
				
			||||||
        android:translateY="-54.68043">
 | 
					        android:translateY="-54.68043">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -88,7 +88,7 @@
 | 
				
			|||||||
    "importExport": "Uvoz/izvoz",
 | 
					    "importExport": "Uvoz/izvoz",
 | 
				
			||||||
    "settings": "Postavke",
 | 
					    "settings": "Postavke",
 | 
				
			||||||
    "exportedTo": "Izvezeno u {}",
 | 
					    "exportedTo": "Izvezeno u {}",
 | 
				
			||||||
    "obtainiumExport": "Obtainium Export",
 | 
					    "obtainiumExport": "Obtainium izvoz",
 | 
				
			||||||
    "invalidInput": "Neispravan unos.",
 | 
					    "invalidInput": "Neispravan unos.",
 | 
				
			||||||
    "importedX": "Uvezeno {}",
 | 
					    "importedX": "Uvezeno {}",
 | 
				
			||||||
    "obtainiumImport": "Obtainium uvoz",
 | 
					    "obtainiumImport": "Obtainium uvoz",
 | 
				
			||||||
@@ -134,7 +134,7 @@
 | 
				
			|||||||
    "close": "Zatvori",
 | 
					    "close": "Zatvori",
 | 
				
			||||||
    "share": "Podijeli",
 | 
					    "share": "Podijeli",
 | 
				
			||||||
    "appNotFound": "Aplikacija nije pronađena",
 | 
					    "appNotFound": "Aplikacija nije pronađena",
 | 
				
			||||||
    "obtainiumExportHyphenatedLowercase": "obtainium-export",
 | 
					    "obtainiumExportHyphenatedLowercase": "obtainium-izvoz",
 | 
				
			||||||
    "pickAnAPK": "Odaberite APK",
 | 
					    "pickAnAPK": "Odaberite APK",
 | 
				
			||||||
    "appHasMoreThanOnePackage": "{} ima više od jednog paketa:",
 | 
					    "appHasMoreThanOnePackage": "{} ima više od jednog paketa:",
 | 
				
			||||||
    "deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.",
 | 
					    "deviceSupportsXArch": "Vaš uređaj podržava {} arhitekturu procesora.",
 | 
				
			||||||
@@ -231,52 +231,56 @@
 | 
				
			|||||||
    "checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije",
 | 
					    "checkUpdateOnDetailPage": "Provjerite ima li novosti pri otvaranju stranice s detaljima aplikacije",
 | 
				
			||||||
    "disablePageTransitions": "Ugasite animaciju prijelaza stranice",
 | 
					    "disablePageTransitions": "Ugasite animaciju prijelaza stranice",
 | 
				
			||||||
    "reversePageTransitions": "Reverzne animacije prijelaza stranice",
 | 
					    "reversePageTransitions": "Reverzne animacije prijelaza stranice",
 | 
				
			||||||
    "minStarCount": "Minimum Star Count",
 | 
					    "minStarCount": "Najmanji broj zvjezdica",
 | 
				
			||||||
    "addInfoBelow": "Add this info below.",
 | 
					    "addInfoBelow": "Dodajte ove informacije ispod.",
 | 
				
			||||||
    "addInfoInSettings": "Add this info in the Settings.",
 | 
					    "addInfoInSettings": "Dodajte ove informacije u Postavkama.",
 | 
				
			||||||
    "githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
 | 
					    "githubSourceNote": "GitHub ograničavanje se može izbjeći korišćenjem tokena za lični pristup.",
 | 
				
			||||||
    "gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
 | 
					    "gitlabSourceNote": "GitLab APK preuzimanje možda neće raditi bez tokena za lični pristup.",
 | 
				
			||||||
    "sortByFileNamesNotLinks": "Sort by file names instead of full links",
 | 
					    "sortByFileNamesNotLinks": "Sortirajte po imenima datoteka umjesto po punim linkovima",
 | 
				
			||||||
    "filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
 | 
					    "filterReleaseNotesByRegEx": "Filtirajte promjene u izdanju po regularnom izrazu",
 | 
				
			||||||
    "customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
 | 
					    "customLinkFilterRegex": "Prilagođeni APK link filtrira se po regularnom izrazu (Zadano '.apk$')",
 | 
				
			||||||
    "appsPossiblyUpdated": "App Updates Attempted",
 | 
					    "appsPossiblyUpdated": "Pokušano ažuriranje aplikacija",
 | 
				
			||||||
    "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
 | 
					    "appsPossiblyUpdatedNotifDescription": "Obavještava korisnika da je ažuriranje jedne ili više aplikacija potencijalno izvršeno u pozadini",
 | 
				
			||||||
    "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
 | 
					    "xWasPossiblyUpdatedToY": "{} aplikacija bi trebala biti ažurirana na {}.",
 | 
				
			||||||
    "enableBackgroundUpdates": "Enable background updates",
 | 
					    "enableBackgroundUpdates": "Dozvolite ažuriranja u pozadini",
 | 
				
			||||||
    "backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
 | 
					    "backgroundUpdateReqsExplanation": "Ažuriranja u pozadini možda neće raditi za sve aplikacije.",
 | 
				
			||||||
    "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
 | 
					    "backgroundUpdateLimitsExplanation": "Uspjeh ažuriranja u pozadini se može provjeriti tek kada otvorite Obtainium.",
 | 
				
			||||||
    "verifyLatestTag": "Verify the 'latest' tag",
 | 
					    "verifyLatestTag": "Provjerite 'posljednu' ('latest') oznaku",
 | 
				
			||||||
    "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
 | 
					    "intermediateLinkRegex": "Filtrirajte da prvo posjetite 'Intemediate' link",
 | 
				
			||||||
    "intermediateLinkNotFound": "Intermediate link not found",
 | 
					    "intermediateLinkNotFound": "Intermediate link nije nađen",
 | 
				
			||||||
    "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
 | 
					    "exemptFromBackgroundUpdates": "Izuzmi iz ažuriranja u pozadini (ako su uključeni)",
 | 
				
			||||||
    "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
 | 
					    "bgUpdatesOnWiFiOnly": "Isključite ažuriranje u pozadini kada niste na WiFi-ju",
 | 
				
			||||||
    "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
 | 
					    "autoSelectHighestVersionCode": "Automatski izaberite najveću (verziju) versionCode APK-a",
 | 
				
			||||||
    "versionExtractionRegEx": "Version Extraction RegEx",
 | 
					    "versionExtractionRegEx": "RegEx ekstrakcija verzije",
 | 
				
			||||||
    "matchGroupToUse": "Match Group to Use",
 | 
					    "matchGroupToUse": "Podjesite grupu za upotebu",
 | 
				
			||||||
    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
					    "highlightTouchTargets": "Istaknite manje vidljive touch mete",
 | 
				
			||||||
    "pickExportDir": "Pick Export Directory",
 | 
					    "pickExportDir": "Izaberite datoteku za izvoz",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-export on changes",
 | 
					    "autoExportOnChanges": "Automatski izvezite pri promjenama",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
					    "filterVersionsByRegEx": "Filtrirajte verzije po regulatnom izrazu",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
 | 
				
			||||||
    "reverseSort": "Reverse sorting",
 | 
					    "dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
 | 
				
			||||||
    "debugMenu": "Debug Menu",
 | 
					    "reverseSort": "Obrni redosled",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
    "runBgCheckNow": "Run Background Update Check Now",
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
 | 
					    "debugMenu": "Meni za otkrivanje grešaka",
 | 
				
			||||||
    "installing": "Installing",
 | 
					    "bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.",
 | 
				
			||||||
    "skipUpdateNotifications": "Skip update notifications",
 | 
					    "runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad",
 | 
				
			||||||
 | 
					    "versionExtractWholePage": "Primjenite Regex ekstrakciju verzije na cijelu stranicu",
 | 
				
			||||||
 | 
					    "installing": "Instaliranje",
 | 
				
			||||||
 | 
					    "skipUpdateNotifications": "Ne prikazujte obavještenja ažuriranja",
 | 
				
			||||||
    "updatesAvailableNotifChannel": "Dostupna ažuriranja",
 | 
					    "updatesAvailableNotifChannel": "Dostupna ažuriranja",
 | 
				
			||||||
    "appsUpdatedNotifChannel": "Aplikacije su ažurirane",
 | 
					    "appsUpdatedNotifChannel": "Aplikacije su ažurirane",
 | 
				
			||||||
    "appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
 | 
					    "appsPossiblyUpdatedNotifChannel": "Pokušano ažuriranje aplikacija",
 | 
				
			||||||
    "errorCheckingUpdatesNotifChannel": "Greška pri provjeri ažuriranja",
 | 
					    "errorCheckingUpdatesNotifChannel": "Greška pri provjeri ažuriranja",
 | 
				
			||||||
    "appsRemovedNotifChannel": "Aplikacije su uklonjene",
 | 
					    "appsRemovedNotifChannel": "Aplikacije su uklonjene",
 | 
				
			||||||
    "downloadingXNotifChannel": "Preuzimanje {}",
 | 
					    "downloadingXNotifChannel": "Preuzimanje {}",
 | 
				
			||||||
    "completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
 | 
					    "completeAppInstallationNotifChannel": "Dovršite instalaciju aplikacije",
 | 
				
			||||||
    "checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
 | 
					    "checkingForUpdatesNotifChannel": "Tražim moguće nadogradnje",
 | 
				
			||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Podržite fiksne APK URL-ove",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Izaberite {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Želite li ukloniti aplikaciju?",
 | 
					        "one": "Želite li ukloniti aplikaciju?",
 | 
				
			||||||
        "other": "Želite li ukloniti aplikacije?"
 | 
					        "other": "Želite li ukloniti aplikacije?"
 | 
				
			||||||
@@ -326,7 +330,7 @@
 | 
				
			|||||||
        "other": "{} i još {} aplikacija je ažurirano."
 | 
					        "other": "{} i još {} aplikacija je ažurirano."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "xAndNMoreUpdatesPossiblyInstalled": {
 | 
					    "xAndNMoreUpdatesPossiblyInstalled": {
 | 
				
			||||||
        "one": "{} and 1 more app may have been updated.",
 | 
					        "one": "{} i još jedna aplikacija je vjerovatno ažurirana.",
 | 
				
			||||||
        "other": "{} and {} more apps may have been updated."
 | 
					        "other": "{} i još {} aplikacija su vjerovatno ažurirane."
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
 | 
					    "highlightTouchTargets": "Zvýraznit méně zjevné cíle dotyku",
 | 
				
			||||||
    "pickExportDir": "Vybrat adresář pro export",
 | 
					    "pickExportDir": "Vybrat adresář pro export",
 | 
				
			||||||
    "autoExportOnChanges": "Automatický export při změnách",
 | 
					    "autoExportOnChanges": "Automatický export při změnách",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
 | 
					    "filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
 | 
					    "trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "dontSortReleasesList": "Retain release order from API",
 | 
				
			||||||
    "reverseSort": "Reverse sorting",
 | 
					    "reverseSort": "Reverse sorting",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Debug Menu",
 | 
					    "debugMenu": "Debug Menu",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "bgTaskStarted": "Background task started - check logs.",
 | 
				
			||||||
    "runBgCheckNow": "Run Background Update Check Now",
 | 
					    "runBgCheckNow": "Run Background Update Check Now",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Odstranit Apku?",
 | 
					        "one": "Odstranit Apku?",
 | 
				
			||||||
        "other": "Odstranit Apky?"
 | 
					        "other": "Odstranit Apky?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
 | 
					    "highlightTouchTargets": "Weniger offensichtliche Touch-Ziele hervorheben",
 | 
				
			||||||
    "pickExportDir": "Export-Verzeichnis wählen",
 | 
					    "pickExportDir": "Export-Verzeichnis wählen",
 | 
				
			||||||
    "autoExportOnChanges": "Automatischer Export bei Änderung(en)",
 | 
					    "autoExportOnChanges": "Automatischer Export bei Änderung(en)",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
 | 
					    "filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
 | 
					    "trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
 | 
				
			||||||
    "dontSortReleasesList": "Freigaberelease von der API ordern",
 | 
					    "dontSortReleasesList": "Freigaberelease von der API ordern",
 | 
				
			||||||
    "reverseSort": "Umgekehrtes Sortieren",
 | 
					    "reverseSort": "Umgekehrtes Sortieren",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Debug-Menü",
 | 
					    "debugMenu": "Debug-Menü",
 | 
				
			||||||
    "bgTaskStarted": "Hintergrundaufgabe gestartet – Logs prüfen.",
 | 
					    "bgTaskStarted": "Hintergrundaufgabe gestartet – Logs prüfen.",
 | 
				
			||||||
    "runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
 | 
					    "runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
 | 
				
			||||||
@@ -275,8 +278,9 @@
 | 
				
			|||||||
    "completeAppInstallationNotifChannel": "App Installation abschließen",
 | 
					    "completeAppInstallationNotifChannel": "App Installation abschließen",
 | 
				
			||||||
    "checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
 | 
					    "checkingForUpdatesNotifChannel": "Nach Aktualisierungen suchen",
 | 
				
			||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Wähle {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "App entfernen?",
 | 
					        "one": "App entfernen?",
 | 
				
			||||||
        "other": "Apps entfernen?"
 | 
					        "other": "Apps entfernen?"
 | 
				
			||||||
@@ -329,4 +333,4 @@
 | 
				
			|||||||
        "one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
 | 
					        "one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
 | 
				
			||||||
        "other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
 | 
					        "other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
					    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
				
			||||||
    "pickExportDir": "Pick Export Directory",
 | 
					    "pickExportDir": "Pick Export Directory",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-export on changes",
 | 
					    "autoExportOnChanges": "Auto-export on changes",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
					    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
					    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "dontSortReleasesList": "Retain release order from API",
 | 
				
			||||||
    "reverseSort": "Reverse sorting",
 | 
					    "reverseSort": "Reverse sorting",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Debug Menu",
 | 
					    "debugMenu": "Debug Menu",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "bgTaskStarted": "Background task started - check logs.",
 | 
				
			||||||
    "runBgCheckNow": "Run Background Update Check Now",
 | 
					    "runBgCheckNow": "Run Background Update Check Now",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Remove App?",
 | 
					        "one": "Remove App?",
 | 
				
			||||||
        "other": "Remove Apps?"
 | 
					        "other": "Remove Apps?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,8 @@
 | 
				
			|||||||
    "githubPATLabel": "Token de Acceso Personal de GitHub (Reduce tiempos de espera)",
 | 
					    "githubPATLabel": "Token de Acceso Personal de GitHub (Reduce tiempos de espera)",
 | 
				
			||||||
    "includePrereleases": "Incluir versiones preliminares",
 | 
					    "includePrereleases": "Incluir versiones preliminares",
 | 
				
			||||||
    "fallbackToOlderReleases": "Retorceder a versiones previas",
 | 
					    "fallbackToOlderReleases": "Retorceder a versiones previas",
 | 
				
			||||||
    "filterReleaseTitlesByRegEx": "Filtra Títulos de Versiones mediantes Expresiones Regulares",
 | 
					    "filterReleaseTitlesByRegEx": "Filtrar Títulos de Versiones",
 | 
				
			||||||
    "invalidRegEx": "Expresión regular inválida",
 | 
					    "invalidRegEx": "Expresión inválida",
 | 
				
			||||||
    "noDescription": "Sin descripción",
 | 
					    "noDescription": "Sin descripción",
 | 
				
			||||||
    "cancel": "Cancelar",
 | 
					    "cancel": "Cancelar",
 | 
				
			||||||
    "continue": "Continuar",
 | 
					    "continue": "Continuar",
 | 
				
			||||||
@@ -30,7 +30,7 @@
 | 
				
			|||||||
    "app": "Aplicación",
 | 
					    "app": "Aplicación",
 | 
				
			||||||
    "appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.",
 | 
					    "appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.",
 | 
				
			||||||
    "youPickedTrackOnly": "Debes seleccionar la opción de 'Solo Seguimiento'.",
 | 
					    "youPickedTrackOnly": "Debes seleccionar la opción de 'Solo Seguimiento'.",
 | 
				
			||||||
    "trackOnlyAppDescription": "Se monitorizará la aplicación en busca de actualizaciones, pero Obtainium no será capaz de descargarla o acutalizarla.",
 | 
					    "trackOnlyAppDescription": "Se monitorizará la aplicación en busca de actualizaciones, pero Obtainium no será capaz de descargarla o actalizarla.",
 | 
				
			||||||
    "cancelled": "Cancelado",
 | 
					    "cancelled": "Cancelado",
 | 
				
			||||||
    "appAlreadyAdded": "Aplicación ya añadida",
 | 
					    "appAlreadyAdded": "Aplicación ya añadida",
 | 
				
			||||||
    "alreadyUpToDateQuestion": "¿Aplicación ya actualizada?",
 | 
					    "alreadyUpToDateQuestion": "¿Aplicación ya actualizada?",
 | 
				
			||||||
@@ -61,7 +61,7 @@
 | 
				
			|||||||
    "removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
 | 
					    "removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
 | 
				
			||||||
    "updateX": "Actualizar {}",
 | 
					    "updateX": "Actualizar {}",
 | 
				
			||||||
    "installX": "Instalar {}",
 | 
					    "installX": "Instalar {}",
 | 
				
			||||||
    "markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimient)\ncomo Actualizada",
 | 
					    "markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimiento)\ncomo Actualizada",
 | 
				
			||||||
    "changeX": "Cambiar {}",
 | 
					    "changeX": "Cambiar {}",
 | 
				
			||||||
    "installUpdateApps": "Instalar/Actualizar Aplicaciones",
 | 
					    "installUpdateApps": "Instalar/Actualizar Aplicaciones",
 | 
				
			||||||
    "installUpdateSelectedApps": "Instalar/Actualizar Aplicaciones Seleccionadas",
 | 
					    "installUpdateSelectedApps": "Instalar/Actualizar Aplicaciones Seleccionadas",
 | 
				
			||||||
@@ -72,7 +72,7 @@
 | 
				
			|||||||
    "pinToTop": "Fijar arriba",
 | 
					    "pinToTop": "Fijar arriba",
 | 
				
			||||||
    "unpinFromTop": "Desfijar de arriba",
 | 
					    "unpinFromTop": "Desfijar de arriba",
 | 
				
			||||||
    "resetInstallStatusForSelectedAppsQuestion": "¿Restuarar Estado de Instalación para las Aplicaciones Seleccionadas?",
 | 
					    "resetInstallStatusForSelectedAppsQuestion": "¿Restuarar Estado de Instalación para las Aplicaciones Seleccionadas?",
 | 
				
			||||||
    "installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de utilidad cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
 | 
					    "installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.",
 | 
				
			||||||
    "shareSelectedAppURLs": "Compartir URLs de las Aplicaciones Seleccionadas",
 | 
					    "shareSelectedAppURLs": "Compartir URLs de las Aplicaciones Seleccionadas",
 | 
				
			||||||
    "resetInstallStatus": "Restaurar Estado de Instalación",
 | 
					    "resetInstallStatus": "Restaurar Estado de Instalación",
 | 
				
			||||||
    "more": "Más",
 | 
					    "more": "Más",
 | 
				
			||||||
@@ -100,7 +100,7 @@
 | 
				
			|||||||
    "noResults": "Resultados no encontrados",
 | 
					    "noResults": "Resultados no encontrados",
 | 
				
			||||||
    "importX": "Importar {}",
 | 
					    "importX": "Importar {}",
 | 
				
			||||||
    "importedAppsIdDisclaimer": "Las Aplicaciones Importadas pueden mostrarse incorrectamente como \"No Instalada\".\nPara arreglar esto, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
 | 
					    "importedAppsIdDisclaimer": "Las Aplicaciones Importadas pueden mostrarse incorrectamente como \"No Instalada\".\nPara arreglar esto, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
 | 
				
			||||||
    "importErrors": "Import Errors",
 | 
					    "importErrors": "Errores de Importación",
 | 
				
			||||||
    "importedXOfYApps": "{} de {} Aplicaciones importadas.",
 | 
					    "importedXOfYApps": "{} de {} Aplicaciones importadas.",
 | 
				
			||||||
    "followingURLsHadErrors": "Las siguientes URLs tuvieron problemas:",
 | 
					    "followingURLsHadErrors": "Las siguientes URLs tuvieron problemas:",
 | 
				
			||||||
    "okay": "Correcto",
 | 
					    "okay": "Correcto",
 | 
				
			||||||
@@ -135,7 +135,7 @@
 | 
				
			|||||||
    "share": "Compartir",
 | 
					    "share": "Compartir",
 | 
				
			||||||
    "appNotFound": "Aplicación no encontrada",
 | 
					    "appNotFound": "Aplicación no encontrada",
 | 
				
			||||||
    "obtainiumExportHyphenatedLowercase": "obtainium-export",
 | 
					    "obtainiumExportHyphenatedLowercase": "obtainium-export",
 | 
				
			||||||
    "pickAnAPK": "Elige una APK",
 | 
					    "pickAnAPK": "Selecciona una APK",
 | 
				
			||||||
    "appHasMoreThanOnePackage": "{} tiene más de un paquete:",
 | 
					    "appHasMoreThanOnePackage": "{} tiene más de un paquete:",
 | 
				
			||||||
    "deviceSupportsXArch": "Tu dispositivo soporta las siguientes arquitecturas de procesador: {}.",
 | 
					    "deviceSupportsXArch": "Tu dispositivo soporta las siguientes arquitecturas de procesador: {}.",
 | 
				
			||||||
    "deviceSupportsFollowingArchs": "Tu dispositivo soporta las siguientes arquitecturas de procesador:",
 | 
					    "deviceSupportsFollowingArchs": "Tu dispositivo soporta las siguientes arquitecturas de procesador:",
 | 
				
			||||||
@@ -154,8 +154,8 @@
 | 
				
			|||||||
    "appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
 | 
					    "appsRemovedNotifDescription": "Notifica al usuario que una o más aplicaciones fueron eliminadas por problemas al cargarlas",
 | 
				
			||||||
    "xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
 | 
					    "xWasRemovedDueToErrorY": "{} ha sido eliminada por: {}",
 | 
				
			||||||
    "completeAppInstallation": "Instalación Completa de la Aplicación",
 | 
					    "completeAppInstallation": "Instalación Completa de la Aplicación",
 | 
				
			||||||
    "obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierta para instalar aplicaciones",
 | 
					    "obtainiumMustBeOpenToInstallApps": "Obtainium debe estar abierto para instalar aplicaciones",
 | 
				
			||||||
    "completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para teminar de instalar una aplicación",
 | 
					    "completeAppInstallationNotifDescription": "Pide al usuario volver a Obtainium para terminar de instalar una aplicación",
 | 
				
			||||||
    "checkingForUpdates": "Buscando Actualizaciones",
 | 
					    "checkingForUpdates": "Buscando Actualizaciones",
 | 
				
			||||||
    "checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
 | 
					    "checkingForUpdatesNotifDescription": "Notificación temporal que aparece al buscar actualizaciones",
 | 
				
			||||||
    "pleaseAllowInstallPerm": "Por favor, permite a Obtainium instalar aplicaciones",
 | 
					    "pleaseAllowInstallPerm": "Por favor, permite a Obtainium instalar aplicaciones",
 | 
				
			||||||
@@ -180,14 +180,14 @@
 | 
				
			|||||||
    "steamMobile": "Steam Mobile",
 | 
					    "steamMobile": "Steam Mobile",
 | 
				
			||||||
    "steamChat": "Steam Chat",
 | 
					    "steamChat": "Steam Chat",
 | 
				
			||||||
    "install": "Instalar",
 | 
					    "install": "Instalar",
 | 
				
			||||||
    "markInstalled": "Marcar como Instalda",
 | 
					    "markInstalled": "Marcar como Instalada",
 | 
				
			||||||
    "update": "Actualizar",
 | 
					    "update": "Actualizar",
 | 
				
			||||||
    "markUpdated": "Marcar como Actualizada",
 | 
					    "markUpdated": "Marcar como Actualizada",
 | 
				
			||||||
    "additionalOptions": "Opciones Adicionales",
 | 
					    "additionalOptions": "Opciones Adicionales",
 | 
				
			||||||
    "disableVersionDetection": "Descativar Detección de Versiones",
 | 
					    "disableVersionDetection": "Descativar Detección de Versiones",
 | 
				
			||||||
    "noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda no funcionar correctamente.",
 | 
					    "noVersionDetectionExplanation": "Esta opción solo se debe usar en aplicaciones en las que la deteción de versiones pueda no funcionar correctamente.",
 | 
				
			||||||
    "downloadingX": "Descargando {}",
 | 
					    "downloadingX": "Descargando {}",
 | 
				
			||||||
    "downloadNotifDescription": "Notifica al usuario de progreso de descarga de una aplicación",
 | 
					    "downloadNotifDescription": "Notifica al usuario del progreso de descarga de una aplicación",
 | 
				
			||||||
    "noAPKFound": "APK no encontrada",
 | 
					    "noAPKFound": "APK no encontrada",
 | 
				
			||||||
    "noVersionDetection": "Sin detección de versiones",
 | 
					    "noVersionDetection": "Sin detección de versiones",
 | 
				
			||||||
    "categorize": "Catogorizar",
 | 
					    "categorize": "Catogorizar",
 | 
				
			||||||
@@ -196,14 +196,14 @@
 | 
				
			|||||||
    "noCategory": "Sin Categoría",
 | 
					    "noCategory": "Sin Categoría",
 | 
				
			||||||
    "noCategories": "Sin Categorías",
 | 
					    "noCategories": "Sin Categorías",
 | 
				
			||||||
    "deleteCategoriesQuestion": "¿Borrar Categorías?",
 | 
					    "deleteCategoriesQuestion": "¿Borrar Categorías?",
 | 
				
			||||||
    "categoryDeleteWarning": "Todas las aplicaciones en las categorías borradas serán margadas como 'Sin Categoría'.",
 | 
					    "categoryDeleteWarning": "Todas las aplicaciones en las categorías borradas serán marcadas como 'Sin Categoría'.",
 | 
				
			||||||
    "addCategory": "Añadir Categoría",
 | 
					    "addCategory": "Añadir Categoría",
 | 
				
			||||||
    "label": "Nombre",
 | 
					    "label": "Nombre",
 | 
				
			||||||
    "language": "Idioma",
 | 
					    "language": "Idioma",
 | 
				
			||||||
    "copiedToClipboard": "Copiado al Portapapeles",
 | 
					    "copiedToClipboard": "Copiado al Portapapeles",
 | 
				
			||||||
    "storagePermissionDenied": "Permiso de Almacenamiento rechazado",
 | 
					    "storagePermissionDenied": "Permiso de Almacenamiento rechazado",
 | 
				
			||||||
    "selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaicones seleccionadas.",
 | 
					    "selectedCategorizeWarning": "Esto reemplazará cualquier ajuste de categoría para las aplicaciones seleccionadas.",
 | 
				
			||||||
    "filterAPKsByRegEx": "Filtrar APKs mediante Expresiones Regulares",
 | 
					    "filterAPKsByRegEx": "Filtrar por APKs",
 | 
				
			||||||
    "removeFromObtainium": "Eliminar de Obtainium",
 | 
					    "removeFromObtainium": "Eliminar de Obtainium",
 | 
				
			||||||
    "uninstallFromDevice": "Desinstalar del Dispositivo",
 | 
					    "uninstallFromDevice": "Desinstalar del Dispositivo",
 | 
				
			||||||
    "onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
 | 
					    "onlyWorksWithNonVersionDetectApps": "Solo funciona para aplicaciones con la detección de versiones desactivada.",
 | 
				
			||||||
@@ -211,72 +211,76 @@
 | 
				
			|||||||
    "releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
 | 
					    "releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
 | 
				
			||||||
    "changes": "Cambios",
 | 
					    "changes": "Cambios",
 | 
				
			||||||
    "releaseDate": "Fecha de Publicación",
 | 
					    "releaseDate": "Fecha de Publicación",
 | 
				
			||||||
    "importFromURLsInFile": "Importar de URls en un Archivo (como OPML)",
 | 
					    "importFromURLsInFile": "Importar de URls desde un Archivo (como OPML)",
 | 
				
			||||||
    "versionDetection": "Detección de Versiones",
 | 
					    "versionDetection": "Detección de Versiones",
 | 
				
			||||||
    "standardVersionDetection": "Detección de versiones estándar",
 | 
					    "standardVersionDetection": "Detección de versiones estándar",
 | 
				
			||||||
    "groupByCategory": "Agrupar por Categoría",
 | 
					    "groupByCategory": "Agrupar por Categoría",
 | 
				
			||||||
    "autoApkFilterByArch": "Tratar de filtrar las APKs mediante arquitecturas de procesador si es posible",
 | 
					    "autoApkFilterByArch": "Filtrar las APKs mediante arquitecturas de procesador, si es posible",
 | 
				
			||||||
    "overrideSource": "Sobrescribir Fuente",
 | 
					    "overrideSource": "Sobrescribir Fuente",
 | 
				
			||||||
    "dontShowAgain": "No mostrar de nuevo",
 | 
					    "dontShowAgain": "No mostrar de nuevo",
 | 
				
			||||||
    "dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'",
 | 
					    "dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'",
 | 
				
			||||||
    "dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APks",
 | 
					    "dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APks",
 | 
				
			||||||
    "moveNonInstalledAppsToBottom": "Move non-installed Apps to bottom of Apps view",
 | 
					    "moveNonInstalledAppsToBottom": "Mover las Apps no instaladas al final de la vista de Apps",
 | 
				
			||||||
    "gitlabPATLabel": "GitLab Personal Access Token\n(Enables Search and Better APK Discovery)",
 | 
					    "gitlabPATLabel": "Token GitLab de Acceso Personal\n(Habilita la Búsqueda y Mejor Detección de APKs)",
 | 
				
			||||||
    "about": "About",
 | 
					    "about": "Acerca",
 | 
				
			||||||
    "requiresCredentialsInSettings": "{}: This needs additional credentials (in Settings)",
 | 
					    "requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en Ajustes)",
 | 
				
			||||||
    "checkOnStart": "Check for updates on startup",
 | 
					    "checkOnStart": "Comprobar actualizaciones durante el inicio",
 | 
				
			||||||
    "tryInferAppIdFromCode": "Try inferring App ID from source code",
 | 
					    "tryInferAppIdFromCode": "Intentar deducir la ID de la APP por el código fuente",
 | 
				
			||||||
    "removeOnExternalUninstall": "Automatically remove externally uninstalled Apps",
 | 
					    "removeOnExternalUninstall": "Auto eliminar Apps desinstaladas externamente",
 | 
				
			||||||
    "pickHighestVersionCode": "Auto-select highest version code APK",
 | 
					    "pickHighestVersionCode": "Auto selección versión superior del código APK",
 | 
				
			||||||
    "checkUpdateOnDetailPage": "Check for updates on opening an App detail page",
 | 
					    "checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la App",
 | 
				
			||||||
    "disablePageTransitions": "Disable page transition animations",
 | 
					    "disablePageTransitions": "Deshabilitar animaciones de transición de la página",
 | 
				
			||||||
    "reversePageTransitions": "Reverse page transition animations",
 | 
					    "reversePageTransitions": "Invertir las animaciones de transición de la página",
 | 
				
			||||||
    "minStarCount": "Minimum Star Count",
 | 
					    "minStarCount": "Número Mínimo de Estrellas",
 | 
				
			||||||
    "addInfoBelow": "Add this info below.",
 | 
					    "addInfoBelow": "Añadir esta información debajo.",
 | 
				
			||||||
    "addInfoInSettings": "Add this info in the Settings.",
 | 
					    "addInfoInSettings": "Añadir esta información en Ajustes.",
 | 
				
			||||||
    "githubSourceNote": "GitHub rate limiting can be avoided using an API key.",
 | 
					    "githubSourceNote": "La limitación de velocidad de GitHub puede evitarse con una clave API.",
 | 
				
			||||||
    "gitlabSourceNote": "GitLab APK extraction may not work without an API key.",
 | 
					    "gitlabSourceNote": "La extracción de APK de GitLab podría no funcionar sin una clave API.",
 | 
				
			||||||
    "sortByFileNamesNotLinks": "Sort by file names instead of full links",
 | 
					    "sortByFileNamesNotLinks": "Ordenar por nombres de fichero en vez de por enlaces completos",
 | 
				
			||||||
    "filterReleaseNotesByRegEx": "Filter Release Notes by Regular Expression",
 | 
					    "filterReleaseNotesByRegEx": "Filtrar por Notas de Versión (Release Notes)",
 | 
				
			||||||
    "customLinkFilterRegex": "Custom APK Link Filter by Regular Expression (Default '.apk$')",
 | 
					    "customLinkFilterRegex": "Filtro personalizado de Enlace APK (por defecto '.apk$')",
 | 
				
			||||||
    "appsPossiblyUpdated": "App Updates Attempted",
 | 
					    "appsPossiblyUpdated": "Actualización de Apps intentada",
 | 
				
			||||||
    "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
 | 
					    "appsPossiblyUpdatedNotifDescription": "Notifica al usuario que las actualizaciones en segundo plano podrían haberse realizado para una o más aplicaciones",
 | 
				
			||||||
    "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
 | 
					    "xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.",
 | 
				
			||||||
    "enableBackgroundUpdates": "Enable background updates",
 | 
					    "enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
 | 
				
			||||||
    "backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
 | 
					    "backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
 | 
				
			||||||
    "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
 | 
					    "backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede verificar con Obtainium abierto.",
 | 
				
			||||||
    "verifyLatestTag": "Verify the 'latest' tag",
 | 
					    "verifyLatestTag": "Verifica la etiqueta 'latest'",
 | 
				
			||||||
    "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
 | 
					    "intermediateLinkRegex": "Filtrar por Enlace 'Intermedio' para Visitar Primero",
 | 
				
			||||||
    "intermediateLinkNotFound": "Intermediate link not found",
 | 
					    "intermediateLinkNotFound": "Enlace Intermedio no encontrado",
 | 
				
			||||||
    "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
 | 
					    "exemptFromBackgroundUpdates": "Exento de actualizciones en segundo plano (si están habilitadas)",
 | 
				
			||||||
    "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
 | 
					    "bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
 | 
				
			||||||
    "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
 | 
					    "autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior",
 | 
				
			||||||
    "versionExtractionRegEx": "Version Extraction RegEx",
 | 
					    "versionExtractionRegEx": "Versión de Extracción de RegEx",
 | 
				
			||||||
    "matchGroupToUse": "Match Group to Use",
 | 
					    "matchGroupToUse": "Match Group to Use",
 | 
				
			||||||
    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
					    "highlightTouchTargets": "Resaltar objetivos menos obvios",
 | 
				
			||||||
    "pickExportDir": "Pick Export Directory",
 | 
					    "pickExportDir": "Selecciona el Directorio para Exportar",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-export on changes",
 | 
					    "autoExportOnChanges": "Auto Exportar cuando haya cambios",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
					    "filterVersionsByRegEx": "Filtrar por Versiones",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "trySelectingSuggestedVersionCode": "Prueba seleccionando la versionCode APK sugerida",
 | 
				
			||||||
    "reverseSort": "Reverse sorting",
 | 
					    "dontSortReleasesList": "Mantener el order de publicación desde API",
 | 
				
			||||||
    "debugMenu": "Debug Menu",
 | 
					    "reverseSort": "Orden inverso",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
    "runBgCheckNow": "Run Background Update Check Now",
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
 | 
					    "debugMenu": "Menu Depurar",
 | 
				
			||||||
    "installing": "Installing",
 | 
					    "bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
 | 
				
			||||||
    "skipUpdateNotifications": "Skip update notifications",
 | 
					    "runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
 | 
				
			||||||
 | 
					    "versionExtractWholePage": "Aplicar la Versión de Extracción Regex a la Página Entera",
 | 
				
			||||||
 | 
					    "installing": "Instalando",
 | 
				
			||||||
 | 
					    "skipUpdateNotifications": "Omitir notificaciones sobre actualizaciones",
 | 
				
			||||||
    "updatesAvailableNotifChannel": "Actualizaciones Disponibles",
 | 
					    "updatesAvailableNotifChannel": "Actualizaciones Disponibles",
 | 
				
			||||||
    "appsUpdatedNotifChannel": "Aplicaciones Actualizadas",
 | 
					    "appsUpdatedNotifChannel": "Aplicaciones Actualizadas",
 | 
				
			||||||
    "appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
 | 
					    "appsPossiblyUpdatedNotifChannel": "Se ha Intentado Actualizar la Aplicación",
 | 
				
			||||||
    "errorCheckingUpdatesNotifChannel": "Error Buscando Actualizaciones",
 | 
					    "errorCheckingUpdatesNotifChannel": "Error Buscando Actualizaciones",
 | 
				
			||||||
    "appsRemovedNotifChannel": "Aplicaciones Eliminadas",
 | 
					    "appsRemovedNotifChannel": "Aplicaciones Eliminadas",
 | 
				
			||||||
    "downloadingXNotifChannel": "Descargando {}",
 | 
					    "downloadingXNotifChannel": "Descargando {}",
 | 
				
			||||||
    "completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
 | 
					    "completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
 | 
				
			||||||
    "checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
 | 
					    "checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
 | 
				
			||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Soporte para URLs fijas de APK",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Selecciona {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "¿Eliminar Aplicación?",
 | 
					        "one": "¿Eliminar Aplicación?",
 | 
				
			||||||
        "other": "¿Eliminar Aplicaciones?"
 | 
					        "other": "¿Eliminar Aplicaciones?"
 | 
				
			||||||
@@ -319,14 +323,14 @@
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    "xAndNMoreUpdatesAvailable": {
 | 
					    "xAndNMoreUpdatesAvailable": {
 | 
				
			||||||
        "one": "{} y 1 aplicación más tiene actualizaciones.",
 | 
					        "one": "{} y 1 aplicación más tiene actualizaciones.",
 | 
				
			||||||
        "other": "{} y {} aplicaciones más tiene actualizaciones."
 | 
					        "other": "{} y {} aplicaciones más tienen actualizaciones."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "xAndNMoreUpdatesInstalled": {
 | 
					    "xAndNMoreUpdatesInstalled": {
 | 
				
			||||||
        "one": "{} y 1 aplicación más han sido actualizadas.",
 | 
					        "one": "{} y 1 aplicación más han sido actualizadas.",
 | 
				
			||||||
        "other": "{} y {} aplicaciones más han sido actualizadas."
 | 
					        "other": "{} y {} aplicaciones más han sido actualizadas."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "xAndNMoreUpdatesPossiblyInstalled": {
 | 
					    "xAndNMoreUpdatesPossiblyInstalled": {
 | 
				
			||||||
        "one": "{} and 1 more app may have been updated.",
 | 
					        "one": "{} y 1 aplicación más podría haber sido actualizada.",
 | 
				
			||||||
        "other": "{} and {} more apps may have been updated."
 | 
					        "other": "{} y {} aplicaciones más podrían haber sido actualizadas."
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,44 +239,48 @@
 | 
				
			|||||||
    "sortByFileNamesNotLinks": "مرتب سازی بر اساس نام فایل به جای پیوندهای کامل",
 | 
					    "sortByFileNamesNotLinks": "مرتب سازی بر اساس نام فایل به جای پیوندهای کامل",
 | 
				
			||||||
    "filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
 | 
					    "filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید",
 | 
				
			||||||
    "customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیشفرض '.apk$')",
 | 
					    "customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیشفرض '.apk$')",
 | 
				
			||||||
    "appsPossiblyUpdated": "App Updates Attempted",
 | 
					    "appsPossiblyUpdated": "بهروزرسانی برنامه انجام شد",
 | 
				
			||||||
    "appsPossiblyUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were potentially applied in the background",
 | 
					    "appsPossiblyUpdatedNotifDescription": "به کاربر اطلاع میدهد که بهروزرسانیهای یک یا چند برنامه به طور بالقوه در پسزمینه اعمال شده است",
 | 
				
			||||||
    "xWasPossiblyUpdatedToY": "{} may have been updated to {}.",
 | 
					    "xWasPossiblyUpdatedToY": "ممکن است {} به {} به روز شده باشد.",
 | 
				
			||||||
    "enableBackgroundUpdates": "Enable background updates",
 | 
					    "enableBackgroundUpdates": "به روز رسانی پس زمینه را فعال کنید",
 | 
				
			||||||
    "backgroundUpdateReqsExplanation": "Background updates may not be possible for all apps.",
 | 
					    "backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.",
 | 
				
			||||||
    "backgroundUpdateLimitsExplanation": "The success of a background install can only be determined when Obtainium is opened.",
 | 
					    "backgroundUpdateLimitsExplanation": "موفقیت نصب پسزمینه تنها زمانی مشخص میشود که Obtainium باز شود.",
 | 
				
			||||||
    "verifyLatestTag": "Verify the 'latest' tag",
 | 
					    "verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید",
 | 
				
			||||||
    "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit First",
 | 
					    "intermediateLinkRegex": "برای اولین بار بازدید از لینک \"متوسط\" را فیلتر کنید",
 | 
				
			||||||
    "intermediateLinkNotFound": "Intermediate link not found",
 | 
					    "intermediateLinkNotFound": "لینک میانی پیدا نشد",
 | 
				
			||||||
    "exemptFromBackgroundUpdates": "Exempt from background updates (if enabled)",
 | 
					    "exemptFromBackgroundUpdates": "معاف از بهروزرسانیهای پسزمینه (در صورت فعال بودن)",
 | 
				
			||||||
    "bgUpdatesOnWiFiOnly": "Disable background updates when not on WiFi",
 | 
					    "bgUpdatesOnWiFiOnly": "بهروزرسانیهای پسزمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
 | 
				
			||||||
    "autoSelectHighestVersionCode": "Auto-select highest versionCode APK",
 | 
					    "autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK",
 | 
				
			||||||
    "versionExtractionRegEx": "Version Extraction RegEx",
 | 
					    "versionExtractionRegEx": "نسخه استخراج RegEx",
 | 
				
			||||||
    "matchGroupToUse": "Match Group to Use",
 | 
					    "matchGroupToUse": "گروه مورد استفاده را مطابقت دهید",
 | 
				
			||||||
    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
					    "highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید",
 | 
				
			||||||
    "pickExportDir": "Pick Export Directory",
 | 
					    "pickExportDir": "فهرست صادرات را انتخاب کنید",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-export on changes",
 | 
					    "autoExportOnChanges": "صادرات خودکار تغییرات",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
					    "filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
 | 
				
			||||||
    "reverseSort": "Reverse sorting",
 | 
					    "dontSortReleasesList": "حفظ سفارش انتشار از API",
 | 
				
			||||||
    "debugMenu": "Debug Menu",
 | 
					    "reverseSort": "مرتب سازی معکوس",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
    "runBgCheckNow": "Run Background Update Check Now",
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
 | 
					    "debugMenu": "منوی اشکال زدایی",
 | 
				
			||||||
    "installing": "Installing",
 | 
					    "bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.",
 | 
				
			||||||
    "skipUpdateNotifications": "Skip update notifications",
 | 
					    "runBgCheckNow": "اکنون بهروزرسانی پسزمینه را بررسی کنید",
 | 
				
			||||||
 | 
					    "versionExtractWholePage": "نسخه Extraction Regex را در کل صفحه اعمال کنید",
 | 
				
			||||||
 | 
					    "installing": "در حال نصب",
 | 
				
			||||||
 | 
					    "skipUpdateNotifications": "رد شدن از اعلان های به روز رسانی",
 | 
				
			||||||
    "updatesAvailableNotifChannel": "بروزرسانی در دسترس ",
 | 
					    "updatesAvailableNotifChannel": "بروزرسانی در دسترس ",
 | 
				
			||||||
    "appsUpdatedNotifChannel": "برنامه ها به روز شدند",
 | 
					    "appsUpdatedNotifChannel": "برنامه ها به روز شدند",
 | 
				
			||||||
    "appsPossiblyUpdatedNotifChannel": "App Updates Attempted",
 | 
					    "appsPossiblyUpdatedNotifChannel": "بهروزرسانی برنامه انجام شد",
 | 
				
			||||||
    "errorCheckingUpdatesNotifChannel": "خطا در بررسی بهروزرسانیها",
 | 
					    "errorCheckingUpdatesNotifChannel": "خطا در بررسی بهروزرسانیها",
 | 
				
			||||||
    "appsRemovedNotifChannel": "برنامه ها حذف شدند",
 | 
					    "appsRemovedNotifChannel": "برنامه ها حذف شدند",
 | 
				
			||||||
    "downloadingXNotifChannel": "در حال دانلود {}",
 | 
					    "downloadingXNotifChannel": "در حال دانلود {}",
 | 
				
			||||||
    "completeAppInstallationNotifChannel": "نصب کامل برنامه",
 | 
					    "completeAppInstallationNotifChannel": "نصب کامل برنامه",
 | 
				
			||||||
    "checkingForUpdatesNotifChannel": "بررسی بهروزرسانیها",
 | 
					    "checkingForUpdatesNotifChannel": "بررسی بهروزرسانیها",
 | 
				
			||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "انتخاب کنید {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "برنامه حذف شود؟",
 | 
					        "one": "برنامه حذف شود؟",
 | 
				
			||||||
        "other": "برنامه ها حذف شوند؟"
 | 
					        "other": "برنامه ها حذف شوند؟"
 | 
				
			||||||
@@ -326,7 +330,7 @@
 | 
				
			|||||||
        "other": "{} و {} برنامه دیگر به روز شدند."
 | 
					        "other": "{} و {} برنامه دیگر به روز شدند."
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "xAndNMoreUpdatesPossiblyInstalled": {
 | 
					    "xAndNMoreUpdatesPossiblyInstalled": {
 | 
				
			||||||
        "one": "{} and 1 more app may have been updated.",
 | 
					        "one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.",
 | 
				
			||||||
        "other": "{} and {} more apps may have been updated."
 | 
					        "other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند."
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
					    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
				
			||||||
    "pickExportDir": "Pick Export Directory",
 | 
					    "pickExportDir": "Pick Export Directory",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-export on changes",
 | 
					    "autoExportOnChanges": "Auto-export on changes",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
					    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
					    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "dontSortReleasesList": "Retain release order from API",
 | 
				
			||||||
    "reverseSort": "Reverse sorting",
 | 
					    "reverseSort": "Reverse sorting",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Debug Menu",
 | 
					    "debugMenu": "Debug Menu",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "bgTaskStarted": "Background task started - check logs.",
 | 
				
			||||||
    "runBgCheckNow": "Run Background Update Check Now",
 | 
					    "runBgCheckNow": "Run Background Update Check Now",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Supprimer l'application ?",
 | 
					        "one": "Supprimer l'application ?",
 | 
				
			||||||
        "other": "Supprimer les applications ?"
 | 
					        "other": "Supprimer les applications ?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -215,7 +215,7 @@
 | 
				
			|||||||
    "versionDetection": "Verzió érzékelés",
 | 
					    "versionDetection": "Verzió érzékelés",
 | 
				
			||||||
    "standardVersionDetection": "Alapért. verzió érzékelés",
 | 
					    "standardVersionDetection": "Alapért. verzió érzékelés",
 | 
				
			||||||
    "groupByCategory": "Csoportosítás Kategória alapján",
 | 
					    "groupByCategory": "Csoportosítás Kategória alapján",
 | 
				
			||||||
    "autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-okat",
 | 
					    "autoApkFilterByArch": "Ha lehetséges, próbálja CPU architektúra szerint szűrni az APK-kat",
 | 
				
			||||||
    "overrideSource": "Forrás felülbírálása",
 | 
					    "overrideSource": "Forrás felülbírálása",
 | 
				
			||||||
    "dontShowAgain": "Ne mutassa ezt újra",
 | 
					    "dontShowAgain": "Ne mutassa ezt újra",
 | 
				
			||||||
    "dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
 | 
					    "dontShowTrackOnlyWarnings": "Ne jelenítsen meg 'Csak nyomon követés' figyelmeztetést",
 | 
				
			||||||
@@ -255,10 +255,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
 | 
					    "highlightTouchTargets": "Emelje ki a kevésbé nyilvánvaló érintési célokat",
 | 
				
			||||||
    "pickExportDir": "Válassza az Exportálási könyvtárat",
 | 
					    "pickExportDir": "Válassza az Exportálási könyvtárat",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-exportálás a változások után",
 | 
					    "autoExportOnChanges": "Auto-exportálás a változások után",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
 | 
					    "filterVersionsByRegEx": "Verziók szűrése reguláris kifejezéssel",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
 | 
					    "trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
 | 
				
			||||||
    "dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
 | 
					    "dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
 | 
				
			||||||
    "reverseSort": "Fordított rendezés",
 | 
					    "reverseSort": "Fordított rendezés",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Hibakereső menü",
 | 
					    "debugMenu": "Hibakereső menü",
 | 
				
			||||||
    "bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.",
 | 
					    "bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.",
 | 
				
			||||||
    "enableBackgroundUpdates": "Frissítések a háttérben",
 | 
					    "enableBackgroundUpdates": "Frissítések a háttérben",
 | 
				
			||||||
@@ -275,8 +278,9 @@
 | 
				
			|||||||
    "completeAppInstallationNotifChannel": "Teljes app telepítés",
 | 
					    "completeAppInstallationNotifChannel": "Teljes app telepítés",
 | 
				
			||||||
    "checkingForUpdatesNotifChannel": "Frissítések keresése",
 | 
					    "checkingForUpdatesNotifChannel": "Frissítések keresése",
 | 
				
			||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Csak a telepített és a csak követhető appokat ellenőrizze frissítésekért",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Kiválaszt {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Eltávolítja az alkalmazást?",
 | 
					        "one": "Eltávolítja az alkalmazást?",
 | 
				
			||||||
        "other": "Eltávolítja az alkalmazást?"
 | 
					        "other": "Eltávolítja az alkalmazást?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
 | 
					    "highlightTouchTargets": "Evidenzia elementi toccabili meno ovvi",
 | 
				
			||||||
    "pickExportDir": "Scegli cartella esp.",
 | 
					    "pickExportDir": "Scegli cartella esp.",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-esporta dopo modifiche",
 | 
					    "autoExportOnChanges": "Auto-esporta dopo modifiche",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filtra versioni con espressione regolare",
 | 
					    "filterVersionsByRegEx": "Filtra versioni con espressione regolare",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
 | 
					    "trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
 | 
				
			||||||
    "dontSortReleasesList": "Conserva l'ordine di release da API",
 | 
					    "dontSortReleasesList": "Conserva l'ordine di release da API",
 | 
				
			||||||
    "reverseSort": "Ordine inverso",
 | 
					    "reverseSort": "Ordine inverso",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Menu di debug",
 | 
					    "debugMenu": "Menu di debug",
 | 
				
			||||||
    "bgTaskStarted": "Attività in secondo piano iniziata - controllo log.",
 | 
					    "bgTaskStarted": "Attività in secondo piano iniziata - controllo log.",
 | 
				
			||||||
    "runBgCheckNow": "Inizia aggiornamento in secondo piano ora",
 | 
					    "runBgCheckNow": "Inizia aggiornamento in secondo piano ora",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Rimuovere l'app?",
 | 
					        "one": "Rimuovere l'app?",
 | 
				
			||||||
        "other": "Rimuovere le app?"
 | 
					        "other": "Rimuovere le app?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
 | 
					    "highlightTouchTargets": "目立たないタップ可能な対象をハイライトする",
 | 
				
			||||||
    "pickExportDir": "エクスポートディレクトリを選択",
 | 
					    "pickExportDir": "エクスポートディレクトリを選択",
 | 
				
			||||||
    "autoExportOnChanges": "変更があった際に自動でエクスポートする",
 | 
					    "autoExportOnChanges": "変更があった際に自動でエクスポートする",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
 | 
					    "filterVersionsByRegEx": "正規表現でバージョンをフィルタリングする",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
 | 
					    "trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
 | 
				
			||||||
    "dontSortReleasesList": "APIからのリリース順を保持する",
 | 
					    "dontSortReleasesList": "APIからのリリース順を保持する",
 | 
				
			||||||
    "reverseSort": "逆順ソート",
 | 
					    "reverseSort": "逆順ソート",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "デバッグメニュー",
 | 
					    "debugMenu": "デバッグメニュー",
 | 
				
			||||||
    "bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
 | 
					    "bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
 | 
				
			||||||
    "runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
 | 
					    "runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "アプリを削除しますか?",
 | 
					        "one": "アプリを削除しますか?",
 | 
				
			||||||
        "other": "アプリを削除しますか?"
 | 
					        "other": "アプリを削除しますか?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
 | 
					    "highlightTouchTargets": "Markeer minder voor de hand liggende aanraakdoelen.",
 | 
				
			||||||
    "pickExportDir": "Kies de exportmap",
 | 
					    "pickExportDir": "Kies de exportmap",
 | 
				
			||||||
    "autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
 | 
					    "autoExportOnChanges": "Automatisch exporteren bij wijzigingen",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter versies met een reguliere expressie",
 | 
					    "filterVersionsByRegEx": "Filter versies met een reguliere expressie",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
 | 
					    "trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
 | 
				
			||||||
    "dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
 | 
					    "dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
 | 
				
			||||||
    "reverseSort": "Sortering omkeren",
 | 
					    "reverseSort": "Sortering omkeren",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Debug menu",
 | 
					    "debugMenu": "Debug menu",
 | 
				
			||||||
    "bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
 | 
					    "bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
 | 
				
			||||||
    "runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
 | 
					    "runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "App verwijderen?",
 | 
					        "one": "App verwijderen?",
 | 
				
			||||||
        "other": "Apps verwijderen?"
 | 
					        "other": "Apps verwijderen?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
 | 
					    "highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
 | 
				
			||||||
    "pickExportDir": "Wybierz katalog eksportu",
 | 
					    "pickExportDir": "Wybierz katalog eksportu",
 | 
				
			||||||
    "autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
 | 
					    "autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
 | 
					    "filterVersionsByRegEx": "Filtruj wersje według wyrażenia regularnego",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
 | 
					    "trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
 | 
				
			||||||
    "dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
 | 
					    "dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
 | 
				
			||||||
    "reverseSort": "Odwrotne sortowanie",
 | 
					    "reverseSort": "Odwrotne sortowanie",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Menu debugowania",
 | 
					    "debugMenu": "Menu debugowania",
 | 
				
			||||||
    "bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
 | 
					    "bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
 | 
				
			||||||
    "runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
 | 
					    "runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
 | 
				
			||||||
@@ -275,8 +278,9 @@
 | 
				
			|||||||
    "completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
 | 
					    "completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
 | 
				
			||||||
    "checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
 | 
					    "checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
 | 
				
			||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Wybierz {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Usunąć aplikację?",
 | 
					        "one": "Usunąć aplikację?",
 | 
				
			||||||
        "few": "Usunąć aplikacje?",
 | 
					        "few": "Usunąć aplikacje?",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Destaque areas de toque menos óbvias",
 | 
					    "highlightTouchTargets": "Destaque areas de toque menos óbvias",
 | 
				
			||||||
    "pickExportDir": "Escolher Diretorio de Exportação",
 | 
					    "pickExportDir": "Escolher Diretorio de Exportação",
 | 
				
			||||||
    "autoExportOnChanges": "Auto-exportar em mudanças",
 | 
					    "autoExportOnChanges": "Auto-exportar em mudanças",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
 | 
					    "filterVersionsByRegEx": "Filtrar Versões por Expressão Regular",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
 | 
					    "trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
 | 
				
			||||||
    "dontSortReleasesList": "Reter a ordem de lançamento da API",
 | 
					    "dontSortReleasesList": "Reter a ordem de lançamento da API",
 | 
				
			||||||
    "reverseSort": "Ordenação reversa",
 | 
					    "reverseSort": "Ordenação reversa",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Menu Debug",
 | 
					    "debugMenu": "Menu Debug",
 | 
				
			||||||
    "bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
 | 
					    "bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
 | 
				
			||||||
    "runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
 | 
					    "runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Remover App?",
 | 
					        "one": "Remover App?",
 | 
				
			||||||
        "other": "Remover Apps?"
 | 
					        "other": "Remover Apps?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
 | 
					    "highlightTouchTargets": "Выделить менее очевидные элементы управления касанием",
 | 
				
			||||||
    "pickExportDir": "Выбрать каталог для экспорта",
 | 
					    "pickExportDir": "Выбрать каталог для экспорта",
 | 
				
			||||||
    "autoExportOnChanges": "Автоэкспорт при изменениях",
 | 
					    "autoExportOnChanges": "Автоэкспорт при изменениях",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
 | 
					    "filterVersionsByRegEx": "Фильтровать версии по регулярному выражению",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
 | 
					    "trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
 | 
				
			||||||
    "dontSortReleasesList": "Сохранить порядок релизов от API",
 | 
					    "dontSortReleasesList": "Сохранить порядок релизов от API",
 | 
				
			||||||
    "reverseSort": "Обратная сортировка",
 | 
					    "reverseSort": "Обратная сортировка",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Меню отладки",
 | 
					    "debugMenu": "Меню отладки",
 | 
				
			||||||
    "bgTaskStarted": "Фоновая задача начата — проверьте журналы",
 | 
					    "bgTaskStarted": "Фоновая задача начата — проверьте журналы",
 | 
				
			||||||
    "runBgCheckNow": "Запустить проверку фонового обновления сейчас",
 | 
					    "runBgCheckNow": "Запустить проверку фонового обновления сейчас",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Удалить приложение?",
 | 
					        "one": "Удалить приложение?",
 | 
				
			||||||
        "other": "Удалить приложения?"
 | 
					        "other": "Удалить приложения?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,13 +256,17 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
					    "highlightTouchTargets": "Highlight less obvious touch targets",
 | 
				
			||||||
    "pickExportDir": "Välj Exportsökväg",
 | 
					    "pickExportDir": "Välj Exportsökväg",
 | 
				
			||||||
    "autoExportOnChanges": "Automatisk export vid ändringar",
 | 
					    "autoExportOnChanges": "Automatisk export vid ändringar",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
					    "filterVersionsByRegEx": "Filter Versions by Regular Expression",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
					    "trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
 | 
				
			||||||
    "dontSortReleasesList": "Retain release order from API",
 | 
					    "dontSortReleasesList": "Retain release order from API",
 | 
				
			||||||
    "reverseSort": "Omvänd sortering",
 | 
					    "reverseSort": "Omvänd sortering",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Felsökningsmeny",
 | 
					    "debugMenu": "Felsökningsmeny",
 | 
				
			||||||
    "bgTaskStarted": "Background task started - check logs.",
 | 
					    "bgTaskStarted": "Background task started - check logs.",
 | 
				
			||||||
    "runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
 | 
					    "runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Ta Bort App?",
 | 
					        "one": "Ta Bort App?",
 | 
				
			||||||
        "other": "Ta Bort Appar?"
 | 
					        "other": "Ta Bort Appar?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
 | 
					    "highlightTouchTargets": "Daha az belirgin dokunma hedeflerini vurgula",
 | 
				
			||||||
    "pickExportDir": "Dışa Aktarılacak Klasörü Seç",
 | 
					    "pickExportDir": "Dışa Aktarılacak Klasörü Seç",
 | 
				
			||||||
    "autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
 | 
					    "autoExportOnChanges": "Değişikliklerde otomatik olarak dışa aktar",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
 | 
					    "filterVersionsByRegEx": "Sürümleri Düzenli İfade ile Filtrele",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
 | 
					    "trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
 | 
				
			||||||
    "dontSortReleasesList": "API'den sıralama düzenini koru",
 | 
					    "dontSortReleasesList": "API'den sıralama düzenini koru",
 | 
				
			||||||
    "reverseSort": "Ters sıralama",
 | 
					    "reverseSort": "Ters sıralama",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Hata Ayıklama Menüsü",
 | 
					    "debugMenu": "Hata Ayıklama Menüsü",
 | 
				
			||||||
    "bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
 | 
					    "bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
 | 
				
			||||||
    "runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
 | 
					    "runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Uygulamayı Kaldır?",
 | 
					        "one": "Uygulamayı Kaldır?",
 | 
				
			||||||
        "other": "Uygulamaları Kaldır?"
 | 
					        "other": "Uygulamaları Kaldır?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
 | 
					    "highlightTouchTargets": "Đánh dấu các mục tiêu cảm ứng ít rõ ràng hơn",
 | 
				
			||||||
    "pickExportDir": "Chọn thư mục xuất",
 | 
					    "pickExportDir": "Chọn thư mục xuất",
 | 
				
			||||||
    "autoExportOnChanges": "Tự động xuất khi thay đổi",
 | 
					    "autoExportOnChanges": "Tự động xuất khi thay đổi",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
 | 
					    "filterVersionsByRegEx": "Lọc phiên bản theo biểu thức chính quy",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
 | 
					    "trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
 | 
				
			||||||
    "dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
 | 
					    "dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
 | 
				
			||||||
    "reverseSort": "Sắp xếp ngược",
 | 
					    "reverseSort": "Sắp xếp ngược",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "Danh sách gỡ lỗi",
 | 
					    "debugMenu": "Danh sách gỡ lỗi",
 | 
				
			||||||
    "bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
 | 
					    "bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
 | 
				
			||||||
    "runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
 | 
					    "runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion":{
 | 
					    "removeAppQuestion":{
 | 
				
			||||||
        "one": "Gỡ ứng dụng?",
 | 
					        "one": "Gỡ ứng dụng?",
 | 
				
			||||||
        "other": "Gỡ ứng dụng?"
 | 
					        "other": "Gỡ ứng dụng?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -256,10 +256,13 @@
 | 
				
			|||||||
    "highlightTouchTargets": "突出展示不明显的触摸区域",
 | 
					    "highlightTouchTargets": "突出展示不明显的触摸区域",
 | 
				
			||||||
    "pickExportDir": "选择导出文件夹",
 | 
					    "pickExportDir": "选择导出文件夹",
 | 
				
			||||||
    "autoExportOnChanges": "数据变更时自动导出",
 | 
					    "autoExportOnChanges": "数据变更时自动导出",
 | 
				
			||||||
 | 
					    "includeSettings": "Include settings",
 | 
				
			||||||
    "filterVersionsByRegEx": "筛选版本号(正则表达式)",
 | 
					    "filterVersionsByRegEx": "筛选版本号(正则表达式)",
 | 
				
			||||||
    "trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
 | 
					    "trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
 | 
				
			||||||
    "dontSortReleasesList": "保持来自 API 的发行顺序",
 | 
					    "dontSortReleasesList": "保持来自 API 的发行顺序",
 | 
				
			||||||
    "reverseSort": "反转排序",
 | 
					    "reverseSort": "反转排序",
 | 
				
			||||||
 | 
					    "takeFirstLink": "Take first link",
 | 
				
			||||||
 | 
					    "skipSort": "Skip sorting",
 | 
				
			||||||
    "debugMenu": "调试选项",
 | 
					    "debugMenu": "调试选项",
 | 
				
			||||||
    "bgTaskStarted": "后台任务已启动 - 详见日志",
 | 
					    "bgTaskStarted": "后台任务已启动 - 详见日志",
 | 
				
			||||||
    "runBgCheckNow": "立即进行后台更新检查",
 | 
					    "runBgCheckNow": "立即进行后台更新检查",
 | 
				
			||||||
@@ -277,6 +280,7 @@
 | 
				
			|||||||
    "onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
 | 
					    "onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
 | 
				
			||||||
    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
					    "supportFixedAPKURL": "Support fixed APK URLs",
 | 
				
			||||||
    "selectX": "Select {}",
 | 
					    "selectX": "Select {}",
 | 
				
			||||||
 | 
					    "parallelDownloads": "Allow parallel downloads",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "是否删除应用?",
 | 
					        "one": "是否删除应用?",
 | 
				
			||||||
        "other": "是否删除应用?"
 | 
					        "other": "是否删除应用?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,7 +65,7 @@ class FDroid extends AppSource {
 | 
				
			|||||||
  ) async {
 | 
					  ) async {
 | 
				
			||||||
    String? appId = await tryInferringAppId(standardUrl);
 | 
					    String? appId = await tryInferringAppId(standardUrl);
 | 
				
			||||||
    String host = Uri.parse(standardUrl).host;
 | 
					    String host = Uri.parse(standardUrl).host;
 | 
				
			||||||
    return getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
					    var details = getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
				
			||||||
        await sourceRequest('https://$host/api/v1/packages/$appId'),
 | 
					        await sourceRequest('https://$host/api/v1/packages/$appId'),
 | 
				
			||||||
        'https://$host/repo/$appId',
 | 
					        'https://$host/repo/$appId',
 | 
				
			||||||
        standardUrl,
 | 
					        standardUrl,
 | 
				
			||||||
@@ -80,6 +80,23 @@ class FDroid extends AppSource {
 | 
				
			|||||||
                    true
 | 
					                    true
 | 
				
			||||||
                ? additionalSettings['filterVersionsByRegEx']
 | 
					                ? additionalSettings['filterVersionsByRegEx']
 | 
				
			||||||
                : null);
 | 
					                : null);
 | 
				
			||||||
 | 
					    if (!hostChanged) {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        var res = await sourceRequest(
 | 
				
			||||||
 | 
					            'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml');
 | 
				
			||||||
 | 
					        String author = res.body
 | 
				
			||||||
 | 
					            .split('\n')
 | 
				
			||||||
 | 
					            .where((l) => l.startsWith('AuthorName: '))
 | 
				
			||||||
 | 
					            .first
 | 
				
			||||||
 | 
					            .split(': ')
 | 
				
			||||||
 | 
					            .sublist(1)
 | 
				
			||||||
 | 
					            .join(': ');
 | 
				
			||||||
 | 
					        details.names.author = author;
 | 
				
			||||||
 | 
					      } catch (e) {
 | 
				
			||||||
 | 
					        // Fail silently
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return details;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -111,79 +128,79 @@ class FDroid extends AppSource {
 | 
				
			|||||||
      throw getObtainiumHttpError(res);
 | 
					      throw getObtainiumHttpError(res);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
					  APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
				
			||||||
    Response res, String apkUrlPrefix, String standardUrl, String sourceName,
 | 
					      Response res, String apkUrlPrefix, String standardUrl, String sourceName,
 | 
				
			||||||
    {bool autoSelectHighestVersionCode = false,
 | 
					      {bool autoSelectHighestVersionCode = false,
 | 
				
			||||||
    bool trySelectingSuggestedVersionCode = false,
 | 
					      bool trySelectingSuggestedVersionCode = false,
 | 
				
			||||||
    String? filterVersionsByRegEx}) {
 | 
					      String? filterVersionsByRegEx}) {
 | 
				
			||||||
  if (res.statusCode == 200) {
 | 
					    if (res.statusCode == 200) {
 | 
				
			||||||
    var response = jsonDecode(res.body);
 | 
					      var response = jsonDecode(res.body);
 | 
				
			||||||
    List<dynamic> releases = response['packages'] ?? [];
 | 
					      List<dynamic> releases = response['packages'] ?? [];
 | 
				
			||||||
    if (releases.isEmpty) {
 | 
					      if (releases.isEmpty) {
 | 
				
			||||||
      throw NoReleasesError();
 | 
					        throw NoReleasesError();
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    String? version;
 | 
					 | 
				
			||||||
    Iterable<dynamic> releaseChoices = [];
 | 
					 | 
				
			||||||
    // Grab the versionCode suggested if the user chose to do that
 | 
					 | 
				
			||||||
    // Only do so at this stage if the user has no release filter
 | 
					 | 
				
			||||||
    if (trySelectingSuggestedVersionCode &&
 | 
					 | 
				
			||||||
        response['suggestedVersionCode'] != null &&
 | 
					 | 
				
			||||||
        filterVersionsByRegEx == null) {
 | 
					 | 
				
			||||||
      var suggestedReleases = releases.where((element) =>
 | 
					 | 
				
			||||||
          element['versionCode'] == response['suggestedVersionCode']);
 | 
					 | 
				
			||||||
      if (suggestedReleases.isNotEmpty) {
 | 
					 | 
				
			||||||
        releaseChoices = suggestedReleases;
 | 
					 | 
				
			||||||
        version = suggestedReleases.first['versionName'];
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					      String? version;
 | 
				
			||||||
    // Apply the release filter if any
 | 
					      Iterable<dynamic> releaseChoices = [];
 | 
				
			||||||
    if (filterVersionsByRegEx?.isNotEmpty == true) {
 | 
					      // Grab the versionCode suggested if the user chose to do that
 | 
				
			||||||
      version = null;
 | 
					      // Only do so at this stage if the user has no release filter
 | 
				
			||||||
      releaseChoices = [];
 | 
					      if (trySelectingSuggestedVersionCode &&
 | 
				
			||||||
      for (var i = 0; i < releases.length; i++) {
 | 
					          response['suggestedVersionCode'] != null &&
 | 
				
			||||||
        if (RegExp(filterVersionsByRegEx!)
 | 
					          filterVersionsByRegEx == null) {
 | 
				
			||||||
            .hasMatch(releases[i]['versionName'])) {
 | 
					        var suggestedReleases = releases.where((element) =>
 | 
				
			||||||
          version = releases[i]['versionName'];
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      if (version == null) {
 | 
					 | 
				
			||||||
        throw NoVersionError();
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // Default to the highest version
 | 
					 | 
				
			||||||
    version ??= releases[0]['versionName'];
 | 
					 | 
				
			||||||
    if (version == null) {
 | 
					 | 
				
			||||||
      throw NoVersionError();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // If a suggested release was not already picked, pick all those with the selected version
 | 
					 | 
				
			||||||
    if (releaseChoices.isEmpty) {
 | 
					 | 
				
			||||||
      releaseChoices =
 | 
					 | 
				
			||||||
          releases.where((element) => element['versionName'] == version);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // For the remaining releases, use the toggles to auto-select one if possible
 | 
					 | 
				
			||||||
    if (releaseChoices.length > 1) {
 | 
					 | 
				
			||||||
      if (autoSelectHighestVersionCode) {
 | 
					 | 
				
			||||||
        releaseChoices = [releaseChoices.first];
 | 
					 | 
				
			||||||
      } else if (trySelectingSuggestedVersionCode &&
 | 
					 | 
				
			||||||
          response['suggestedVersionCode'] != null) {
 | 
					 | 
				
			||||||
        var suggestedReleases = releaseChoices.where((element) =>
 | 
					 | 
				
			||||||
            element['versionCode'] == response['suggestedVersionCode']);
 | 
					            element['versionCode'] == response['suggestedVersionCode']);
 | 
				
			||||||
        if (suggestedReleases.isNotEmpty) {
 | 
					        if (suggestedReleases.isNotEmpty) {
 | 
				
			||||||
          releaseChoices = suggestedReleases;
 | 
					          releaseChoices = suggestedReleases;
 | 
				
			||||||
 | 
					          version = suggestedReleases.first['versionName'];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      // Apply the release filter if any
 | 
				
			||||||
 | 
					      if (filterVersionsByRegEx?.isNotEmpty == true) {
 | 
				
			||||||
 | 
					        version = null;
 | 
				
			||||||
 | 
					        releaseChoices = [];
 | 
				
			||||||
 | 
					        for (var i = 0; i < releases.length; i++) {
 | 
				
			||||||
 | 
					          if (RegExp(filterVersionsByRegEx!)
 | 
				
			||||||
 | 
					              .hasMatch(releases[i]['versionName'])) {
 | 
				
			||||||
 | 
					            version = releases[i]['versionName'];
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (version == null) {
 | 
				
			||||||
 | 
					          throw NoVersionError();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // Default to the highest version
 | 
				
			||||||
 | 
					      version ??= releases[0]['versionName'];
 | 
				
			||||||
 | 
					      if (version == null) {
 | 
				
			||||||
 | 
					        throw NoVersionError();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // If a suggested release was not already picked, pick all those with the selected version
 | 
				
			||||||
 | 
					      if (releaseChoices.isEmpty) {
 | 
				
			||||||
 | 
					        releaseChoices =
 | 
				
			||||||
 | 
					            releases.where((element) => element['versionName'] == version);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      // For the remaining releases, use the toggles to auto-select one if possible
 | 
				
			||||||
 | 
					      if (releaseChoices.length > 1) {
 | 
				
			||||||
 | 
					        if (autoSelectHighestVersionCode) {
 | 
				
			||||||
 | 
					          releaseChoices = [releaseChoices.first];
 | 
				
			||||||
 | 
					        } else if (trySelectingSuggestedVersionCode &&
 | 
				
			||||||
 | 
					            response['suggestedVersionCode'] != null) {
 | 
				
			||||||
 | 
					          var suggestedReleases = releaseChoices.where((element) =>
 | 
				
			||||||
 | 
					              element['versionCode'] == response['suggestedVersionCode']);
 | 
				
			||||||
 | 
					          if (suggestedReleases.isNotEmpty) {
 | 
				
			||||||
 | 
					            releaseChoices = suggestedReleases;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (releaseChoices.isEmpty) {
 | 
				
			||||||
 | 
					        throw NoReleasesError();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      List<String> apkUrls = releaseChoices
 | 
				
			||||||
 | 
					          .map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
 | 
				
			||||||
 | 
					          .toList();
 | 
				
			||||||
 | 
					      return APKDetails(version, getApkUrlsFromUrls(apkUrls.toSet().toList()),
 | 
				
			||||||
 | 
					          AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw getObtainiumHttpError(res);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (releaseChoices.isEmpty) {
 | 
					 | 
				
			||||||
      throw NoReleasesError();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    List<String> apkUrls = releaseChoices
 | 
					 | 
				
			||||||
        .map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk')
 | 
					 | 
				
			||||||
        .toList();
 | 
					 | 
				
			||||||
    return APKDetails(version, getApkUrlsFromUrls(apkUrls.toSet().toList()),
 | 
					 | 
				
			||||||
        AppNames(sourceName, Uri.parse(standardUrl).pathSegments.last));
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    throw getObtainiumHttpError(res);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -234,7 +234,7 @@ class GitHub extends AppSource {
 | 
				
			|||||||
    bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
 | 
					    bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
 | 
				
			||||||
    bool dontSortReleasesList =
 | 
					    bool dontSortReleasesList =
 | 
				
			||||||
        additionalSettings['dontSortReleasesList'] == true;
 | 
					        additionalSettings['dontSortReleasesList'] == true;
 | 
				
			||||||
    String? latestTag;
 | 
					    dynamic latestRelease;
 | 
				
			||||||
    if (verifyLatestTag) {
 | 
					    if (verifyLatestTag) {
 | 
				
			||||||
      var temp = requestUrl.split('?');
 | 
					      var temp = requestUrl.split('?');
 | 
				
			||||||
      Response res = await sourceRequest(
 | 
					      Response res = await sourceRequest(
 | 
				
			||||||
@@ -245,12 +245,20 @@ class GitHub extends AppSource {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        throw getObtainiumHttpError(res);
 | 
					        throw getObtainiumHttpError(res);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      var jsres = jsonDecode(res.body);
 | 
					      latestRelease = jsonDecode(res.body);
 | 
				
			||||||
      latestTag = jsres['tag_name'] ?? jsres['name'];
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Response res = await sourceRequest(requestUrl);
 | 
					    Response res = await sourceRequest(requestUrl);
 | 
				
			||||||
    if (res.statusCode == 200) {
 | 
					    if (res.statusCode == 200) {
 | 
				
			||||||
      var releases = jsonDecode(res.body) as List<dynamic>;
 | 
					      var releases = jsonDecode(res.body) as List<dynamic>;
 | 
				
			||||||
 | 
					      if (latestRelease != null) {
 | 
				
			||||||
 | 
					        var latestTag = latestRelease['tag_name'] ?? latestRelease['name'];
 | 
				
			||||||
 | 
					        if (releases
 | 
				
			||||||
 | 
					            .where((element) =>
 | 
				
			||||||
 | 
					                (element['tag_name'] ?? element['name']) == latestTag)
 | 
				
			||||||
 | 
					            .isEmpty) {
 | 
				
			||||||
 | 
					          releases = [latestRelease, ...releases];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
 | 
					      List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
 | 
				
			||||||
          (release['assets'] as List<dynamic>?)
 | 
					          (release['assets'] as List<dynamic>?)
 | 
				
			||||||
@@ -299,13 +307,13 @@ class GitHub extends AppSource {
 | 
				
			|||||||
          }
 | 
					          }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (latestTag != null &&
 | 
					      if (latestRelease != null &&
 | 
				
			||||||
          releases.isNotEmpty &&
 | 
					          releases.isNotEmpty &&
 | 
				
			||||||
          latestTag !=
 | 
					          latestRelease !=
 | 
				
			||||||
              (releases[releases.length - 1]['tag_name'] ??
 | 
					              (releases[releases.length - 1]['tag_name'] ??
 | 
				
			||||||
                  releases[0]['name'])) {
 | 
					                  releases[0]['name'])) {
 | 
				
			||||||
        var ind = releases.indexWhere(
 | 
					        var ind = releases.indexWhere((element) =>
 | 
				
			||||||
            (element) => latestTag == (element['tag_name'] ?? element['name']));
 | 
					            latestRelease == (element['tag_name'] ?? element['name']));
 | 
				
			||||||
        if (ind >= 0) {
 | 
					        if (ind >= 0) {
 | 
				
			||||||
          releases.add(releases.removeAt(ind));
 | 
					          releases.add(releases.removeAt(ind));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,12 +48,6 @@ class GitLab extends AppSource {
 | 
				
			|||||||
            label: tr('fallbackToOlderReleases'), defaultValue: true)
 | 
					            label: tr('fallbackToOlderReleases'), defaultValue: true)
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    searchQuerySettingFormItems = [
 | 
					 | 
				
			||||||
      GeneratedFormTextField('PAT',
 | 
					 | 
				
			||||||
          label: tr('gitlabPATLabel').split('(')[0],
 | 
					 | 
				
			||||||
          password: true,
 | 
					 | 
				
			||||||
          required: false)
 | 
					 | 
				
			||||||
    ];
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
@@ -86,18 +80,8 @@ class GitLab extends AppSource {
 | 
				
			|||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Future<Map<String, List<String>>> search(String query,
 | 
					  Future<Map<String, List<String>>> search(String query,
 | 
				
			||||||
      {Map<String, dynamic> querySettings = const {}}) async {
 | 
					      {Map<String, dynamic> querySettings = const {}}) async {
 | 
				
			||||||
    String? PAT;
 | 
					 | 
				
			||||||
    if (!hostChanged) {
 | 
					 | 
				
			||||||
      PAT = await getPATIfAny({});
 | 
					 | 
				
			||||||
      if (PAT == null) {
 | 
					 | 
				
			||||||
        throw CredsNeededError(name);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if ((querySettings['PAT'] as String?)?.isNotEmpty == true) {
 | 
					 | 
				
			||||||
      PAT = querySettings['PAT'];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    var url =
 | 
					    var url =
 | 
				
			||||||
        'https://$host/api/v4/search?${PAT?.isNotEmpty == true ? 'private_token=$PAT&' : ''}scope=projects&search=${Uri.encodeQueryComponent(query)}';
 | 
					        'https://$host/api/v4/projects?search=${Uri.encodeQueryComponent(query)}';
 | 
				
			||||||
    var res = await sourceRequest(url);
 | 
					    var res = await sourceRequest(url);
 | 
				
			||||||
    if (res.statusCode != 200) {
 | 
					    if (res.statusCode != 200) {
 | 
				
			||||||
      throw getObtainiumHttpError(res);
 | 
					      throw getObtainiumHttpError(res);
 | 
				
			||||||
@@ -203,7 +187,7 @@ class GitLab extends AppSource {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (apkDetailsList.isEmpty) {
 | 
					    if (apkDetailsList.isEmpty) {
 | 
				
			||||||
      throw NoReleasesError();
 | 
					      throw NoReleasesError(note: tr('gitlabSourceNote'));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (fallbackToOlderReleases) {
 | 
					    if (fallbackToOlderReleases) {
 | 
				
			||||||
      if (additionalSettings['trackOnly'] != true) {
 | 
					      if (additionalSettings['trackOnly'] != true) {
 | 
				
			||||||
@@ -211,7 +195,7 @@ class GitLab extends AppSource {
 | 
				
			|||||||
            apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
 | 
					            apkDetailsList.where((e) => e.apkUrls.isNotEmpty).toList();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (apkDetailsList.isEmpty) {
 | 
					      if (apkDetailsList.isEmpty) {
 | 
				
			||||||
        throw NoReleasesError();
 | 
					        throw NoReleasesError(note: tr('gitlabSourceNote'));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return apkDetailsList.first;
 | 
					    return apkDetailsList.first;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -94,7 +94,8 @@ class HTML extends AppSource {
 | 
				
			|||||||
        GeneratedFormSwitch('sortByFileNamesNotLinks',
 | 
					        GeneratedFormSwitch('sortByFileNamesNotLinks',
 | 
				
			||||||
            label: tr('sortByFileNamesNotLinks'))
 | 
					            label: tr('sortByFileNamesNotLinks'))
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      [GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
 | 
					      [GeneratedFormSwitch('skipSort', label: tr('skipSort'))],
 | 
				
			||||||
 | 
					      [GeneratedFormSwitch('reverseSort', label: tr('takeTopLink'))],
 | 
				
			||||||
      [
 | 
					      [
 | 
				
			||||||
        GeneratedFormSwitch('supportFixedAPKURL',
 | 
					        GeneratedFormSwitch('supportFixedAPKURL',
 | 
				
			||||||
            defaultValue: true, label: tr('supportFixedAPKURL')),
 | 
					            defaultValue: true, label: tr('supportFixedAPKURL')),
 | 
				
			||||||
@@ -185,12 +186,15 @@ class HTML extends AppSource {
 | 
				
			|||||||
            .toList();
 | 
					            .toList();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      List<String> links = [];
 | 
					      List<String> links = [];
 | 
				
			||||||
 | 
					      bool skipSort = additionalSettings['skipSort'] == true;
 | 
				
			||||||
      if ((additionalSettings['intermediateLinkRegex'] as String?)
 | 
					      if ((additionalSettings['intermediateLinkRegex'] as String?)
 | 
				
			||||||
              ?.isNotEmpty ==
 | 
					              ?.isNotEmpty ==
 | 
				
			||||||
          true) {
 | 
					          true) {
 | 
				
			||||||
        var reg = RegExp(additionalSettings['intermediateLinkRegex']);
 | 
					        var reg = RegExp(additionalSettings['intermediateLinkRegex']);
 | 
				
			||||||
        links = allLinks.where((element) => reg.hasMatch(element)).toList();
 | 
					        links = allLinks.where((element) => reg.hasMatch(element)).toList();
 | 
				
			||||||
        links.sort((a, b) => compareAlphaNumeric(a, b));
 | 
					        if (!skipSort) {
 | 
				
			||||||
 | 
					          links.sort((a, b) => compareAlphaNumeric(a, b));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (links.isEmpty) {
 | 
					        if (links.isEmpty) {
 | 
				
			||||||
          throw ObtainiumError(tr('intermediateLinkNotFound'));
 | 
					          throw ObtainiumError(tr('intermediateLinkNotFound'));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -211,10 +215,14 @@ class HTML extends AppSource {
 | 
				
			|||||||
                Uri.parse(element).path.toLowerCase().endsWith('.apk'))
 | 
					                Uri.parse(element).path.toLowerCase().endsWith('.apk'))
 | 
				
			||||||
            .toList();
 | 
					            .toList();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      links.sort((a, b) => additionalSettings['sortByFileNamesNotLinks'] == true
 | 
					      if (!skipSort) {
 | 
				
			||||||
          ? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last,
 | 
					        links.sort((a, b) =>
 | 
				
			||||||
              b.split('/').where((e) => e.isNotEmpty).last)
 | 
					            additionalSettings['sortByFileNamesNotLinks'] == true
 | 
				
			||||||
          : compareAlphaNumeric(a, b));
 | 
					                ? compareAlphaNumeric(
 | 
				
			||||||
 | 
					                    a.split('/').where((e) => e.isNotEmpty).last,
 | 
				
			||||||
 | 
					                    b.split('/').where((e) => e.isNotEmpty).last)
 | 
				
			||||||
 | 
					                : compareAlphaNumeric(a, b));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      if (additionalSettings['reverseSort'] == true) {
 | 
					      if (additionalSettings['reverseSort'] == true) {
 | 
				
			||||||
        links = links.reversed.toList();
 | 
					        links = links.reversed.toList();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,7 @@ class IzzyOnDroid extends AppSource {
 | 
				
			|||||||
    Map<String, dynamic> additionalSettings,
 | 
					    Map<String, dynamic> additionalSettings,
 | 
				
			||||||
  ) async {
 | 
					  ) async {
 | 
				
			||||||
    String? appId = await tryInferringAppId(standardUrl);
 | 
					    String? appId = await tryInferringAppId(standardUrl);
 | 
				
			||||||
    return getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
					    return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
 | 
				
			||||||
        await sourceRequest(
 | 
					        await sourceRequest(
 | 
				
			||||||
            'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
 | 
					            'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
 | 
				
			||||||
        'https://android.izzysoft.de/frepo/$appId',
 | 
					        'https://android.izzysoft.de/frepo/$appId',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,9 @@ class CredsNeededError extends ObtainiumError {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoReleasesError extends ObtainiumError {
 | 
					class NoReleasesError extends ObtainiumError {
 | 
				
			||||||
  NoReleasesError() : super(tr('noReleaseFound'));
 | 
					  NoReleasesError({String? note})
 | 
				
			||||||
 | 
					      : super(
 | 
				
			||||||
 | 
					            '${tr('noReleaseFound')}${note?.isNotEmpty == true ? '\n\n$note' : ''}');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoAPKError extends ObtainiumError {
 | 
					class NoAPKError extends ObtainiumError {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
 | 
				
			|||||||
// ignore: implementation_imports
 | 
					// ignore: implementation_imports
 | 
				
			||||||
import 'package:easy_localization/src/localization.dart';
 | 
					import 'package:easy_localization/src/localization.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const String currentVersion = '0.14.35';
 | 
					const String currentVersion = '0.14.40';
 | 
				
			||||||
const String currentReleaseTag =
 | 
					const String currentReleaseTag =
 | 
				
			||||||
    'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
 | 
					    'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,10 +21,10 @@ class AddAppPage extends StatefulWidget {
 | 
				
			|||||||
  const AddAppPage({super.key});
 | 
					  const AddAppPage({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  State<AddAppPage> createState() => _AddAppPageState();
 | 
					  State<AddAppPage> createState() => AddAppPageState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _AddAppPageState extends State<AddAppPage> {
 | 
					class AddAppPageState extends State<AddAppPage> {
 | 
				
			||||||
  bool gettingAppInfo = false;
 | 
					  bool gettingAppInfo = false;
 | 
				
			||||||
  bool searching = false;
 | 
					  bool searching = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,9 +36,62 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
				
			|||||||
  bool additionalSettingsValid = true;
 | 
					  bool additionalSettingsValid = true;
 | 
				
			||||||
  bool inferAppIdIfOptional = true;
 | 
					  bool inferAppIdIfOptional = true;
 | 
				
			||||||
  List<String> pickedCategories = [];
 | 
					  List<String> pickedCategories = [];
 | 
				
			||||||
  int searchnum = 0;
 | 
					  int urlInputKey = 0;
 | 
				
			||||||
  SourceProvider sourceProvider = SourceProvider();
 | 
					  SourceProvider sourceProvider = SourceProvider();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  linkFn(String input) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      if (input.isEmpty) {
 | 
				
			||||||
 | 
					        throw UnsupportedURLError();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      sourceProvider.getSource(input);
 | 
				
			||||||
 | 
					      changeUserInput(input, true, false, updateUrlInput: true);
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      showError(e, context);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  changeUserInput(String input, bool valid, bool isBuilding,
 | 
				
			||||||
 | 
					      {bool updateUrlInput = false}) {
 | 
				
			||||||
 | 
					    userInput = input;
 | 
				
			||||||
 | 
					    if (!isBuilding) {
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        if (updateUrlInput) {
 | 
				
			||||||
 | 
					          urlInputKey++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var prevHost = pickedSource?.host;
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					          var naturalSource =
 | 
				
			||||||
 | 
					              valid ? sourceProvider.getSource(userInput) : null;
 | 
				
			||||||
 | 
					          if (naturalSource != null &&
 | 
				
			||||||
 | 
					              naturalSource.runtimeType.toString() !=
 | 
				
			||||||
 | 
					                  HTML().runtimeType.toString()) {
 | 
				
			||||||
 | 
					            // If input has changed to match a regular source, reset the override
 | 
				
			||||||
 | 
					            pickedSourceOverride = null;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					          // ignore
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        var source = valid
 | 
				
			||||||
 | 
					            ? sourceProvider.getSource(userInput,
 | 
				
			||||||
 | 
					                overrideSource: pickedSourceOverride)
 | 
				
			||||||
 | 
					            : null;
 | 
				
			||||||
 | 
					        if (pickedSource.runtimeType != source.runtimeType ||
 | 
				
			||||||
 | 
					            (prevHost != null && prevHost != source?.host)) {
 | 
				
			||||||
 | 
					          pickedSource = source;
 | 
				
			||||||
 | 
					          additionalSettings = source != null
 | 
				
			||||||
 | 
					              ? getDefaultValuesFromFormItems(
 | 
				
			||||||
 | 
					                  source.combinedAppSpecificSettingFormItems)
 | 
				
			||||||
 | 
					              : {};
 | 
				
			||||||
 | 
					          additionalSettingsValid = source != null
 | 
				
			||||||
 | 
					              ? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
 | 
				
			||||||
 | 
					              : true;
 | 
				
			||||||
 | 
					          inferAppIdIfOptional = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    AppsProvider appsProvider = context.read<AppsProvider>();
 | 
					    AppsProvider appsProvider = context.read<AppsProvider>();
 | 
				
			||||||
@@ -48,47 +101,6 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bool doingSomething = gettingAppInfo || searching;
 | 
					    bool doingSomething = gettingAppInfo || searching;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    changeUserInput(String input, bool valid, bool isBuilding,
 | 
					 | 
				
			||||||
        {bool isSearch = false}) {
 | 
					 | 
				
			||||||
      userInput = input;
 | 
					 | 
				
			||||||
      if (!isBuilding) {
 | 
					 | 
				
			||||||
        setState(() {
 | 
					 | 
				
			||||||
          if (isSearch) {
 | 
					 | 
				
			||||||
            searchnum++;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          var prevHost = pickedSource?.host;
 | 
					 | 
				
			||||||
          try {
 | 
					 | 
				
			||||||
            var naturalSource =
 | 
					 | 
				
			||||||
                valid ? sourceProvider.getSource(userInput) : null;
 | 
					 | 
				
			||||||
            if (naturalSource != null &&
 | 
					 | 
				
			||||||
                naturalSource.runtimeType.toString() !=
 | 
					 | 
				
			||||||
                    HTML().runtimeType.toString()) {
 | 
					 | 
				
			||||||
              // If input has changed to match a regular source, reset the override
 | 
					 | 
				
			||||||
              pickedSourceOverride = null;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          } catch (e) {
 | 
					 | 
				
			||||||
            // ignore
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          var source = valid
 | 
					 | 
				
			||||||
              ? sourceProvider.getSource(userInput,
 | 
					 | 
				
			||||||
                  overrideSource: pickedSourceOverride)
 | 
					 | 
				
			||||||
              : null;
 | 
					 | 
				
			||||||
          if (pickedSource.runtimeType != source.runtimeType ||
 | 
					 | 
				
			||||||
              (prevHost != null && prevHost != source?.host)) {
 | 
					 | 
				
			||||||
            pickedSource = source;
 | 
					 | 
				
			||||||
            additionalSettings = source != null
 | 
					 | 
				
			||||||
                ? getDefaultValuesFromFormItems(
 | 
					 | 
				
			||||||
                    source.combinedAppSpecificSettingFormItems)
 | 
					 | 
				
			||||||
                : {};
 | 
					 | 
				
			||||||
            additionalSettingsValid = source != null
 | 
					 | 
				
			||||||
                ? !sourceProvider.ifRequiredAppSpecificSettingsExist(source)
 | 
					 | 
				
			||||||
                : true;
 | 
					 | 
				
			||||||
            inferAppIdIfOptional = true;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
 | 
					    Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly,
 | 
				
			||||||
        {bool ignoreHideSetting = false}) async {
 | 
					        {bool ignoreHideSetting = false}) async {
 | 
				
			||||||
      var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
 | 
					      var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly;
 | 
				
			||||||
@@ -205,7 +217,7 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
				
			|||||||
          children: [
 | 
					          children: [
 | 
				
			||||||
            Expanded(
 | 
					            Expanded(
 | 
				
			||||||
                child: GeneratedForm(
 | 
					                child: GeneratedForm(
 | 
				
			||||||
                    key: Key(searchnum.toString()),
 | 
					                    key: Key(urlInputKey.toString()),
 | 
				
			||||||
                    items: [
 | 
					                    items: [
 | 
				
			||||||
                      [
 | 
					                      [
 | 
				
			||||||
                        GeneratedFormTextField('appSourceURL',
 | 
					                        GeneratedFormTextField('appSourceURL',
 | 
				
			||||||
@@ -325,7 +337,7 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
				
			|||||||
                    );
 | 
					                    );
 | 
				
			||||||
                  });
 | 
					                  });
 | 
				
			||||||
          if (selectedUrls != null && selectedUrls.isNotEmpty) {
 | 
					          if (selectedUrls != null && selectedUrls.isNotEmpty) {
 | 
				
			||||||
            changeUserInput(selectedUrls[0], true, false, isSearch: true);
 | 
					            changeUserInput(selectedUrls[0], true, false, updateUrlInput: true);
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } catch (e) {
 | 
					      } catch (e) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,6 +145,29 @@ class _AppPageState extends State<AppPage> {
 | 
				
			|||||||
                    appsProvider.saveApps([app.app]);
 | 
					                    appsProvider.saveApps([app.app]);
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
                }),
 | 
					                }),
 | 
				
			||||||
 | 
					            if (app?.app.additionalSettings['about'] is String &&
 | 
				
			||||||
 | 
					                app?.app.additionalSettings['about'].isNotEmpty)
 | 
				
			||||||
 | 
					              Column(
 | 
				
			||||||
 | 
					                children: [
 | 
				
			||||||
 | 
					                  const SizedBox(
 | 
				
			||||||
 | 
					                    height: 48,
 | 
				
			||||||
 | 
					                  ),
 | 
				
			||||||
 | 
					                  GestureDetector(
 | 
				
			||||||
 | 
					                    onLongPress: () {
 | 
				
			||||||
 | 
					                      Clipboard.setData(ClipboardData(
 | 
				
			||||||
 | 
					                          text: app?.app.additionalSettings['about'] ?? ''));
 | 
				
			||||||
 | 
					                      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
 | 
				
			||||||
 | 
					                        content: Text(tr('copiedToClipboard')),
 | 
				
			||||||
 | 
					                      ));
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    child: Text(
 | 
				
			||||||
 | 
					                      app?.app.additionalSettings['about'],
 | 
				
			||||||
 | 
					                      textAlign: TextAlign.center,
 | 
				
			||||||
 | 
					                      style: const TextStyle(fontStyle: FontStyle.italic),
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -496,14 +496,8 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
      var transparent =
 | 
					      var transparent =
 | 
				
			||||||
          Theme.of(context).colorScheme.background.withAlpha(0).value;
 | 
					          Theme.of(context).colorScheme.background.withAlpha(0).value;
 | 
				
			||||||
      List<double> stops = [
 | 
					      List<double> stops = [
 | 
				
			||||||
        ...listedApps[index]
 | 
					        ...listedApps[index].app.categories.asMap().entries.map(
 | 
				
			||||||
            .app
 | 
					            (e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
 | 
				
			||||||
            .categories
 | 
					 | 
				
			||||||
            .asMap()
 | 
					 | 
				
			||||||
            .entries
 | 
					 | 
				
			||||||
            .map((e) =>
 | 
					 | 
				
			||||||
                ((e.key / (listedApps[index].app.categories.length - 1))))
 | 
					 | 
				
			||||||
            ,
 | 
					 | 
				
			||||||
        1
 | 
					        1
 | 
				
			||||||
      ];
 | 
					      ];
 | 
				
			||||||
      if (stops.length == 2) {
 | 
					      if (stops.length == 2) {
 | 
				
			||||||
@@ -516,13 +510,9 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
                  begin: const Alignment(-1, 0),
 | 
					                  begin: const Alignment(-1, 0),
 | 
				
			||||||
                  end: const Alignment(-0.97, 0),
 | 
					                  end: const Alignment(-0.97, 0),
 | 
				
			||||||
                  colors: [
 | 
					                  colors: [
 | 
				
			||||||
                ...listedApps[index]
 | 
					                ...listedApps[index].app.categories.map((e) =>
 | 
				
			||||||
                    .app
 | 
					                    Color(settingsProvider.categories[e] ?? transparent)
 | 
				
			||||||
                    .categories
 | 
					                        .withAlpha(255)),
 | 
				
			||||||
                    .map((e) =>
 | 
					 | 
				
			||||||
                        Color(settingsProvider.categories[e] ?? transparent)
 | 
					 | 
				
			||||||
                            .withAlpha(255))
 | 
					 | 
				
			||||||
                    ,
 | 
					 | 
				
			||||||
                Color(transparent)
 | 
					                Color(transparent)
 | 
				
			||||||
              ])),
 | 
					              ])),
 | 
				
			||||||
          child: ListTile(
 | 
					          child: ListTile(
 | 
				
			||||||
@@ -881,7 +871,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
                        onPressed: () {
 | 
					                        onPressed: () {
 | 
				
			||||||
                          String urls = '';
 | 
					                          String urls = '';
 | 
				
			||||||
                          for (var a in selectedApps) {
 | 
					                          for (var a in selectedApps) {
 | 
				
			||||||
                            urls += '${a.url}\n';
 | 
					                            urls += 'obtainium://add/${a.url}\n';
 | 
				
			||||||
                          }
 | 
					                          }
 | 
				
			||||||
                          urls = urls.substring(0, urls.length - 1);
 | 
					                          urls = urls.substring(0, urls.length - 1);
 | 
				
			||||||
                          Share.share(urls,
 | 
					                          Share.share(urls,
 | 
				
			||||||
@@ -981,10 +971,8 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
                      defaultValue: filter.sourceFilter,
 | 
					                      defaultValue: filter.sourceFilter,
 | 
				
			||||||
                      [
 | 
					                      [
 | 
				
			||||||
                        MapEntry('', tr('none')),
 | 
					                        MapEntry('', tr('none')),
 | 
				
			||||||
                        ...sourceProvider.sources
 | 
					                        ...sourceProvider.sources.map(
 | 
				
			||||||
                            .map((e) =>
 | 
					                            (e) => MapEntry(e.runtimeType.toString(), e.name))
 | 
				
			||||||
                                MapEntry(e.runtimeType.toString(), e.name))
 | 
					 | 
				
			||||||
                            
 | 
					 | 
				
			||||||
                      ])
 | 
					                      ])
 | 
				
			||||||
                ]
 | 
					                ]
 | 
				
			||||||
              ],
 | 
					              ],
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,11 @@
 | 
				
			|||||||
 | 
					import 'dart:async';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import 'package:animations/animations.dart';
 | 
					import 'package:animations/animations.dart';
 | 
				
			||||||
 | 
					import 'package:app_links/app_links.dart';
 | 
				
			||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:flutter/services.dart';
 | 
					import 'package:flutter/services.dart';
 | 
				
			||||||
 | 
					import 'package:obtainium/custom_errors.dart';
 | 
				
			||||||
import 'package:obtainium/pages/add_app.dart';
 | 
					import 'package:obtainium/pages/add_app.dart';
 | 
				
			||||||
import 'package:obtainium/pages/apps.dart';
 | 
					import 'package:obtainium/pages/apps.dart';
 | 
				
			||||||
import 'package:obtainium/pages/import_export.dart';
 | 
					import 'package:obtainium/pages/import_export.dart';
 | 
				
			||||||
@@ -30,58 +34,119 @@ class _HomePageState extends State<HomePage> {
 | 
				
			|||||||
  bool isReversing = false;
 | 
					  bool isReversing = false;
 | 
				
			||||||
  int prevAppCount = -1;
 | 
					  int prevAppCount = -1;
 | 
				
			||||||
  bool prevIsLoading = true;
 | 
					  bool prevIsLoading = true;
 | 
				
			||||||
 | 
					  late AppLinks _appLinks;
 | 
				
			||||||
 | 
					  StreamSubscription<Uri>? _linkSubscription;
 | 
				
			||||||
 | 
					  bool isLinkActivity = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  List<NavigationPageItem> pages = [
 | 
					  List<NavigationPageItem> pages = [
 | 
				
			||||||
    NavigationPageItem(tr('appsString'), Icons.apps,
 | 
					    NavigationPageItem(tr('appsString'), Icons.apps,
 | 
				
			||||||
        AppsPage(key: GlobalKey<AppsPageState>())),
 | 
					        AppsPage(key: GlobalKey<AppsPageState>())),
 | 
				
			||||||
    NavigationPageItem(tr('addApp'), Icons.add, const AddAppPage()),
 | 
					    NavigationPageItem(
 | 
				
			||||||
 | 
					        tr('addApp'), Icons.add, AddAppPage(key: GlobalKey<AddAppPageState>())),
 | 
				
			||||||
    NavigationPageItem(
 | 
					    NavigationPageItem(
 | 
				
			||||||
        tr('importExport'), Icons.import_export, const ImportExportPage()),
 | 
					        tr('importExport'), Icons.import_export, const ImportExportPage()),
 | 
				
			||||||
    NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
 | 
					    NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage())
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    initDeepLinks();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Future<void> initDeepLinks() async {
 | 
				
			||||||
 | 
					    _appLinks = AppLinks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    goToAddApp(String data) async {
 | 
				
			||||||
 | 
					      switchToPage(1);
 | 
				
			||||||
 | 
					      while (
 | 
				
			||||||
 | 
					          (pages[1].widget.key as GlobalKey<AddAppPageState>?)?.currentState ==
 | 
				
			||||||
 | 
					              null) {
 | 
				
			||||||
 | 
					        await Future.delayed(const Duration(microseconds: 1));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      (pages[1].widget.key as GlobalKey<AddAppPageState>?)
 | 
				
			||||||
 | 
					          ?.currentState
 | 
				
			||||||
 | 
					          ?.linkFn(data);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    interpretLink(Uri uri) async {
 | 
				
			||||||
 | 
					      isLinkActivity = true;
 | 
				
			||||||
 | 
					      var action = uri.host;
 | 
				
			||||||
 | 
					      var data = uri.path.length > 1 ? uri.path.substring(1) : "";
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        if (action == 'add') {
 | 
				
			||||||
 | 
					          await goToAddApp(data);
 | 
				
			||||||
 | 
					        } else if (action == 'app') {
 | 
				
			||||||
 | 
					          await context
 | 
				
			||||||
 | 
					              .read<AppsProvider>()
 | 
				
			||||||
 | 
					              .import('{ "apps": [${Uri.decodeComponent(data)}] }');
 | 
				
			||||||
 | 
					        } else if (action == 'apps') {
 | 
				
			||||||
 | 
					          await context
 | 
				
			||||||
 | 
					              .read<AppsProvider>()
 | 
				
			||||||
 | 
					              .import('{ "apps": ${Uri.decodeComponent(data)} }');
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          throw ObtainiumError(tr('unknown'));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (e) {
 | 
				
			||||||
 | 
					        showError(e, context);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check initial link if app was in cold state (terminated)
 | 
				
			||||||
 | 
					    final appLink = await _appLinks.getInitialAppLink();
 | 
				
			||||||
 | 
					    if (appLink != null) {
 | 
				
			||||||
 | 
					      await interpretLink(appLink);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Handle link when app is in warm state (front or background)
 | 
				
			||||||
 | 
					    _linkSubscription = _appLinks.uriLinkStream.listen((uri) async {
 | 
				
			||||||
 | 
					      await interpretLink(uri);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setIsReversing(int targetIndex) {
 | 
				
			||||||
 | 
					    bool reversing = selectedIndexHistory.isNotEmpty &&
 | 
				
			||||||
 | 
					        selectedIndexHistory.last > targetIndex;
 | 
				
			||||||
 | 
					    setState(() {
 | 
				
			||||||
 | 
					      isReversing = reversing;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switchToPage(int index) async {
 | 
				
			||||||
 | 
					    setIsReversing(index);
 | 
				
			||||||
 | 
					    if (index == 0) {
 | 
				
			||||||
 | 
					      while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState !=
 | 
				
			||||||
 | 
					          null) {
 | 
				
			||||||
 | 
					        // Avoid duplicate GlobalKey error
 | 
				
			||||||
 | 
					        await Future.delayed(const Duration(microseconds: 1));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        selectedIndexHistory.clear();
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } else if (selectedIndexHistory.isEmpty ||
 | 
				
			||||||
 | 
					        (selectedIndexHistory.isNotEmpty &&
 | 
				
			||||||
 | 
					            selectedIndexHistory.last != index)) {
 | 
				
			||||||
 | 
					      setState(() {
 | 
				
			||||||
 | 
					        int existingInd = selectedIndexHistory.indexOf(index);
 | 
				
			||||||
 | 
					        if (existingInd >= 0) {
 | 
				
			||||||
 | 
					          selectedIndexHistory.removeAt(existingInd);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        selectedIndexHistory.add(index);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    AppsProvider appsProvider = context.watch<AppsProvider>();
 | 
					    AppsProvider appsProvider = context.watch<AppsProvider>();
 | 
				
			||||||
    SettingsProvider settingsProvider = context.watch<SettingsProvider>();
 | 
					    SettingsProvider settingsProvider = context.watch<SettingsProvider>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setIsReversing(int targetIndex) {
 | 
					 | 
				
			||||||
      bool reversing = selectedIndexHistory.isNotEmpty &&
 | 
					 | 
				
			||||||
          selectedIndexHistory.last > targetIndex;
 | 
					 | 
				
			||||||
      setState(() {
 | 
					 | 
				
			||||||
        isReversing = reversing;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    switchToPage(int index) async {
 | 
					 | 
				
			||||||
      setIsReversing(index);
 | 
					 | 
				
			||||||
      if (index == 0) {
 | 
					 | 
				
			||||||
        while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState !=
 | 
					 | 
				
			||||||
            null) {
 | 
					 | 
				
			||||||
          // Avoid duplicate GlobalKey error
 | 
					 | 
				
			||||||
          await Future.delayed(const Duration(microseconds: 1));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        setState(() {
 | 
					 | 
				
			||||||
          selectedIndexHistory.clear();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      } else if (selectedIndexHistory.isEmpty ||
 | 
					 | 
				
			||||||
          (selectedIndexHistory.isNotEmpty &&
 | 
					 | 
				
			||||||
              selectedIndexHistory.last != index)) {
 | 
					 | 
				
			||||||
        setState(() {
 | 
					 | 
				
			||||||
          int existingInd = selectedIndexHistory.indexOf(index);
 | 
					 | 
				
			||||||
          if (existingInd >= 0) {
 | 
					 | 
				
			||||||
            selectedIndexHistory.removeAt(existingInd);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          selectedIndexHistory.add(index);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (!prevIsLoading &&
 | 
					    if (!prevIsLoading &&
 | 
				
			||||||
        prevAppCount >= 0 &&
 | 
					        prevAppCount >= 0 &&
 | 
				
			||||||
        appsProvider.apps.length > prevAppCount &&
 | 
					        appsProvider.apps.length > prevAppCount &&
 | 
				
			||||||
        selectedIndexHistory.isNotEmpty &&
 | 
					        selectedIndexHistory.isNotEmpty &&
 | 
				
			||||||
        selectedIndexHistory.last == 1) {
 | 
					        selectedIndexHistory.last == 1 &&
 | 
				
			||||||
 | 
					        !isLinkActivity) {
 | 
				
			||||||
      switchToPage(0);
 | 
					      switchToPage(0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    prevAppCount = appsProvider.apps.length;
 | 
					    prevAppCount = appsProvider.apps.length;
 | 
				
			||||||
@@ -129,6 +194,11 @@ class _HomePageState extends State<HomePage> {
 | 
				
			|||||||
          ),
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        onWillPop: () async {
 | 
					        onWillPop: () async {
 | 
				
			||||||
 | 
					          if (isLinkActivity &&
 | 
				
			||||||
 | 
					              selectedIndexHistory.length == 1 &&
 | 
				
			||||||
 | 
					              selectedIndexHistory.last == 1) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
          setIsReversing(selectedIndexHistory.length >= 2
 | 
					          setIsReversing(selectedIndexHistory.length >= 2
 | 
				
			||||||
              ? selectedIndexHistory.reversed.toList()[1]
 | 
					              ? selectedIndexHistory.reversed.toList()[1]
 | 
				
			||||||
              : 0);
 | 
					              : 0);
 | 
				
			||||||
@@ -143,4 +213,10 @@ class _HomePageState extends State<HomePage> {
 | 
				
			|||||||
              ?.clearSelected();
 | 
					              ?.clearSelected();
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void dispose() {
 | 
				
			||||||
 | 
					    super.dispose();
 | 
				
			||||||
 | 
					    _linkSubscription?.cancel();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -106,7 +106,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
    runObtainiumExport({bool pickOnly = false}) async {
 | 
					    runObtainiumExport({bool pickOnly = false}) async {
 | 
				
			||||||
      HapticFeedback.selectionClick();
 | 
					      HapticFeedback.selectionClick();
 | 
				
			||||||
      appsProvider
 | 
					      appsProvider
 | 
				
			||||||
          .exportApps(
 | 
					          .export(
 | 
				
			||||||
              pickOnly:
 | 
					              pickOnly:
 | 
				
			||||||
                  pickOnly || (await settingsProvider.getExportDir()) == null,
 | 
					                  pickOnly || (await settingsProvider.getExportDir()) == null,
 | 
				
			||||||
              sp: settingsProvider)
 | 
					              sp: settingsProvider)
 | 
				
			||||||
@@ -132,7 +132,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
          } catch (e) {
 | 
					          } catch (e) {
 | 
				
			||||||
            throw ObtainiumError(tr('invalidInput'));
 | 
					            throw ObtainiumError(tr('invalidInput'));
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          appsProvider.importApps(data).then((value) {
 | 
					          appsProvider.import(data).then((value) {
 | 
				
			||||||
            var cats = settingsProvider.categories;
 | 
					            var cats = settingsProvider.categories;
 | 
				
			||||||
            appsProvider.apps.forEach((key, value) {
 | 
					            appsProvider.apps.forEach((key, value) {
 | 
				
			||||||
              for (var c in value.app.categories) {
 | 
					              for (var c in value.app.categories) {
 | 
				
			||||||
@@ -143,7 +143,10 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
            appsProvider.addMissingCategories(settingsProvider);
 | 
					            appsProvider.addMissingCategories(settingsProvider);
 | 
				
			||||||
            showMessage(
 | 
					            showMessage(
 | 
				
			||||||
                tr('importedX', args: [plural('apps', value)]), context);
 | 
					                '${tr('importedX', args: [
 | 
				
			||||||
 | 
					                      plural('apps', value.key)
 | 
				
			||||||
 | 
					                    ])}${value.value ? ' + ${tr('settings')}' : ''}',
 | 
				
			||||||
 | 
					                context);
 | 
				
			||||||
          });
 | 
					          });
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          // User canceled the picker
 | 
					          // User canceled the picker
 | 
				
			||||||
@@ -344,7 +347,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
                                        : () {
 | 
					                                        : () {
 | 
				
			||||||
                                            runObtainiumExport(pickOnly: true);
 | 
					                                            runObtainiumExport(pickOnly: true);
 | 
				
			||||||
                                          },
 | 
					                                          },
 | 
				
			||||||
                                    child: Text(tr('pickExportDir')),
 | 
					                                    child: Text(tr('pickExportDir'),
 | 
				
			||||||
 | 
					                                        textAlign: TextAlign.center),
 | 
				
			||||||
                                  )),
 | 
					                                  )),
 | 
				
			||||||
                                  const SizedBox(
 | 
					                                  const SizedBox(
 | 
				
			||||||
                                    width: 16,
 | 
					                                    width: 16,
 | 
				
			||||||
@@ -357,7 +361,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
                                            snapshot.data == null
 | 
					                                            snapshot.data == null
 | 
				
			||||||
                                        ? null
 | 
					                                        ? null
 | 
				
			||||||
                                        : runObtainiumExport,
 | 
					                                        : runObtainiumExport,
 | 
				
			||||||
                                    child: Text(tr('obtainiumExport')),
 | 
					                                    child: Text(tr('obtainiumExport'),
 | 
				
			||||||
 | 
					                                        textAlign: TextAlign.center),
 | 
				
			||||||
                                  )),
 | 
					                                  )),
 | 
				
			||||||
                                ],
 | 
					                                ],
 | 
				
			||||||
                              ),
 | 
					                              ),
 | 
				
			||||||
@@ -372,7 +377,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
                                          onPressed: importInProgress
 | 
					                                          onPressed: importInProgress
 | 
				
			||||||
                                              ? null
 | 
					                                              ? null
 | 
				
			||||||
                                              : runObtainiumImport,
 | 
					                                              : runObtainiumImport,
 | 
				
			||||||
                                          child: Text(tr('obtainiumImport')))),
 | 
					                                          child: Text(tr('obtainiumImport'),
 | 
				
			||||||
 | 
					                                              textAlign: TextAlign.center))),
 | 
				
			||||||
                                ],
 | 
					                                ],
 | 
				
			||||||
                              ),
 | 
					                              ),
 | 
				
			||||||
                              if (snapshot.data != null)
 | 
					                              if (snapshot.data != null)
 | 
				
			||||||
@@ -388,6 +394,14 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
                                              defaultValue: settingsProvider
 | 
					                                              defaultValue: settingsProvider
 | 
				
			||||||
                                                  .autoExportOnChanges,
 | 
					                                                  .autoExportOnChanges,
 | 
				
			||||||
                                            )
 | 
					                                            )
 | 
				
			||||||
 | 
					                                          ],
 | 
				
			||||||
 | 
					                                          [
 | 
				
			||||||
 | 
					                                            GeneratedFormSwitch(
 | 
				
			||||||
 | 
					                                              'exportSettings',
 | 
				
			||||||
 | 
					                                              label: tr('includeSettings'),
 | 
				
			||||||
 | 
					                                              defaultValue: settingsProvider
 | 
				
			||||||
 | 
					                                                  .exportSettings,
 | 
				
			||||||
 | 
					                                            )
 | 
				
			||||||
                                          ]
 | 
					                                          ]
 | 
				
			||||||
                                        ],
 | 
					                                        ],
 | 
				
			||||||
                                        onValueChanges:
 | 
					                                        onValueChanges:
 | 
				
			||||||
@@ -400,6 +414,12 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
				
			|||||||
                                                      'autoExportOnChanges'] ==
 | 
					                                                      'autoExportOnChanges'] ==
 | 
				
			||||||
                                                  true;
 | 
					                                                  true;
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
 | 
					                                            if (value['exportSettings'] !=
 | 
				
			||||||
 | 
					                                                null) {
 | 
				
			||||||
 | 
					                                              settingsProvider.exportSettings =
 | 
				
			||||||
 | 
					                                                  value['exportSettings'] ==
 | 
				
			||||||
 | 
					                                                      true;
 | 
				
			||||||
 | 
					                                            }
 | 
				
			||||||
                                          }
 | 
					                                          }
 | 
				
			||||||
                                        }),
 | 
					                                        }),
 | 
				
			||||||
                                  ],
 | 
					                                  ],
 | 
				
			||||||
@@ -598,6 +618,7 @@ class SelectionModal extends StatefulWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class _SelectionModalState extends State<SelectionModal> {
 | 
					class _SelectionModalState extends State<SelectionModal> {
 | 
				
			||||||
  Map<MapEntry<String, List<String>>, bool> entrySelections = {};
 | 
					  Map<MapEntry<String, List<String>>, bool> entrySelections = {};
 | 
				
			||||||
 | 
					  String filterRegex = '';
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  void initState() {
 | 
					  void initState() {
 | 
				
			||||||
    super.initState();
 | 
					    super.initState();
 | 
				
			||||||
@@ -618,11 +639,50 @@ class _SelectionModalState extends State<SelectionModal> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    Map<MapEntry<String, List<String>>, bool> filteredEntrySelections = {};
 | 
				
			||||||
 | 
					    entrySelections.forEach((key, value) {
 | 
				
			||||||
 | 
					      var searchableText = key.value.isEmpty ? key.key : key.value[0];
 | 
				
			||||||
 | 
					      if (filterRegex.isEmpty || RegExp(filterRegex).hasMatch(searchableText)) {
 | 
				
			||||||
 | 
					        filteredEntrySelections.putIfAbsent(key, () => value);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    if (filterRegex.isNotEmpty && filteredEntrySelections.isEmpty) {
 | 
				
			||||||
 | 
					      entrySelections.forEach((key, value) {
 | 
				
			||||||
 | 
					        var searchableText = key.value.isEmpty ? key.key : key.value[0];
 | 
				
			||||||
 | 
					        if (filterRegex.isEmpty ||
 | 
				
			||||||
 | 
					            RegExp(filterRegex, caseSensitive: false)
 | 
				
			||||||
 | 
					                .hasMatch(searchableText)) {
 | 
				
			||||||
 | 
					          filteredEntrySelections.putIfAbsent(key, () => value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return AlertDialog(
 | 
					    return AlertDialog(
 | 
				
			||||||
      scrollable: true,
 | 
					      scrollable: true,
 | 
				
			||||||
      title: Text(widget.title ?? tr('pick')),
 | 
					      title: Text(widget.title ?? tr('pick')),
 | 
				
			||||||
      content: Column(children: [
 | 
					      content: Column(children: [
 | 
				
			||||||
        ...entrySelections.keys.map((entry) {
 | 
					        GeneratedForm(
 | 
				
			||||||
 | 
					            items: [
 | 
				
			||||||
 | 
					              [
 | 
				
			||||||
 | 
					                GeneratedFormTextField('filter',
 | 
				
			||||||
 | 
					                    label: tr('filter'),
 | 
				
			||||||
 | 
					                    required: false,
 | 
				
			||||||
 | 
					                    additionalValidators: [
 | 
				
			||||||
 | 
					                      (value) {
 | 
				
			||||||
 | 
					                        return regExValidator(value);
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    ])
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            onValueChanges: (value, valid, isBuilding) {
 | 
				
			||||||
 | 
					              if (valid && !isBuilding) {
 | 
				
			||||||
 | 
					                if (value['filter'] != null) {
 | 
				
			||||||
 | 
					                  setState(() {
 | 
				
			||||||
 | 
					                    filterRegex = value['filter'];
 | 
				
			||||||
 | 
					                  });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					        ...filteredEntrySelections.keys.map((entry) {
 | 
				
			||||||
          selectThis(bool? value) {
 | 
					          selectThis(bool? value) {
 | 
				
			||||||
            setState(() {
 | 
					            setState(() {
 | 
				
			||||||
              value ??= false;
 | 
					              value ??= false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -327,6 +327,19 @@ class _SettingsPageState extends State<SettingsPage> {
 | 
				
			|||||||
                                    })
 | 
					                                    })
 | 
				
			||||||
                              ],
 | 
					                              ],
 | 
				
			||||||
                            ),
 | 
					                            ),
 | 
				
			||||||
 | 
					                            height16,
 | 
				
			||||||
 | 
					                            Row(
 | 
				
			||||||
 | 
					                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
 | 
					                              children: [
 | 
				
			||||||
 | 
					                                Flexible(child: Text(tr('parallelDownloads'))),
 | 
				
			||||||
 | 
					                                Switch(
 | 
				
			||||||
 | 
					                                    value: settingsProvider.parallelDownloads,
 | 
				
			||||||
 | 
					                                    onChanged: (value) {
 | 
				
			||||||
 | 
					                                      settingsProvider.parallelDownloads =
 | 
				
			||||||
 | 
					                                          value;
 | 
				
			||||||
 | 
					                                    })
 | 
				
			||||||
 | 
					                              ],
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
                            height32,
 | 
					                            height32,
 | 
				
			||||||
                            Text(
 | 
					                            Text(
 | 
				
			||||||
                              tr('sourceSpecific'),
 | 
					                              tr('sourceSpecific'),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -657,7 +657,7 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    appsToInstall =
 | 
					    appsToInstall =
 | 
				
			||||||
        moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
 | 
					        moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (var id in appsToInstall) {
 | 
					    Future<void> updateFn(String id, {bool skipInstalls = false}) async {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        var downloadedArtifact =
 | 
					        var downloadedArtifact =
 | 
				
			||||||
            // ignore: use_build_context_synchronously
 | 
					            // ignore: use_build_context_synchronously
 | 
				
			||||||
@@ -682,24 +682,26 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
        apps[id]?.downloadProgress = -1;
 | 
					        apps[id]?.downloadProgress = -1;
 | 
				
			||||||
        notifyListeners();
 | 
					        notifyListeners();
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
          if (downloadedFile != null) {
 | 
					          if (!skipInstalls) {
 | 
				
			||||||
            if (willBeSilent && context == null) {
 | 
					            if (downloadedFile != null) {
 | 
				
			||||||
              installApk(downloadedFile, needsBGWorkaround: true);
 | 
					              if (willBeSilent && context == null) {
 | 
				
			||||||
 | 
					                installApk(downloadedFile, needsBGWorkaround: true);
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                await installApk(downloadedFile);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
              await installApk(downloadedFile);
 | 
					              if (willBeSilent && context == null) {
 | 
				
			||||||
 | 
					                installXApkDir(downloadedDir!, needsBGWorkaround: true);
 | 
				
			||||||
 | 
					              } else {
 | 
				
			||||||
 | 
					                await installXApkDir(downloadedDir!);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            if (willBeSilent && context == null) {
 | 
					            if (willBeSilent && context == null) {
 | 
				
			||||||
              installXApkDir(downloadedDir!, needsBGWorkaround: true);
 | 
					              notificationsProvider?.notify(SilentUpdateAttemptNotification(
 | 
				
			||||||
            } else {
 | 
					                  [apps[appId]!.app],
 | 
				
			||||||
              await installXApkDir(downloadedDir!);
 | 
					                  id: appId.hashCode));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (willBeSilent && context == null) {
 | 
					 | 
				
			||||||
            notificationsProvider?.notify(SilentUpdateAttemptNotification(
 | 
					 | 
				
			||||||
                [apps[appId]!.app],
 | 
					 | 
				
			||||||
                id: appId.hashCode));
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
          apps[id]?.downloadProgress = null;
 | 
					          apps[id]?.downloadProgress = null;
 | 
				
			||||||
          notifyListeners();
 | 
					          notifyListeners();
 | 
				
			||||||
@@ -710,6 +712,18 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!settingsProvider.parallelDownloads) {
 | 
				
			||||||
 | 
					      for (var id in appsToInstall) {
 | 
				
			||||||
 | 
					        await updateFn(id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      await Future.wait(
 | 
				
			||||||
 | 
					          appsToInstall.map((id) => updateFn(id, skipInstalls: true)));
 | 
				
			||||||
 | 
					      for (var id in appsToInstall) {
 | 
				
			||||||
 | 
					        await updateFn(id);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (errors.idsByErrorString.isNotEmpty) {
 | 
					    if (errors.idsByErrorString.isNotEmpty) {
 | 
				
			||||||
      throw errors;
 | 
					      throw errors;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -726,12 +740,15 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    return appsDir;
 | 
					    return appsDir;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<PackageInfo?> getInstalledInfo(String? packageName) async {
 | 
					  Future<PackageInfo?> getInstalledInfo(String? packageName,
 | 
				
			||||||
 | 
					      {bool printErr = true}) async {
 | 
				
			||||||
    if (packageName != null) {
 | 
					    if (packageName != null) {
 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        return await pm.getPackageInfo(packageName: packageName);
 | 
					        return await pm.getPackageInfo(packageName: packageName);
 | 
				
			||||||
      } catch (e) {
 | 
					      } catch (e) {
 | 
				
			||||||
        print(e); // OK
 | 
					        if (printErr) {
 | 
				
			||||||
 | 
					          print(e); // OK
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@@ -974,7 +991,7 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    notifyListeners();
 | 
					    notifyListeners();
 | 
				
			||||||
    exportApps(isAuto: true);
 | 
					    export(isAuto: true);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<void> removeApps(List<String> appIds) async {
 | 
					  Future<void> removeApps(List<String> appIds) async {
 | 
				
			||||||
@@ -996,7 +1013,7 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    if (appIds.isNotEmpty) {
 | 
					    if (appIds.isNotEmpty) {
 | 
				
			||||||
      notifyListeners();
 | 
					      notifyListeners();
 | 
				
			||||||
      exportApps(isAuto: true);
 | 
					      export(isAuto: true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1173,7 +1190,7 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    return updateAppIds;
 | 
					    return updateAppIds;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<String?> exportApps(
 | 
					  Future<String?> export(
 | 
				
			||||||
      {bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
 | 
					      {bool pickOnly = false, isAuto = false, SettingsProvider? sp}) async {
 | 
				
			||||||
    SettingsProvider settingsProvider = sp ?? this.settingsProvider;
 | 
					    SettingsProvider settingsProvider = sp ?? this.settingsProvider;
 | 
				
			||||||
    var exportDir = await settingsProvider.getExportDir();
 | 
					    var exportDir = await settingsProvider.getExportDir();
 | 
				
			||||||
@@ -1203,12 +1220,22 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    String? returnPath;
 | 
					    String? returnPath;
 | 
				
			||||||
    if (!pickOnly) {
 | 
					    if (!pickOnly) {
 | 
				
			||||||
 | 
					      Map<String, dynamic> finalExport = {};
 | 
				
			||||||
 | 
					      finalExport['apps'] = apps.values.map((e) => e.app.toJson()).toList();
 | 
				
			||||||
 | 
					      if (settingsProvider.exportSettings) {
 | 
				
			||||||
 | 
					        finalExport['settings'] = Map<String, Object?>.fromEntries(
 | 
				
			||||||
 | 
					            (settingsProvider.prefs
 | 
				
			||||||
 | 
					                    ?.getKeys()
 | 
				
			||||||
 | 
					                    .map((key) =>
 | 
				
			||||||
 | 
					                        MapEntry(key, settingsProvider.prefs?.get(key)))
 | 
				
			||||||
 | 
					                    .toList()) ??
 | 
				
			||||||
 | 
					                []);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      var result = await saf.createFile(exportDir,
 | 
					      var result = await saf.createFile(exportDir,
 | 
				
			||||||
          displayName:
 | 
					          displayName:
 | 
				
			||||||
              '${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
 | 
					              '${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json',
 | 
				
			||||||
          mimeType: 'application/json',
 | 
					          mimeType: 'application/json',
 | 
				
			||||||
          bytes: Uint8List.fromList(utf8.encode(
 | 
					          bytes: Uint8List.fromList(utf8.encode(jsonEncode(finalExport))));
 | 
				
			||||||
              jsonEncode(apps.values.map((e) => e.app.toJson()).toList()))));
 | 
					 | 
				
			||||||
      if (result == null) {
 | 
					      if (result == null) {
 | 
				
			||||||
        throw ObtainiumError(tr('unexpectedError'));
 | 
					        throw ObtainiumError(tr('unexpectedError'));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -1218,21 +1245,36 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
    return returnPath;
 | 
					    return returnPath;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<int> importApps(String appsJSON) async {
 | 
					  Future<MapEntry<int, bool>> import(String appsJSON) async {
 | 
				
			||||||
    List<App> importedApps = (jsonDecode(appsJSON) as List<dynamic>)
 | 
					    var decodedJSON = jsonDecode(appsJSON);
 | 
				
			||||||
        .map((e) => App.fromJson(e))
 | 
					    var newFormat = decodedJSON is! List;
 | 
				
			||||||
        .toList();
 | 
					    List<App> importedApps =
 | 
				
			||||||
 | 
					        ((newFormat ? decodedJSON['apps'] : decodedJSON) as List<dynamic>)
 | 
				
			||||||
 | 
					            .map((e) => App.fromJson(e))
 | 
				
			||||||
 | 
					            .toList();
 | 
				
			||||||
    while (loadingApps) {
 | 
					    while (loadingApps) {
 | 
				
			||||||
      await Future.delayed(const Duration(microseconds: 1));
 | 
					      await Future.delayed(const Duration(microseconds: 1));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    for (App a in importedApps) {
 | 
					    for (App a in importedApps) {
 | 
				
			||||||
      if (apps[a.id]?.app.installedVersion != null) {
 | 
					      a.installedVersion =
 | 
				
			||||||
        a.installedVersion = apps[a.id]?.app.installedVersion;
 | 
					          (await getInstalledInfo(a.id, printErr: false))?.versionName;
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    await saveApps(importedApps, onlyIfExists: false);
 | 
					    await saveApps(importedApps, onlyIfExists: false);
 | 
				
			||||||
    notifyListeners();
 | 
					    notifyListeners();
 | 
				
			||||||
    return importedApps.length;
 | 
					    if (newFormat && decodedJSON['settings'] != null) {
 | 
				
			||||||
 | 
					      var settingsMap = decodedJSON['settings'] as Map<String, Object?>;
 | 
				
			||||||
 | 
					      settingsMap.forEach((key, value) {
 | 
				
			||||||
 | 
					        if (value is int) {
 | 
				
			||||||
 | 
					          settingsProvider.prefs?.setInt(key, value);
 | 
				
			||||||
 | 
					        } else if (value is bool) {
 | 
				
			||||||
 | 
					          settingsProvider.prefs?.setBool(key, value);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          settingsProvider.prefs?.setString(key, value as String);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return MapEntry<int, bool>(
 | 
				
			||||||
 | 
					        importedApps.length, newFormat && decodedJSON['settings'] != null);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -213,7 +213,8 @@ class SettingsProvider with ChangeNotifier {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  String? getSettingString(String settingId) {
 | 
					  String? getSettingString(String settingId) {
 | 
				
			||||||
    return prefs?.getString(settingId);
 | 
					    String? str = prefs?.getString(settingId);
 | 
				
			||||||
 | 
					    return str?.isNotEmpty == true ? str : null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void setSettingString(String settingId, String value) {
 | 
					  void setSettingString(String settingId, String value) {
 | 
				
			||||||
@@ -415,4 +416,22 @@ class SettingsProvider with ChangeNotifier {
 | 
				
			|||||||
    prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
 | 
					    prefs?.setBool('onlyCheckInstalledOrTrackOnlyApps', val);
 | 
				
			||||||
    notifyListeners();
 | 
					    notifyListeners();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get exportSettings {
 | 
				
			||||||
 | 
					    return prefs?.getBool('exportSettings') ?? false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set exportSettings(bool val) {
 | 
				
			||||||
 | 
					    prefs?.setBool('exportSettings', val);
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool get parallelDownloads {
 | 
				
			||||||
 | 
					    return prefs?.getBool('parallelDownloads') ?? false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set parallelDownloads(bool val) {
 | 
				
			||||||
 | 
					    prefs?.setBool('parallelDownloads', val);
 | 
				
			||||||
 | 
					    notifyListeners();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -454,7 +454,8 @@ abstract class AppSource {
 | 
				
			|||||||
    [
 | 
					    [
 | 
				
			||||||
      GeneratedFormSwitch('skipUpdateNotifications',
 | 
					      GeneratedFormSwitch('skipUpdateNotifications',
 | 
				
			||||||
          label: tr('skipUpdateNotifications'))
 | 
					          label: tr('skipUpdateNotifications'))
 | 
				
			||||||
    ]
 | 
					    ],
 | 
				
			||||||
 | 
					    [GeneratedFormTextField('about', label: tr('about'), required: false)]
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Previous 2 variables combined into one at runtime for convenient usage
 | 
					  // Previous 2 variables combined into one at runtime for convenient usage
 | 
				
			||||||
@@ -683,8 +684,9 @@ class SourceProvider {
 | 
				
			|||||||
    name = name.isNotEmpty ? name : apk.names.name;
 | 
					    name = name.isNotEmpty ? name : apk.names.name;
 | 
				
			||||||
    App finalApp = App(
 | 
					    App finalApp = App(
 | 
				
			||||||
        currentApp?.id ??
 | 
					        currentApp?.id ??
 | 
				
			||||||
            ((!source.appIdInferIsOptional ||
 | 
					            (!trackOnly &&
 | 
				
			||||||
                    (source.appIdInferIsOptional && inferAppIdIfOptional))
 | 
					                    (!source.appIdInferIsOptional ||
 | 
				
			||||||
 | 
					                        (source.appIdInferIsOptional && inferAppIdIfOptional))
 | 
				
			||||||
                ? await source.tryInferringAppId(standardUrl,
 | 
					                ? await source.tryInferringAppId(standardUrl,
 | 
				
			||||||
                    additionalSettings: additionalSettings)
 | 
					                    additionalSettings: additionalSettings)
 | 
				
			||||||
                : null) ??
 | 
					                : null) ??
 | 
				
			||||||
@@ -704,8 +706,9 @@ class SourceProvider {
 | 
				
			|||||||
        changeLog: apk.changeLog,
 | 
					        changeLog: apk.changeLog,
 | 
				
			||||||
        overrideSource: overrideSource ?? currentApp?.overrideSource,
 | 
					        overrideSource: overrideSource ?? currentApp?.overrideSource,
 | 
				
			||||||
        allowIdChange: currentApp?.allowIdChange ??
 | 
					        allowIdChange: currentApp?.allowIdChange ??
 | 
				
			||||||
            source.appIdInferIsOptional &&
 | 
					            trackOnly ||
 | 
				
			||||||
                inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install
 | 
					                (source.appIdInferIsOptional &&
 | 
				
			||||||
 | 
					                    inferAppIdIfOptional) // Optional ID inferring may be incorrect - allow correction on first install
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    return source.endOfGetAppChanges(finalApp);
 | 
					    return source.endOfGetAppChanges(finalApp);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										96
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								pubspec.lock
									
									
									
									
									
								
							@@ -5,10 +5,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: android_alarm_manager_plus
 | 
					      name: android_alarm_manager_plus
 | 
				
			||||||
      sha256: "82fb28c867c4b3dd7e9157728e46426b8916362f977dbba46b949210f00099f4"
 | 
					      sha256: "84720c8ad2758aabfbeafd24a8c355d8c8dd3aa52b01eaf3bb827c7210f61a91"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.0.3"
 | 
					    version: "3.0.4"
 | 
				
			||||||
  android_intent_plus:
 | 
					  android_intent_plus:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -38,10 +38,18 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: animations
 | 
					      name: animations
 | 
				
			||||||
      sha256: ef57563eed3620bd5d75ad96189846aca1e033c0c45fc9a7d26e80ab02b88a70
 | 
					      sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.0.8"
 | 
					    version: "2.0.10"
 | 
				
			||||||
 | 
					  app_links:
 | 
				
			||||||
 | 
					    dependency: "direct main"
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: app_links
 | 
				
			||||||
 | 
					      sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb"
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "3.5.0"
 | 
				
			||||||
  archive:
 | 
					  archive:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -94,10 +102,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: cli_util
 | 
					      name: cli_util
 | 
				
			||||||
      sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7
 | 
					      sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.4.0"
 | 
					    version: "0.4.1"
 | 
				
			||||||
  clock:
 | 
					  clock:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -142,10 +150,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: cross_file
 | 
					      name: cross_file
 | 
				
			||||||
      sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5"
 | 
					      sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.3.3+7"
 | 
					    version: "0.3.3+8"
 | 
				
			||||||
  crypto:
 | 
					  crypto:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -198,10 +206,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: dynamic_color
 | 
					      name: dynamic_color
 | 
				
			||||||
      sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
 | 
					      sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.6.8"
 | 
					    version: "1.6.9"
 | 
				
			||||||
  easy_localization:
 | 
					  easy_localization:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -259,10 +267,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: flutter_archive
 | 
					      name: flutter_archive
 | 
				
			||||||
      sha256: aec85d1da65e5b33a529db00a86df0b8e92bda78088a7cfaeeba5187701d0d85
 | 
					      sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "5.0.0"
 | 
					    version: "6.0.0"
 | 
				
			||||||
  flutter_fgbg:
 | 
					  flutter_fgbg:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -350,6 +358,14 @@ packages:
 | 
				
			|||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "8.2.4"
 | 
					    version: "8.2.4"
 | 
				
			||||||
 | 
					  gtk:
 | 
				
			||||||
 | 
					    dependency: transitive
 | 
				
			||||||
 | 
					    description:
 | 
				
			||||||
 | 
					      name: gtk
 | 
				
			||||||
 | 
					      sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c
 | 
				
			||||||
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
 | 
					    source: hosted
 | 
				
			||||||
 | 
					    version: "2.1.0"
 | 
				
			||||||
  hsluv:
 | 
					  hsluv:
 | 
				
			||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -370,10 +386,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: http
 | 
					      name: http
 | 
				
			||||||
      sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
 | 
					      sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "1.1.0"
 | 
					    version: "1.1.2"
 | 
				
			||||||
  http_parser:
 | 
					  http_parser:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -562,10 +578,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: permission_handler_html
 | 
					      name: permission_handler_html
 | 
				
			||||||
      sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8
 | 
					      sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "0.1.0+1"
 | 
					    version: "0.1.0+2"
 | 
				
			||||||
  permission_handler_platform_interface:
 | 
					  permission_handler_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -586,10 +602,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: petitparser
 | 
					      name: petitparser
 | 
				
			||||||
      sha256: eeb2d1428ee7f4170e2bd498827296a18d4e7fc462b71727d111c0ac7707cfa6
 | 
					      sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "6.0.1"
 | 
					    version: "6.0.2"
 | 
				
			||||||
  platform:
 | 
					  platform:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -767,10 +783,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: synchronized
 | 
					      name: synchronized
 | 
				
			||||||
      sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
 | 
					      sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.1.0"
 | 
					    version: "3.1.0+1"
 | 
				
			||||||
  term_glyph:
 | 
					  term_glyph:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -807,10 +823,10 @@ packages:
 | 
				
			|||||||
    dependency: "direct main"
 | 
					    dependency: "direct main"
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: url_launcher
 | 
					      name: url_launcher
 | 
				
			||||||
      sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba
 | 
					      sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "6.2.1"
 | 
					    version: "6.2.2"
 | 
				
			||||||
  url_launcher_android:
 | 
					  url_launcher_android:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -831,10 +847,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: url_launcher_linux
 | 
					      name: url_launcher_linux
 | 
				
			||||||
      sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd"
 | 
					      sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.1.0"
 | 
					    version: "3.1.1"
 | 
				
			||||||
  url_launcher_macos:
 | 
					  url_launcher_macos:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -855,26 +871,26 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: url_launcher_web
 | 
					      name: url_launcher_web
 | 
				
			||||||
      sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7"
 | 
					      sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.2.1"
 | 
					    version: "2.2.2"
 | 
				
			||||||
  url_launcher_windows:
 | 
					  url_launcher_windows:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: url_launcher_windows
 | 
					      name: url_launcher_windows
 | 
				
			||||||
      sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc"
 | 
					      sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.1.0"
 | 
					    version: "3.1.1"
 | 
				
			||||||
  uuid:
 | 
					  uuid:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: uuid
 | 
					      name: uuid
 | 
				
			||||||
      sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921
 | 
					      sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "4.2.1"
 | 
					    version: "4.2.2"
 | 
				
			||||||
  vector_math:
 | 
					  vector_math:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -903,34 +919,34 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: webview_flutter_android
 | 
					      name: webview_flutter_android
 | 
				
			||||||
      sha256: "8326ee235f87605a2bfc444a4abc897f4abc78d83f054ba7d3d1074ce82b4fbf"
 | 
					      sha256: b54c89fe14a6d26a2a46e24880da0441cdd2bf1f6d01a5b3e1d39558feb1de0b
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.12.1"
 | 
					    version: "3.13.1"
 | 
				
			||||||
  webview_flutter_platform_interface:
 | 
					  webview_flutter_platform_interface:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: webview_flutter_platform_interface
 | 
					      name: webview_flutter_platform_interface
 | 
				
			||||||
      sha256: adb8c03c2be231bea5a8ed0e9039e9d18dbb049603376beaefa15393ede468a5
 | 
					      sha256: dbe745ee459a16b6fec296f7565a8ef430d0d681001d8ae521898b9361854943
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "2.7.0"
 | 
					    version: "2.9.0"
 | 
				
			||||||
  webview_flutter_wkwebview:
 | 
					  webview_flutter_wkwebview:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: webview_flutter_wkwebview
 | 
					      name: webview_flutter_wkwebview
 | 
				
			||||||
      sha256: accdaaa49a2aca2dc3c3230907988954cdd23fed0a19525d6c9789d380f4dc76
 | 
					      sha256: eebfabfa8a115b535b52031b8b26f7a4b58ceceab378bc9db8762b0fb46f7b5d
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "3.9.4"
 | 
					    version: "3.10.0"
 | 
				
			||||||
  win32:
 | 
					  win32:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: win32
 | 
					      name: win32
 | 
				
			||||||
      sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f"
 | 
					      sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "5.1.0"
 | 
					    version: "5.1.1"
 | 
				
			||||||
  win32_registry:
 | 
					  win32_registry:
 | 
				
			||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
@@ -951,10 +967,10 @@ packages:
 | 
				
			|||||||
    dependency: transitive
 | 
					    dependency: transitive
 | 
				
			||||||
    description:
 | 
					    description:
 | 
				
			||||||
      name: xml
 | 
					      name: xml
 | 
				
			||||||
      sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556
 | 
					      sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
 | 
				
			||||||
      url: "https://pub.dev"
 | 
					      url: "https://pub.dev"
 | 
				
			||||||
    source: hosted
 | 
					    source: hosted
 | 
				
			||||||
    version: "6.4.2"
 | 
					    version: "6.5.0"
 | 
				
			||||||
  yaml:
 | 
					  yaml:
 | 
				
			||||||
    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: 0.14.35+229 # When changing this, update the tag in main() accordingly
 | 
					version: 0.14.40+234 # 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'
 | 
				
			||||||
@@ -62,11 +62,12 @@ dependencies:
 | 
				
			|||||||
  easy_localization: ^3.0.1
 | 
					  easy_localization: ^3.0.1
 | 
				
			||||||
  android_intent_plus: ^4.0.0
 | 
					  android_intent_plus: ^4.0.0
 | 
				
			||||||
  flutter_markdown: ^0.6.14
 | 
					  flutter_markdown: ^0.6.14
 | 
				
			||||||
  flutter_archive: ^5.0.0
 | 
					  flutter_archive: ^6.0.0
 | 
				
			||||||
  hsluv: ^1.1.3
 | 
					  hsluv: ^1.1.3
 | 
				
			||||||
  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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dev_dependencies:
 | 
					dev_dependencies:
 | 
				
			||||||
  flutter_test:
 | 
					  flutter_test:
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user