mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 21:36:42 +02:00
Compare commits
50 Commits
v0.15.1-be
...
v0.15.4-be
Author | SHA1 | Date | |
---|---|---|---|
ad8463ac8b | |||
7843de240a | |||
0037a08017 | |||
92ba432992 | |||
3a521f4014 | |||
e8bbc9799c | |||
96a48b4813 | |||
355450e82d | |||
99307c5b0c | |||
ace8be6514 | |||
1b538931b1 | |||
7ccd08ee2b | |||
104beed594 | |||
e8df594edb | |||
6e922a84f8 | |||
b74bf86277 | |||
6cbe50c9ef | |||
14fcca040b | |||
7bb545cff9 | |||
f52b1a246c | |||
273dfd89cc | |||
52d72d287b | |||
e8d8fcbe69 | |||
30c6c1afd3 | |||
c8c8093b3a | |||
c970b2cf2e | |||
0815283d31 | |||
55ee2d93b8 | |||
c9e17ed42b | |||
76a91b7fe0 | |||
f9b3169b6a | |||
4f42b5a3ee | |||
5d9645eaff | |||
c032808d82 | |||
282c94266e | |||
e2f7d52bee | |||
6317f0162a | |||
436a6310d7 | |||
0f74195255 | |||
5ba33786ab | |||
70fcfc1753 | |||
1e34048c0c | |||
45b1b23262 | |||
07842ace4e | |||
8e75280093 | |||
029b9ef498 | |||
51970abce7 | |||
f6faa19e5f | |||
dd7217ca54 | |||
1fc8ee6fee |
@ -1,12 +1,14 @@
|
|||||||
#  Obtainium
|
#  Obtainium
|
||||||
|
|
||||||
|
[](https://techforpalestine.org/learn-more)
|
||||||
|
|
||||||
Get Android App Updates Directly From the Source.
|
Get Android App Updates Directly From the Source.
|
||||||
|
|
||||||
Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.
|
Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.
|
||||||
|
|
||||||
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)
|
Read the 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:
|
||||||
@ -30,6 +32,7 @@ Currently supported App sources:
|
|||||||
- [Signal](https://signal.org/)
|
- [Signal](https://signal.org/)
|
||||||
- [VLC](https://videolan.org/)
|
- [VLC](https://videolan.org/)
|
||||||
- Other - App-Specific:
|
- Other - App-Specific:
|
||||||
|
- [WhatsApp](https://whatsapp.com)
|
||||||
- [Telegram App](https://telegram.org)
|
- [Telegram App](https://telegram.org)
|
||||||
- [Steam Mobile Apps](https://store.steampowered.com/mobile)
|
- [Steam Mobile Apps](https://store.steampowered.com/mobile)
|
||||||
- [Neutron Code](https://neutroncode.com)
|
- [Neutron Code](https://neutroncode.com)
|
||||||
|
@ -96,19 +96,15 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
|
||||||
|
|
||||||
def shizuku_version = '13.1.5'
|
def shizuku_version = '13.1.5'
|
||||||
implementation "dev.rikka.shizuku:api:$shizuku_version"
|
implementation "dev.rikka.shizuku:api:$shizuku_version"
|
||||||
implementation "dev.rikka.shizuku:provider:$shizuku_version"
|
implementation "dev.rikka.shizuku:provider:$shizuku_version"
|
||||||
|
|
||||||
def hidden_api_version = '4.1.0'
|
def hidden_api_version = '4.3.1'
|
||||||
// DO NOT UPDATE Hidden API without updating the Android tools
|
|
||||||
// and do not update Android tools without updating the whole Flutter
|
|
||||||
// (also in android/build.gradle)
|
|
||||||
implementation "dev.rikka.tools.refine:runtime:$hidden_api_version"
|
implementation "dev.rikka.tools.refine:runtime:$hidden_api_version"
|
||||||
implementation "dev.rikka.hidden:compat:$hidden_api_version"
|
implementation "dev.rikka.hidden:compat:$hidden_api_version"
|
||||||
compileOnly "dev.rikka.hidden:stub:$hidden_api_version"
|
compileOnly "dev.rikka.hidden:stub:$hidden_api_version"
|
||||||
|
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
|
||||||
|
|
||||||
implementation "com.github.topjohnwu.libsu:core:5.2.2"
|
implementation "com.github.topjohnwu.libsu:core:5.2.2"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
android:label="Obtainium"
|
android:label="Obtainium"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@ -44,21 +45,6 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
<service
|
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmService"
|
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
|
||||||
android:exported="false" />
|
|
||||||
<receiver
|
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.AlarmBroadcastReceiver"
|
|
||||||
android:exported="false" />
|
|
||||||
<receiver
|
|
||||||
android:name="dev.fluttercommunity.plus.androidalarmmanager.RebootBroadcastReceiver"
|
|
||||||
android:enabled="false"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
</intent-filter>
|
|
||||||
</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"
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package dev.imranr.obtainium
|
||||||
|
|
||||||
|
import android.util.Xml
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
|
|
||||||
|
class DefaultSystemFont {
|
||||||
|
fun get(): String {
|
||||||
|
return try {
|
||||||
|
val file = File("/system/etc/fonts.xml")
|
||||||
|
val fileStream = FileInputStream(file)
|
||||||
|
parseFontsFileStream(fileStream)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.message ?: "Unknown fonts.xml parsing exception"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFontsFileStream(fileStream: FileInputStream): String {
|
||||||
|
fileStream.use { stream ->
|
||||||
|
val parser = Xml.newPullParser()
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false)
|
||||||
|
parser.setInput(stream, null)
|
||||||
|
parser.nextTag()
|
||||||
|
return parseFonts(parser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseFonts(parser: XmlPullParser): String {
|
||||||
|
while (!((parser.next() == XmlPullParser.END_TAG) && (parser.name == "family"))) {
|
||||||
|
if ((parser.eventType == XmlPullParser.START_TAG) && (parser.name == "font")
|
||||||
|
&& (parser.getAttributeValue(null, "style") == "normal")
|
||||||
|
&& (parser.getAttributeValue(null, "weight") == "400")) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.next()
|
||||||
|
val fontFile = parser.text.trim()
|
||||||
|
if (fontFile == "") {
|
||||||
|
throw NoSuchFieldException("The font filename couldn't be found in fonts.xml")
|
||||||
|
}
|
||||||
|
return "/system/fonts/$fontFile"
|
||||||
|
}
|
||||||
|
}
|
@ -22,12 +22,13 @@ import io.flutter.plugin.common.MethodChannel
|
|||||||
import io.flutter.plugin.common.MethodChannel.Result
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import org.lsposed.hiddenapibypass.HiddenApiBypass
|
||||||
import rikka.shizuku.Shizuku
|
import rikka.shizuku.Shizuku
|
||||||
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
||||||
import rikka.shizuku.ShizukuBinderWrapper
|
import rikka.shizuku.ShizukuBinderWrapper
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
class MainActivity: FlutterActivity() {
|
||||||
private var installersChannel: MethodChannel? = null
|
private var nativeChannel: MethodChannel? = null
|
||||||
private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random()
|
private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random()
|
||||||
|
|
||||||
private fun shizukuCheckPermission(result: Result) {
|
private fun shizukuCheckPermission(result: Result) {
|
||||||
@ -51,7 +52,7 @@ class MainActivity: FlutterActivity() {
|
|||||||
requestCode: Int, grantResult: Int ->
|
requestCode: Int, grantResult: Int ->
|
||||||
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
|
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
|
||||||
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
|
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
|
||||||
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
|
nativeChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +77,8 @@ class MainActivity: FlutterActivity() {
|
|||||||
val params =
|
val params =
|
||||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
|
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
|
||||||
installFlags = installFlags or 0x00000004 // PackageManager.INSTALL_ALLOW_TEST
|
installFlags = installFlags or (0x00000002/*PackageManager.INSTALL_REPLACE_EXISTING*/
|
||||||
|
or 0x00000004 /*PackageManager.INSTALL_ALLOW_TEST*/)
|
||||||
PackageInstallerUtils.setInstallFlags(params, installFlags)
|
PackageInstallerUtils.setInstallFlags(params, installFlags)
|
||||||
val sessionId = packageInstaller.createSession(params)
|
val sessionId = packageInstaller.createSession(params)
|
||||||
val iSession = IPackageInstallerSession.Stub.asInterface(
|
val iSession = IPackageInstallerSession.Stub.asInterface(
|
||||||
@ -136,7 +138,7 @@ class MainActivity: FlutterActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun rootInstallApk(apkFilePath: String, result: Result) {
|
private fun rootInstallApk(apkFilePath: String, result: Result) {
|
||||||
Shell.sh("pm install -R -t " + apkFilePath).submit { out ->
|
Shell.sh("pm install -r -t " + apkFilePath).submit { out ->
|
||||||
val builder = StringBuilder()
|
val builder = StringBuilder()
|
||||||
for (data in out.getOut()) { builder.append(data) }
|
for (data in out.getOut()) { builder.append(data) }
|
||||||
result.success(builder.toString().endsWith("Success"))
|
result.success(builder.toString().endsWith("Success"))
|
||||||
@ -145,12 +147,18 @@ class MainActivity: FlutterActivity() {
|
|||||||
|
|
||||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
HiddenApiBypass.addHiddenApiExemptions("")
|
||||||
|
}
|
||||||
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
installersChannel = MethodChannel(
|
nativeChannel = MethodChannel(
|
||||||
flutterEngine.dartExecutor.binaryMessenger, "installers")
|
flutterEngine.dartExecutor.binaryMessenger, "native")
|
||||||
installersChannel!!.setMethodCallHandler {
|
nativeChannel!!.setMethodCallHandler {
|
||||||
call, result ->
|
call, result ->
|
||||||
if (call.method == "checkPermissionShizuku") {
|
if (call.method == "getSystemFont") {
|
||||||
|
val res = DefaultSystemFont().get()
|
||||||
|
result.success(res)
|
||||||
|
} else if (call.method == "checkPermissionShizuku") {
|
||||||
shizukuCheckPermission(result)
|
shizukuCheckPermission(result)
|
||||||
} else if (call.method == "checkPermissionRoot") {
|
} else if (call.method == "checkPermissionRoot") {
|
||||||
rootCheckPermission(result)
|
rootCheckPermission(result)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.7.10'
|
ext.kotlin_version = '1.8.10'
|
||||||
ext {
|
ext {
|
||||||
compileSdkVersion = 34 // or latest
|
compileSdkVersion = 34 // or latest
|
||||||
targetSdkVersion = 34 // or latest
|
targetSdkVersion = 34 // or latest
|
||||||
@ -11,9 +11,9 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
classpath "com.android.tools.build:gradle:7.4.2"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath 'dev.rikka.tools.refine:gradle-plugin:4.1.0' // Do not update!
|
classpath "dev.rikka.tools.refine:gradle-plugin:4.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Uvezi greške",
|
"importErrors": "Uvezi greške",
|
||||||
"importedXOfYApps": "{} od {} aplikacija uvezeno.",
|
"importedXOfYApps": "{} od {} aplikacija uvezeno.",
|
||||||
"followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:",
|
"followingURLsHadErrors": "Sljedeći URL-ovi su imali greške:",
|
||||||
"okay": "Dobro",
|
|
||||||
"selectURL": "Odaberite URL",
|
"selectURL": "Odaberite URL",
|
||||||
"selectURLs": "Odaberite URL-ove",
|
"selectURLs": "Odaberite URL-ove",
|
||||||
"pick": "Odaberi",
|
"pick": "Odaberi",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
|
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
|
||||||
"selectX": "Izaberite {}",
|
"selectX": "Izaberite {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Želite li ukloniti aplikaciju?",
|
"one": "Želite li ukloniti aplikaciju?",
|
||||||
"other": "Želite li ukloniti aplikacije?"
|
"other": "Želite li ukloniti aplikacije?"
|
||||||
|
@ -9,36 +9,36 @@
|
|||||||
"placeholder": "Zástupce",
|
"placeholder": "Zástupce",
|
||||||
"someErrors": "Vyskytly se nějaké chyby",
|
"someErrors": "Vyskytly se nějaké chyby",
|
||||||
"unexpectedError": "Neočekávaná chyba",
|
"unexpectedError": "Neočekávaná chyba",
|
||||||
"ok": "Okay",
|
"ok": "Ok",
|
||||||
"and": "a",
|
"and": "a",
|
||||||
"githubPATLabel": "GitHub Personal Access Token (Raises Rate Limit)",
|
"githubPATLabel": "GitHub Personal Access Token (zvyšuje limit rychlosti)",
|
||||||
"includePrereleases": "includepreleases",
|
"includePrereleases": "Zahrnout předběžné verze",
|
||||||
"fallbackToOlderReleases": "Fallback to older releases",
|
"fallbackToOlderReleases": "Přechod na starší verze",
|
||||||
"filterReleaseTitlesByRegEx": "Názvy vydání podle regulárního výrazu\filtr",
|
"filterReleaseTitlesByRegEx": "Filtrovat názvy verzí podle regulárního výrazu",
|
||||||
"invalidRegEx": "Neplatný regulární výraz",
|
"invalidRegEx": "Neplatný regulární výraz",
|
||||||
"noDescription": "Žádný popis",
|
"noDescription": "Žádný popis",
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"continue": "Pokračovat",
|
"continue": "Pokračovat",
|
||||||
"requiredInBracets": "(Required)",
|
"requiredInBracets": "(Required)",
|
||||||
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
|
"dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST",
|
||||||
"color": "barva",
|
"colour": "Barva",
|
||||||
"githubStarredRepos": "GitHub Starred Repos",
|
"githubStarredRepos": "GitHub Starred Repos",
|
||||||
"uname": "username",
|
"uname": "Uživatelské jméno",
|
||||||
"wrongArgNum": "Špatný počet předložených argumentů",
|
"wrongArgNum": "Nesprávný počet zadaných argumentů",
|
||||||
"xIsTrackOnly": "{} je určeno pouze pro sledování",
|
"xIsTrackOnly":"{} je určeno pouze pro sledování",
|
||||||
"source": "zdroj",
|
"source": "Zdroj",
|
||||||
"app": "App",
|
"app": "App",
|
||||||
"appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou 'Jen sledovány'.",
|
"appsFromSourceAreTrackOnly": "Aplikace z tohoto zdroje jsou Jen sledovány.",
|
||||||
"youPickedTrackOnly": "Vybrali jste možnost 'Jen sledovat'.",
|
"youPickedTrackOnly": "Vybrali jste možnost Jen sledovat.",
|
||||||
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
|
"trackOnlyAppDescription": "Aplikace je sledována kvůli aktualizacím, ale Obtainium ji nebude stahovat ani instalovat.",
|
||||||
"cancelled": "Zrušeno",
|
"cancelled": "Zrušeno",
|
||||||
"appAlreadyAdded": "Aplikace již přidána",
|
"appAlreadyAdded": "Aplikace již přidána",
|
||||||
"alreadyUpToDateQuestion": "App already up to date?",
|
"alreadyUpToDateQuestion": "App already up to date?",
|
||||||
"addApp": "Přidat aplikaci",
|
"addApp": "Přidat aplikaci",
|
||||||
"appSourceURL": "zdrojová adresa URL aplikace",
|
"appSourceURL": "Zdrojová adresa URL aplikace",
|
||||||
"error": "Chyba",
|
"error": "Chyba",
|
||||||
"add": "Přidat",
|
"add": "Přidat",
|
||||||
"searchSomeSourcesLabel": "Vyhledávání (pouze konkrétní zdroje)",
|
"searchSomeSourcesLabel": "Vyhledávání (pouze pro určité zdroje)",
|
||||||
"search": "Hledat",
|
"search": "Hledat",
|
||||||
"additionalOptsFor": "Další možnosti pro {}",
|
"additionalOptsFor": "Další možnosti pro {}",
|
||||||
"supportedSources": "Podporované zdroje",
|
"supportedSources": "Podporované zdroje",
|
||||||
@ -46,45 +46,45 @@
|
|||||||
"searchableInBrackets": "(s možností vyhledávání)",
|
"searchableInBrackets": "(s možností vyhledávání)",
|
||||||
"appsString": "Apky",
|
"appsString": "Apky",
|
||||||
"noApps": "Žádné aplikace",
|
"noApps": "Žádné aplikace",
|
||||||
"noAppsForFilter": "žádné aplikace pro vybraný filtr",
|
"noAppsForFilter": "Žádné aplikace pro vybraný filtr",
|
||||||
"byX": "By {}",
|
"byX": "Od {}",
|
||||||
"percentProgress": "Pokrok: {}%",
|
"percentProgress": "Pokrok: {}%",
|
||||||
"pleaseWait": "Počkejte prosím",
|
"pleaseWait": "Počkejte prosím",
|
||||||
"updateAvailable": "Aktualizace je k dispozici",
|
"updateAvailable": "Aktualizace je k dispozici",
|
||||||
"estimateInBracketsShort": "(approx.)",
|
"estimateInBracketsShort": "(approx.)",
|
||||||
"notInstalled": "Není nainstalováno",
|
"notInstalled": "Není nainstalováno",
|
||||||
"estimateInBrackets": "(přibližně)",
|
"estimateInBrackets": "(přibližně)",
|
||||||
"selectAll": "Vybrat Vše",
|
"selectAll": "Vybrat vše",
|
||||||
"deselectX": "{} deselected",
|
"deselectX": "{} deselected",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
|
"xWillBeRemovedButRemainInstalled": "{} bude odstraněn z Obtainium, ale zůstane nainstalován v zařízení.",
|
||||||
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
|
"removeSelectedAppsQuestion": "Odebrat vybrané aplikace?",
|
||||||
"removeSelectedApps": "Odebrat vybrané aplikace",
|
"removeSelectedApps": "Odebrat vybrané aplikace",
|
||||||
"updateX": "Aktualizovat {}",
|
"updateX": "Aktualizovat {}",
|
||||||
"installX": "Instalovat {}",
|
"installX": "Instalovat {}",
|
||||||
"markXTrackOnlyAsUpdated": "Označit {}\n(Track-Only)\njako aktualizované",
|
"markXTrackOnlyAsUpdated": "Označit {}\n(Jen sledované)\njako aktualizované",
|
||||||
"changeX": "Změnit {}",
|
"changeX": "Změnit {}",
|
||||||
"installUpdateApps": "Instalovat/aktualizovat aplikace",
|
"installUpdateApps": "Instalovat/aktualizovat aplikace",
|
||||||
"installUpdateSelectedApps": "Instalovat/aktualizovat vybrané aplikace",
|
"installUpdateSelectedApps": "Instalovat/aktualizovat vybrané aplikace",
|
||||||
"markXSelectedAppsAsUpdated": "označit {} vybrané aplikace jako aktuální?",
|
"markXSelectedAppsAsUpdated": "Označit {} vybrané aplikace jako aktuální?",
|
||||||
"no": "Ne",
|
"no": "Ne",
|
||||||
"yes": "ano",
|
"yes": "Ano",
|
||||||
"markSelectedAppsUpdated": "označit vybrané aplikace jako aktuální",
|
"markSelectedAppsUpdated": "Označit vybrané aplikace jako aktuální",
|
||||||
"pinToTop": "Připnout nahoru",
|
"pinToTop": "Připnout nahoru",
|
||||||
"unpinFromTop": "'Unpin Top'",
|
"unpinFromTop": "Odepnout shora",
|
||||||
"resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?",
|
"resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?",
|
||||||
"installStatusOfXWillBeResetExplanation": "Stav instalace vybraných aplikací bude resetován. To může být užitečné, pokud je verze aplikace zobrazená v Obtainium nesprávná z důvodu neúspěšných aktualizací nebo jiných problémů.",
|
"installStatusOfXWillBeResetExplanation": "Stav instalace vybraných aplikací bude resetován. To může být užitečné, pokud je verze aplikace zobrazená v Obtainium nesprávná z důvodu neúspěšných aktualizací nebo jiných problémů.",
|
||||||
"shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací",
|
"shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací",
|
||||||
"resetInstallStatus": "Obnovení stavu instalace",
|
"resetInstallStatus": "Obnovit stav instalace",
|
||||||
"more": "more",
|
"more": "Více",
|
||||||
"removeOutdatedFilter": "Odstranit filtr aplikace 'Not Current'",
|
"removeOutdatedFilter": "Odstranit filtr Neaktuální",
|
||||||
"showOutdatedOnly": "Zobrazit pouze aplikace, které nejsou aktuální",
|
"showOutdatedOnly": "Zobrazovat pouze zastaralé aplikace",
|
||||||
"filter": "Filtr",
|
"filter": "Filtr",
|
||||||
"filterActive": "Filtr *",
|
"filterActive": "Filtr *",
|
||||||
"filterApps": "Filtrovat aplikace",
|
"filterApps": "Filtrovat aplikace",
|
||||||
"appName": "název aplikace",
|
"appName": "Název aplikace",
|
||||||
"author": "Autor",
|
"author": "Autor",
|
||||||
"upToDateApps": "Apps with current version",
|
"upToDateApps": "Aktuální apky",
|
||||||
"nonInstalledApps": "Apps not installed",
|
"nonInstalledApps": "Neinstalované apky",
|
||||||
"importExport": "Import/Export",
|
"importExport": "Import/Export",
|
||||||
"settings": "Nastavení",
|
"settings": "Nastavení",
|
||||||
"exportedTo": "Exportováno do {}",
|
"exportedTo": "Exportováno do {}",
|
||||||
@ -93,76 +93,75 @@
|
|||||||
"importedX": "Importováno {}",
|
"importedX": "Importováno {}",
|
||||||
"obtainiumImport": "Obtainium Import",
|
"obtainiumImport": "Obtainium Import",
|
||||||
"importFromURLList": "Import ze seznamu URL",
|
"importFromURLList": "Import ze seznamu URL",
|
||||||
"searchQuery": "Search Query",
|
"searchQuery": "Vyhledávací dotaz",
|
||||||
"appURLList": "App URL List",
|
"appURLList": "Seznam adres aplikací",
|
||||||
"line": "line",
|
"line": "Linka",
|
||||||
"searchX": "Search {}",
|
"searchX": "Search {}",
|
||||||
"noResults": "Nebyly nalezeny žádné výsledky",
|
"noResults": "Nebyly nalezeny žádné výsledky",
|
||||||
"importX": "Import {}",
|
"importX": "Import {}",
|
||||||
"importedAppsIdDisclaimer": "Importované aplikace mohou být nesprávně zobrazeny jako \"Neinstalované\". Chcete-li to opravit, nainstalujte je znovu prostřednictvím Obtainium. To nemá vliv na data aplikací. Ovlivňuje pouze metody importu URL a třetích stran.",
|
"importedAppsIdDisclaimer": "Importované aplikace mohou být nesprávně zobrazeny jako \"Neinstalovány\". Chcete-li to opravit, nainstalujte je znovu prostřednictvím Obtainium. To nemá vliv na data aplikací. Ovlivňuje pouze metody importu URL a třetích stran.",
|
||||||
"importErrors": "Import Errors",
|
"importErrors": "Chyba importu",
|
||||||
"importedXOfYApps": "{}importováno {}aplikací.",
|
"importedXOfYApps": "{}importováno z {} aplikací.",
|
||||||
"followingURLsHadErrors": "U následujících adres URL došlo k chybám:",
|
"followingURLsHadErrors": "U následujících adres došlo k chybám:",
|
||||||
"okay": "Okay",
|
"selectURL": "Vybrat adresu",
|
||||||
"selectURL": "Select URL",
|
"selectURLs": "Select adresy",
|
||||||
"selectURLs": "Select URLs",
|
|
||||||
"pick": "Vybrat",
|
"pick": "Vybrat",
|
||||||
"theme": "Téma",
|
"theme": "Téma",
|
||||||
"dark": "Tmavé",
|
"dark": "Tmavé",
|
||||||
"light": "Světlé",
|
"light": "Světlé",
|
||||||
"followSystem": "Follow System",
|
"followSystem": "Jako systém",
|
||||||
"obtainium": "Obtainium",
|
"obtainium": "Obtainium",
|
||||||
"materialYou": "Material You",
|
"materialYou": "Material You",
|
||||||
"useBlackTheme": "Použít čistě černé tmavé téma",
|
"useBlackTheme": "Použít čistě černé tmavé téma",
|
||||||
"appSortBy": "Seřadit aplikaci podle",
|
"appSortBy": "Seřadit podle",
|
||||||
"authorName": "autor/jméno",
|
"authorName": "Autor/Jméno",
|
||||||
"nameAuthor": "jméno/autor",
|
"nameAuthor": "Jméno/Autor",
|
||||||
"asAdded": "AsAdded",
|
"asAdded": "Přidáno",
|
||||||
"appSortOrder": "Sort App By",
|
"appSortOrder": "Seřadit",
|
||||||
"ascending": "Vzestupně",
|
"ascending": "Vzestupně",
|
||||||
"descending": "Sestupně",
|
"descending": "Sestupně",
|
||||||
"bgUpdateCheckInterval": "Background Update Check Interval",
|
"bgUpdateCheckInterval": "Interval kontroly aktualizace na pozadí",
|
||||||
"neverManualOnly": "Nikdy - pouze ručně",
|
"neverManualOnly": "Nikdy - pouze ručně",
|
||||||
"appearance": "Vzhled",
|
"appearance": "Vzhled",
|
||||||
"showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace",
|
"showWebInAppView": "Zobrazit zdrojové webové stránky v zobrazení aplikace",
|
||||||
"pinUpdates": "Připnout aplikace s aktualizacemi nahoře",
|
"pinUpdates": "Připnout aplikace s aktualizacemi nahoru",
|
||||||
"updates": "Updates",
|
"updates": "Updates",
|
||||||
"sourceSpecific": "source specific",
|
"sourceSpecific": "Specifické pro zdroj",
|
||||||
"appSource": "zdroj aplikace",
|
"appSource": "Zdroj aplikace",
|
||||||
"noLogs": "Žádné protokoly",
|
"noLogs": "Žádné protokoly",
|
||||||
"appLogs": "App Logs",
|
"appLogs": "Záznamy apky",
|
||||||
"close": "Zavřít",
|
"close": "Zavřít",
|
||||||
"share": "Sdílet",
|
"share": "Sdílet",
|
||||||
"appNotFound": "App not found",
|
"appNotFound": "Aplikace nenalezena",
|
||||||
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
"obtainiumExportHyphenatedLowercase": "obtainium-export",
|
||||||
"pickAnAPK": "Vybrat APK",
|
"pickAnAPK": "Vybrat APK",
|
||||||
"appHasMoreThanOnePackage": "{} má více než jeden balíček:",
|
"appHasMoreThanOnePackage": "{} má více než jeden balíček:",
|
||||||
"deviceSupportsXArch": "Vaše zařízení podporuje architekturu CPU {}.",
|
"deviceSupportsXArch": "Vaše zařízení podporuje architekturu CPU {}.",
|
||||||
"deviceSupportsFollowingArchs": "Vaše zařízení podporuje následující architektury CPU:",
|
"deviceSupportsFollowingArchs": "Vaše zařízení podporuje následující architektury CPU:",
|
||||||
"warning": "Varování",
|
"warning": "Varování",
|
||||||
"sourceIsXButPackageFromYPrompt": "The app source is '{}' but the release package is from '{}'. Pokračovat?",
|
"sourceIsXButPackageFromYPrompt": "Zdroj aplikace je '{}', ale balíček pro vydání je z '{}'. Pokračovat?",
|
||||||
"updatesAvailable": "dostupné aktualizace",
|
"updatesAvailable": "Dostupné aktualizace",
|
||||||
"updatesAvailableNotifDescription": "Upozorňuje uživatele, že jsou k dispozici aktualizace pro jednu nebo více aplikací sledovaných Obtainium",
|
"updatesAvailableNotifDescription": "Upozorňuje uživatele, že jsou k dispozici aktualizace pro jednu nebo více aplikací sledovaných Obtainium",
|
||||||
"noNewUpdates": "Žádné nové aktualizace.",
|
"noNewUpdates": "Žádné nové aktualizace.",
|
||||||
"xHasAnUpdate": "{} má aktualizaci.",
|
"xHasAnUpdate": "{} má aktualizaci.",
|
||||||
"appsUpdated": "Aplikace aktualizovány",
|
"appsUpdated": "Aplikace aktualizovány",
|
||||||
"appsUpdatedNotifDescription": "Upozorňuje uživatele, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
|
"appsUpdatedNotifDescription": "Upozornit, že byly provedeny aktualizace jedné nebo více aplikací na pozadí",
|
||||||
"xWasUpdatedToY": "{} byl aktualizován na {}",
|
"xWasUpdatedToY": "{} byla aktualizována na {}",
|
||||||
"errorCheckingUpdates": "Chybová kontrola aktualizací",
|
"errorCheckingUpdates": "Chyba kontroly aktualizací",
|
||||||
"errorCheckingUpdatesNotifDescription": "Oznámení zobrazené při neúspěšné kontrole aktualizací na pozadí",
|
"errorCheckingUpdatesNotifDescription": "Zobrazit oznámení při neúspěšné kontrole aktualizací na pozadí",
|
||||||
"appsRemoved": "Odstraněné aplikace",
|
"appsRemoved": "Odstraněné aplikace",
|
||||||
"appsRemovedNotifDescription": "Oznámení uživateli, že jedna nebo více aplikací byly odstraněny z důvodu chyb při načítání",
|
"appsRemovedNotifDescription": "Oznámit, že jedna nebo více aplikací bylo odstraněno z důvodu chyb při načítání",
|
||||||
"xWasRemovedDueToErrorY": "{} byla odstraněna z důvodu následující chyby: {}",
|
"xWasRemovedDueToErrorY": "{} byla odstraněna z důvodu následující chyby: {}",
|
||||||
"completeAppInstallation": "Dokončit instalaci aplikace",
|
"completeAppInstallation": "Dokončit instalaci aplikace",
|
||||||
"obtainiumMustBeOpenToInstallApps": "Obtainium musí být otevřeno, aby bylo možné instalovat aplikace",
|
"obtainiumMustBeOpenToInstallApps": "Obtainium musí být otevřeno, aby bylo možné instalovat aplikace",
|
||||||
"completeAppInstallationNotifDescription": "Vyzvat uživatele k návratu do Obtainium pro dokončení instalace aplikací",
|
"completeAppInstallationNotifDescription": "Vyzvat k návratu do Obtainium pro dokončení instalace aplikací",
|
||||||
"checkingForUpdates": "Zkontrolovat aktualizace",
|
"checkingForUpdates": "Zkontrolovat aktualizace",
|
||||||
"checkingForUpdatesNotifDescription": "Dočasné oznámení zobrazené při kontrole aktualizací",
|
"checkingForUpdatesNotifDescription": "Dočasné oznámení zobrazené při kontrole aktualizací",
|
||||||
"pleaseAllowInstallPerm": "Povolte prosím Obtainium instalovat aplikace",
|
"pleaseAllowInstallPerm": "Povolte prosím Obtainium instalovat aplikace",
|
||||||
"trackOnly": "Jen sledovat",
|
"trackOnly": "Jen sledovat",
|
||||||
"errorWithHttpStatusCode": "error {}",
|
"errorWithHttpStatusCode": "Chyba {}",
|
||||||
"versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)",
|
"versionCorrectionDisabled": "Oprava verze zakázána (zásuvný modul zřejmě nefunguje)",
|
||||||
"unknown": "Unknown",
|
"unknown": "Neznám",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"never": "Nikdy",
|
"never": "Nikdy",
|
||||||
"latestVersionX": "Nejnovější verze: {}",
|
"latestVersionX": "Nejnovější verze: {}",
|
||||||
@ -170,12 +169,12 @@
|
|||||||
"lastUpdateCheckX": "Poslední kontrola aktualizace: {}",
|
"lastUpdateCheckX": "Poslední kontrola aktualizace: {}",
|
||||||
"remove": "Odebrat",
|
"remove": "Odebrat",
|
||||||
"yesMarkUpdated": "Ano, označit jako aktualizované",
|
"yesMarkUpdated": "Ano, označit jako aktualizované",
|
||||||
"fdroid": "F-Droid Official",
|
"fdroid": "Oficiální repozitář F-Droid",
|
||||||
"appIdOrName": "App ID or Name",
|
"appIdOrName": "ID nebo název apky",
|
||||||
"appId": "App ID",
|
"appId": "App ID",
|
||||||
"appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena",
|
"appWithIdOrNameNotFound": "Žádná aplikace s tímto ID nebo názvem nebyla nalezena",
|
||||||
"reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací",
|
"reposHaveMultipleApps": "Repozitáře mohou obsahovat více aplikací",
|
||||||
"fdroidThirdPartyRepo": "F-Droid Third-Party Repo",
|
"fdroidThirdPartyRepo": "F-Droid repozitář třetí strany",
|
||||||
"steam": "Steam",
|
"steam": "Steam",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamMobile": "Steam Mobile",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Steam Chat",
|
||||||
@ -183,106 +182,111 @@
|
|||||||
"markInstalled": "Označit jako nainstalovaný",
|
"markInstalled": "Označit jako nainstalovaný",
|
||||||
"update": "Aktualizovat",
|
"update": "Aktualizovat",
|
||||||
"markUpdated": "Označit jako aktuální",
|
"markUpdated": "Označit jako aktuální",
|
||||||
"additionalOptions": "Additional Options",
|
"additionalOptions": "Další možnosti",
|
||||||
"disableVersionDetection": "Zakázat detekci verze",
|
"disableVersionDetection": "Deaktivovat detekci verze",
|
||||||
"noVersionDetectionExplanation": "Tato volba by měla být použita pouze u aplikací, kde detekce verzí nefunguje správně.",
|
"noVersionDetectionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně.",
|
||||||
"downloadingX": "download {}",
|
"downloadingX": "Stáhnout {}",
|
||||||
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
|
"downloadNotifDescription": "Informuje uživatele o průběhu stahování aplikace",
|
||||||
"noAPKFound": "Žádná APK nebyla nalezena",
|
"noAPKFound": "Žádná APK nebyla nalezena",
|
||||||
"noVersionDetection": "Žádná detekce verze",
|
"noVersionDetection": "Žádná detekce verze",
|
||||||
"categorize": "Kategorizovat",
|
"categorize": "Kategorizovat",
|
||||||
"categories": "Kategorie",
|
"categories": "Kategorie",
|
||||||
"category": "kategorie",
|
"category": "Kategorie",
|
||||||
"noCategory": "Žádná kategorie",
|
"noCategory": "Žádná kategorie",
|
||||||
"noCategories": "Žádné kategorie",
|
"noCategories": "Žádné kategorie",
|
||||||
"deleteCategoriesQuestion": "Smazat kategorie?",
|
"deleteCategoriesQuestion": "Smazat kategorie?",
|
||||||
"categoryDeleteWarning": "Všechny aplikace v odstraněných kategoriích budou nastaveny na nekategorizované.",
|
"categoryDeleteWarning": "Všechny aplikace v odstraněných kategoriích budou nastaveny na nekategorizované.",
|
||||||
"addCategory": "přidat kategorii",
|
"addCategory": "Přidat kategorii",
|
||||||
"label": "štítek",
|
"label": "Štítek",
|
||||||
"language": "Jazyk",
|
"language": "Jazyk",
|
||||||
"copiedToClipboard": "zkopírováno do schránky",
|
"copiedToClipboard": "Zkopírováno do schránky",
|
||||||
"storagePermissionDenied": "povolení k ukládání odepřeno",
|
"storagePermissionDenied": "Oprávnění k ukládání odepřeno",
|
||||||
"selectedCategorizeWarning": "Toto nahradí všechna stávající nastavení kategorií pro vybrané aplikace.",
|
"selectedCategorizeWarning": "Toto nahradí všechna stávající nastavení kategorií pro vybrané aplikace.",
|
||||||
"filterAPKsByRegEx": "Filtrovat APK podle regulárního výrazu",
|
"filterAPKsByRegEx": "Filtrovat APK podle regulárního výrazu",
|
||||||
"removeFromObtainium": "Odebrat z Obtainium",
|
"removeFromObtainium": "Odebrat z Obtainium",
|
||||||
"uninstallFromDevice": "Odinstalovat ze zařízení",
|
"uninstallFromDevice": "Odinstalovat ze zařízení",
|
||||||
"onlyWorksWithNonVersionDetectApps": "Funguje pouze pro aplikace s vypnutou detekcí verze.",
|
"onlyWorksWithNonVersionDetectApps": "Funguje pouze pro aplikace s vypnutou detekcí verze.",
|
||||||
"releaseDateAsVersion": "Použít datum vydání jako verzi",
|
"releaseDateAsVersion": "Použít datum vydání jako verzi",
|
||||||
"releaseDateAsVersionExplanation": "Tato možnost by měla být použita pouze u aplikací, u kterých detekce verze nefunguje správně, ale je k dispozici datum vydání.",
|
"releaseDateAsVersionExplanation": "Tato možnost by měla být použita pouze u aplikace, kde detekce verzí nefunguje správně, ale je k dispozici datum vydání.",
|
||||||
"changes": "Změny",
|
"changes": "Změny",
|
||||||
"releaseDate": "datum vydání",
|
"releaseDate": "Datum vydání",
|
||||||
"importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)",
|
"importFromURLsInFile": "Importovat adresy URL ze souboru (např. OPML)",
|
||||||
"versionDetection": "detekce verze",
|
"versionDetection": "Detekce verze",
|
||||||
"standardVersionDetection": "standardní detekce verze",
|
"standardVersionDetection": "Standardní detekce verze",
|
||||||
"groupByCategory": "Seskupit podle kategorie",
|
"groupByCategory": "Seskupit podle kategorie",
|
||||||
"autoApkFilterByArch": "Pokud je to možné, pokuste se filtrovat soubory APK podle architektury procesoru",
|
"autoApkFilterByArch": "Pokud je to možné, pokuste se filtrovat soubory APK podle architektury procesoru",
|
||||||
"overrideSource": "Přepsat zdroj",
|
"overrideSource": "Přepsat zdroj",
|
||||||
"dontShowAgain": "Nezobrazovat znovu",
|
"dontShowAgain": "Nezobrazovat znovu",
|
||||||
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Track Only'",
|
"dontShowTrackOnlyWarnings": "Nezobrazovat varování pro 'Jen sledované'",
|
||||||
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
|
"dontShowAPKOriginWarnings": "Nezobrazovat varování pro původ APK",
|
||||||
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
|
"moveNonInstalledAppsToBottom": "Přesunout nenainstalované aplikace na konec zobrazení Aplikace",
|
||||||
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
|
"gitlabPATLabel": "GitLab Personal Access Token\n(Umožňuje vyhledávání a lepší zjišťování APK)",
|
||||||
"about": "About",
|
"about": "O",
|
||||||
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
|
"requiresCredentialsInSettings": "{}: Vyžaduje další pověření (v nastavení)",
|
||||||
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
"checkOnStart": "Zkontrolovat jednou při spuštění",
|
||||||
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
|
"tryInferAppIdFromCode": "Pokusit se určit ID aplikace ze zdrojového kódu",
|
||||||
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
|
"removeOnExternalUninstall": "Automaticky odstranit externě odinstalované aplikace",
|
||||||
"pickHighestVersionCode": "Automaticky vybrat APK s kódem nejvyšší verze",
|
"pickHighestVersionCode": "Automaticky vybrat nejvyšší verzi APK",
|
||||||
"checkUpdateOnDetailPage": "Zkontrolovat aktualizace při otevření stránky s podrobnostmi aplikace",
|
"checkUpdateOnDetailPage": "Zkontrolovat aktualizaci při otevření stránky s podrobnostmi aplikace",
|
||||||
"disablePageTransitions": "Zakázat animace pro přechody stránek",
|
"disablePageTransitions": "Zakázat animace pro přechody stránek",
|
||||||
"reversePageTransitions": "Obrátit animace pro přechody stránek",
|
"reversePageTransitions": "Obrátit animace pro přechody stránek",
|
||||||
"minStarCount": "Minimální počet hvězdiček",
|
"minStarCount": "Minimální počet hvězdiček",
|
||||||
"addInfoBelow": "Přidat tuto informaci na konec stránky",
|
"addInfoBelow": "Přidat tuto informaci na konec stránky.",
|
||||||
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
|
"addInfoInSettings": "Přidat tuto informaci do nastavení.",
|
||||||
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
|
"githubSourceNote": "Omezení rychlosti GitHub lze obejít pomocí klíče API.",
|
||||||
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
|
"gitlabSourceNote": "Extrakce GitLab APK nemusí fungovat bez klíče API",
|
||||||
"sortByLastLinkSegment": "Sort by only the last segment of the link",
|
"sortByLastLinkSegment": "Seřadit pouze podle poslední části odkazu",
|
||||||
"filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu",
|
"filterReleaseNotesByRegEx": "Filtrovat poznámky k vydání podle regulárního výrazu",
|
||||||
"customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')",
|
"customLinkFilterRegex": "Vlastní filtr odkazů APK podle regulárního výrazu (výchozí '.apk$')",
|
||||||
"appsPossiblyUpdated": "Byly provedeny pokusy o aktualizaci aplikací",
|
"appsPossiblyUpdated": "Byly provedeny pokusy o aktualizaci aplikací",
|
||||||
"appsPossiblyUpdatedNotifDescription": "Upozorňuje uživatele, že na pozadí mohly být provedeny aktualizace jedné nebo více aplikací",
|
"appsPossiblyUpdatedNotifDescription": "Upozorňuje uživatele, že na pozadí mohly být provedeny aktualizace jedné nebo více aplikací",
|
||||||
"xWasPossiblyUpdatedToY": "{} mohlo být aktualizováno na {}.",
|
"xWasPossiblyUpdatedToY":"{} mohlo být aktualizováno na {}.",
|
||||||
"enableBackgroundUpdates": "Povolit aktualizace na pozadí",
|
"enableBackgroundUpdates": "Povolit aktualizace na pozadí",
|
||||||
"backgroundUpdateReqsExplanation": "Aktualizace na pozadí nemusí být možné pro všechny aplikace.",
|
"backgroundUpdateReqsExplanation": "Aktualizace na pozadí nemusí být možná pro všechny aplikace.",
|
||||||
"backgroundUpdateLimitsExplanation": "Úspěšnost instalace na pozadí lze určit pouze v případě, že je otevřen Obtainium.",
|
"backgroundUpdateLimitsExplanation": "Úspěšnost instalace na pozadí lze určit pouze v případě, že je otevřeno Obtainium.",
|
||||||
"verifyLatestTag": "Ověřit značku 'latest'",
|
"verifyLatestTag": "Zkontrolovat značku latest",
|
||||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit",
|
"intermediateLinkRegex": "Filtrovat mezipropojení, které by mělo být navštíveno jako první",
|
||||||
"filterByLinkText": "Filter links by link text",
|
"filterByLinkText": "Filtrovat odkazy podle textu odkazu",
|
||||||
"intermediateLinkNotFound": "Intermediate link not found",
|
"intermediateLinkNotFound": "Připojený odkaz nenalezen",
|
||||||
"intermediateLink": "Intermediate link",
|
"intermediateLink": "Připojený odkaz",
|
||||||
"exemptFromBackgroundUpdates": "Vyloučit aktualizace na pozadí (pokud jsou povoleny)",
|
"exemptFromBackgroundUpdates": "Vyloučit z aktualizací na pozadí (je-li povoleno)",
|
||||||
"bgUpdatesOnWiFiOnly": "Zakázat aktualizace na pozadí, pokud není přítomna Wi-Fi",
|
"bgUpdatesOnWiFiOnly": "Deaktivovat aktualizace na pozadí, pokud není k dispozici Wi-Fi",
|
||||||
"autoSelectHighestVersionCode": "Automatický výběr nejvyššího kódu verze APK",
|
"autoSelectHighestVersionCode": "Automaticky vybrat nejvyšší verzi APK",
|
||||||
"versionExtractionRegEx": "Version Extraction RegEx",
|
"versionExtractionRegEx": "Extrakce verze pomocí RegEx",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"matchGroupToUse": "Odpovídá použité skupině",
|
||||||
"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ě",
|
||||||
"includeSettings": "Include settings",
|
"includeSettings": "Zahrnout nastavení",
|
||||||
"filterVersionsByRegEx": "Filtrovat verze podle regulárního výrazu",
|
"filterVersionsByRegEx": "Filtrovat verze podle regulárních výrazů",
|
||||||
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
|
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovanou verzi APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Seřadit vydání z rozhraní API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Obrácené třídění",
|
||||||
"takeFirstLink": "Take first link",
|
"takeFirstLink": "Použít první odkaz",
|
||||||
"skipSort": "Skip sorting",
|
"skipSort": "Přeskočit třídění",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Nabídka ladění",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Spuštěna úloha na pozadí - zkontrolujte protokoly.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Spustit kontrolu aktualizací na pozadí nyní",
|
||||||
"versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
|
"versionExtractWholePage": "Použít extrakci verze pomocí RegEx na celou stránku",
|
||||||
"installing": "Installing",
|
"installing": "Instaluji",
|
||||||
"skipUpdateNotifications": "Skip update notifications",
|
"skipUpdateNotifications": "Neposkytovat oznámení o aktualizaci",
|
||||||
"updatesAvailableNotifChannel": "dostupné aktualizace",
|
"updatesAvailableNotifChannel": "Dostupné aktualizace",
|
||||||
"appsUpdatedNotifChannel": "Aplikace aktualizovány",
|
"appsUpdatedNotifChannel": "Apky aktualizovány",
|
||||||
"appsPossiblyUpdatedNotifChannel": "Byly provedeny pokusy o aktualizaci aplikací",
|
"appsPossiblyUpdatedNotifChannel": "Byly provedeny pokusy o aktualizace aplikací",
|
||||||
"errorCheckingUpdatesNotifChannel": "Chybová kontrola aktualizací",
|
"errorCheckingUpdatesNotifChannel": "Chyba při kontrole aktualizací",
|
||||||
"appsRemovedNotifChannel": "Odstraněné aplikace",
|
"appsRemovedNotifChannel": "Odstraněné apky",
|
||||||
"downloadingXNotifChannel": "download {}",
|
"downloadingXNotifChannel": "Stáhnout {}",
|
||||||
"completeAppInstallationNotifChannel": "Dokončit instalaci aplikace",
|
"completeAppInstallationNotifChannel": "Dokončit instalaci aplikace",
|
||||||
"checkingForUpdatesNotifChannel": "Zkontrolovat aktualizace",
|
"checkingForUpdatesNotifChannel": "Zkontrolovat aktualizace",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Na aktualizace kontrolovat pouze nainstalované aplikace a aplikace označené Track only",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Odhadnout novější verzi na základě prvních třiceti číslic kontrolního součtu adresy URL APK, pokud není podporována jinak",
|
||||||
"selectX": "Select {}",
|
"selectX": "Vybrat {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Povolit souběžné stahování",
|
||||||
|
"installMethod": "Metoda instalace",
|
||||||
|
"normal": "Normální",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Správce",
|
||||||
|
"shizukuBinderNotFound": "Shizuku neběží",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Odstranit Apku?",
|
"one": "Odstranit Apku?",
|
||||||
"other": "Odstranit Apky?"
|
"other": "Odstranit Apky?"
|
||||||
@ -292,47 +296,47 @@
|
|||||||
"other": "Příliš mnoho požadavků (omezená rychlost) - zkuste to znovu za {} minut"
|
"other": "Příliš mnoho požadavků (omezená rychlost) - zkuste to znovu za {} minut"
|
||||||
},
|
},
|
||||||
"bgUpdateGotErrorRetryInMinutes": {
|
"bgUpdateGotErrorRetryInMinutes": {
|
||||||
"one": "Při kontrole aktualizace na pozadí byla zjištěna chyba {}, opakování pokusu bude naplánováno za {} minut",
|
"one": "Při kontrole aktualizace na pozadí byla zjištěna chyba {}, opakování bude naplánováno za {} minut",
|
||||||
"other": "Během kontroly aktualizace na pozadí byla zjištěna chyba {}, opakování bude naplánováno za {} minut"
|
"other": "Při kontrole aktualizací na pozadí byla zjištěna chyba {}, opakování bude naplánováno za {} minut"
|
||||||
},
|
},
|
||||||
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
"bgCheckFoundUpdatesWillNotifyIfNeeded": {
|
||||||
"one": "Při kontrole aktualizací na pozadí nalezena {}aktualizace - v případě potřeby upozorní uživatele",
|
"one": "Při kontrole aktualizací na pozadí nalezena {}aktualizace - v případě potřeby upozorní uživatele",
|
||||||
"other": "Kontrola aktualizací na pozadí našla {} aktualizací - v případě potřeby upozorní uživatele"
|
"other": "Kontrola aktualizací na pozadí nalezla {} aktualizací - v případě potřeby upozorní uživatele"
|
||||||
},
|
},
|
||||||
"apps": {
|
"apps": {
|
||||||
"one": "{} App",
|
"one": "{} Apka",
|
||||||
"other": "{} apps"
|
"other": "{} Apky"
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"jedna": "{} URL",
|
"one": "{} Adresa",
|
||||||
"other": "{} URLs"
|
"other": "{} Adres"
|
||||||
},
|
},
|
||||||
"minute": {
|
"minute": {
|
||||||
"one": "{} minute",
|
"one": "{} Minuta",
|
||||||
"other": "{} minutes"
|
"other": "{} Minut"
|
||||||
},
|
},
|
||||||
"hour": {
|
"hour": {
|
||||||
"jedna": "{} hodina",
|
"one": "{} Hodina",
|
||||||
"other": "{} hours"
|
"other": "{} Hodin"
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"jedna": "{} den",
|
"one": "{} Den",
|
||||||
"other": "{} dny"
|
"other": "{} Dnů"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "{n} log vymazán (před = {před}, po = {po})",
|
"one": "{n} Záznam vymazán (před = {before}, po = {after})",
|
||||||
"other": "{n} logů vymazáno (před = {před}, po = {po})"
|
"other": "{n} Záznamů vymazáno (před = {before}, po = {after})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} a 1 další aplikace mají aktualizace.",
|
"one": "{} a 1 další aplikace mají aktualizace.",
|
||||||
"other": "{} a {} další aplikace mají aktualizace."
|
"other": "{} a {} další aplikace mají aktualizace."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesInstalled": {
|
"xAndNMoreUpdatesInstalled": {
|
||||||
"one": "{} a {} další aplikace mají aktualizace.",
|
"one": "{} a 1 další aplikace mají aktualizace.",
|
||||||
"další": "{} a {} další aplikace byly aktualizovány."
|
"other": "{} a {} další aplikace byly aktualizovány."
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesPossiblyInstalled": {
|
"xAndNMoreUpdatesPossiblyInstalled": {
|
||||||
"one": "{} a {} další aplikace byly možná aktualizovány",
|
"one": "{} a 1 další aplikace možno aktualizovat",
|
||||||
"other": "{} a {} další aplikace mohly být aktualizovány."
|
"other": "{} a {} další aplikace mohou být aktualizovány."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Importfehler",
|
"importErrors": "Importfehler",
|
||||||
"importedXOfYApps": "{} von {} Apps importiert.",
|
"importedXOfYApps": "{} von {} Apps importiert.",
|
||||||
"followingURLsHadErrors": "Bei folgenden URLs traten Fehler auf:",
|
"followingURLsHadErrors": "Bei folgenden URLs traten Fehler auf:",
|
||||||
"okay": "Okay",
|
|
||||||
"selectURL": "URL auswählen",
|
"selectURL": "URL auswählen",
|
||||||
"selectURLs": "URLs auswählen",
|
"selectURLs": "URLs auswählen",
|
||||||
"pick": "Auswählen",
|
"pick": "Auswählen",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
|
"supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt",
|
||||||
"selectX": "Wähle {}",
|
"selectX": "Wähle {}",
|
||||||
"parallelDownloads": "Erlaube parallele Downloads",
|
"parallelDownloads": "Erlaube parallele Downloads",
|
||||||
|
"installMethod": "Installationsmethode",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku läuft nicht",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App entfernen?",
|
"one": "App entfernen?",
|
||||||
"other": "Apps entfernen?"
|
"other": "Apps entfernen?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Import Errors",
|
"importErrors": "Import Errors",
|
||||||
"importedXOfYApps": "{} of {} Apps imported.",
|
"importedXOfYApps": "{} of {} Apps imported.",
|
||||||
"followingURLsHadErrors": "The following URLs had errors:",
|
"followingURLsHadErrors": "The following URLs had errors:",
|
||||||
"okay": "Okay",
|
|
||||||
"selectURL": "Select URL",
|
"selectURL": "Select URL",
|
||||||
"selectURLs": "Select URLs",
|
"selectURLs": "Select URLs",
|
||||||
"pick": "Pick",
|
"pick": "Pick",
|
||||||
@ -287,7 +286,9 @@
|
|||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
"shizuku": "Shizuku",
|
"shizuku": "Shizuku",
|
||||||
"root": "Root",
|
"root": "Root",
|
||||||
"shizukuBinderNotFound": "Shizuku is not running",
|
"shizukuBinderNotFound": "Сompatible Shizuku service wasn't found",
|
||||||
|
"useSystemFont": "Use the system font",
|
||||||
|
"systemFontError": "Error loading the system font: {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Errores de Importación",
|
"importErrors": "Errores de Importación",
|
||||||
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
||||||
"followingURLsHadErrors": "Las siguientes URLs han tenido problemas:",
|
"followingURLsHadErrors": "Las siguientes URLs han tenido problemas:",
|
||||||
"okay": "Aceptar",
|
|
||||||
"selectURL": "Seleccionar URL",
|
"selectURL": "Seleccionar URL",
|
||||||
"selectURLs": "Seleccionar URLs",
|
"selectURLs": "Seleccionar URLs",
|
||||||
"pick": "Escoger",
|
"pick": "Escoger",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
|
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
|
||||||
"selectX": "Selecciona {}",
|
"selectX": "Selecciona {}",
|
||||||
"parallelDownloads": "Permitir descargas paralelas",
|
"parallelDownloads": "Permitir descargas paralelas",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "¿Eliminar Aplicación?",
|
"one": "¿Eliminar Aplicación?",
|
||||||
"other": "¿Eliminar Aplicaciones?"
|
"other": "¿Eliminar Aplicaciones?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "خطاهای وارد کردن",
|
"importErrors": "خطاهای وارد کردن",
|
||||||
"importedXOfYApps": "{} از {} برنامه وارد شد.",
|
"importedXOfYApps": "{} از {} برنامه وارد شد.",
|
||||||
"followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:",
|
"followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:",
|
||||||
"okay": "باشه",
|
|
||||||
"selectURL": "آدرس اینترنتی انتخاب شده",
|
"selectURL": "آدرس اینترنتی انتخاب شده",
|
||||||
"selectURLs": "آدرس های اینترنتی انتخاب شده",
|
"selectURLs": "آدرس های اینترنتی انتخاب شده",
|
||||||
"pick": "انتخاب",
|
"pick": "انتخاب",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
|
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
|
||||||
"selectX": "انتخاب کنید {}",
|
"selectX": "انتخاب کنید {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "برنامه حذف شود؟",
|
"one": "برنامه حذف شود؟",
|
||||||
"other": "برنامه ها حذف شوند؟"
|
"other": "برنامه ها حذف شوند؟"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Erreurs d'importation",
|
"importErrors": "Erreurs d'importation",
|
||||||
"importedXOfYApps": "{} sur {} applications importées.",
|
"importedXOfYApps": "{} sur {} applications importées.",
|
||||||
"followingURLsHadErrors": "Les URL suivantes comportaient des erreurs :",
|
"followingURLsHadErrors": "Les URL suivantes comportaient des erreurs :",
|
||||||
"okay": "Okay",
|
|
||||||
"selectURL": "Sélectionnez l'URL",
|
"selectURL": "Sélectionnez l'URL",
|
||||||
"selectURLs": "Sélectionnez les URLs",
|
"selectURLs": "Sélectionnez les URLs",
|
||||||
"pick": "Prendre",
|
"pick": "Prendre",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Supprimer l'application ?",
|
"one": "Supprimer l'application ?",
|
||||||
"other": "Supprimer les applications ?"
|
"other": "Supprimer les applications ?"
|
||||||
@ -335,4 +339,4 @@
|
|||||||
"one": "{} and 1 more app may have been updated.",
|
"one": "{} and 1 more app may have been updated.",
|
||||||
"other": "{} and {} more apps may have been updated."
|
"other": "{} and {} more apps may have been updated."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Importálási hibák",
|
"importErrors": "Importálási hibák",
|
||||||
"importedXOfYApps": "{}/{} app importálva.",
|
"importedXOfYApps": "{}/{} app importálva.",
|
||||||
"followingURLsHadErrors": "A következő URL-ek hibákat tartalmaztak:",
|
"followingURLsHadErrors": "A következő URL-ek hibákat tartalmaztak:",
|
||||||
"okay": "Oké",
|
|
||||||
"selectURL": "Válassza ki az URL-t",
|
"selectURL": "Válassza ki az URL-t",
|
||||||
"selectURLs": "Kiválasztott URL-ek",
|
"selectURLs": "Kiválasztott URL-ek",
|
||||||
"pick": "Válasszon",
|
"pick": "Válasszon",
|
||||||
@ -282,7 +281,12 @@
|
|||||||
"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": "Támogatja a rögzített APK URL-eket",
|
"supportFixedAPKURL": "Támogatja a rögzített APK URL-eket",
|
||||||
"selectX": "Kiválaszt {}",
|
"selectX": "Kiválaszt {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Párhuzamos letöltéseket enged",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"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?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Errori di importazione",
|
"importErrors": "Errori di importazione",
|
||||||
"importedXOfYApps": "{} app di {} importate.",
|
"importedXOfYApps": "{} app di {} importate.",
|
||||||
"followingURLsHadErrors": "I seguenti URL contengono errori:",
|
"followingURLsHadErrors": "I seguenti URL contengono errori:",
|
||||||
"okay": "Va bene",
|
|
||||||
"selectURL": "Seleziona l'URL",
|
"selectURL": "Seleziona l'URL",
|
||||||
"selectURLs": "Seleziona gli URL",
|
"selectURLs": "Seleziona gli URL",
|
||||||
"pick": "Seleziona",
|
"pick": "Seleziona",
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "インポートエラー",
|
"importErrors": "インポートエラー",
|
||||||
"importedXOfYApps": "{} / {} アプリをインポートしました",
|
"importedXOfYApps": "{} / {} アプリをインポートしました",
|
||||||
"followingURLsHadErrors": "以下のURLでエラーが発生しました:",
|
"followingURLsHadErrors": "以下のURLでエラーが発生しました:",
|
||||||
"okay": "OK",
|
|
||||||
"selectURL": "URLを選択",
|
"selectURL": "URLを選択",
|
||||||
"selectURLs": "URLを選択",
|
"selectURLs": "URLを選択",
|
||||||
"pick": "選択",
|
"pick": "選択",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "アプリを削除しますか?",
|
"one": "アプリを削除しますか?",
|
||||||
"other": "アプリを削除しますか?"
|
"other": "アプリを削除しますか?"
|
||||||
@ -335,4 +339,4 @@
|
|||||||
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。",
|
"one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。",
|
||||||
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。"
|
"other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Import foutmeldingen",
|
"importErrors": "Import foutmeldingen",
|
||||||
"importedXOfYApps": "{} van {} apps geïmporteerd.",
|
"importedXOfYApps": "{} van {} apps geïmporteerd.",
|
||||||
"followingURLsHadErrors": "De volgende URL's bevatten fouten:",
|
"followingURLsHadErrors": "De volgende URL's bevatten fouten:",
|
||||||
"okay": "Ok",
|
|
||||||
"selectURL": "Selecteer URL",
|
"selectURL": "Selecteer URL",
|
||||||
"selectURLs": "Selecteer URL's",
|
"selectURLs": "Selecteer URL's",
|
||||||
"pick": "Kies",
|
"pick": "Kies",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App verwijderen?",
|
"one": "App verwijderen?",
|
||||||
"other": "Apps verwijderen?"
|
"other": "Apps verwijderen?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Błędy importowania",
|
"importErrors": "Błędy importowania",
|
||||||
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
|
"importedXOfYApps": "Zaimportowano {} z {} aplikacji.",
|
||||||
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
|
"followingURLsHadErrors": "Następujące adresy URL zawierały błędy:",
|
||||||
"okay": "Okej",
|
|
||||||
"selectURL": "Wybierz adres URL",
|
"selectURL": "Wybierz adres URL",
|
||||||
"selectURLs": "Wybierz adresy URL",
|
"selectURLs": "Wybierz adresy URL",
|
||||||
"pick": "Wybierz",
|
"pick": "Wybierz",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
|
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
|
||||||
"selectX": "Wybierz {}",
|
"selectX": "Wybierz {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Usunąć aplikację?",
|
"one": "Usunąć aplikację?",
|
||||||
"few": "Usunąć aplikacje?",
|
"few": "Usunąć aplikacje?",
|
||||||
@ -361,4 +365,4 @@
|
|||||||
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
|
"many": "{} i {} innych apek mogło zostać zaktualizowanych.",
|
||||||
"other": "{} i {} inne apki mogły zostać zaktualizowane."
|
"other": "{} i {} inne apki mogły zostać zaktualizowane."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Erros de Importação",
|
"importErrors": "Erros de Importação",
|
||||||
"importedXOfYApps": "{} de {} Apps importados.",
|
"importedXOfYApps": "{} de {} Apps importados.",
|
||||||
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
|
"followingURLsHadErrors": "As seguintes URLs apresentaram erros:",
|
||||||
"okay": "Ok",
|
|
||||||
"selectURL": "Selecionar URL",
|
"selectURL": "Selecionar URL",
|
||||||
"selectURLs": "Selecionar URLs",
|
"selectURLs": "Selecionar URLs",
|
||||||
"pick": "Escolher",
|
"pick": "Escolher",
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Ошибка импорта",
|
"importErrors": "Ошибка импорта",
|
||||||
"importedXOfYApps": "Импортировано приложений: {} из {}",
|
"importedXOfYApps": "Импортировано приложений: {} из {}",
|
||||||
"followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:",
|
"followingURLsHadErrors": "При импорте следующие URL-адреса содержали ошибки:",
|
||||||
"okay": "Ok",
|
|
||||||
"selectURL": "Выбрать URL-адрес",
|
"selectURL": "Выбрать URL-адрес",
|
||||||
"selectURLs": "Выбрать URL-адреса",
|
"selectURLs": "Выбрать URL-адреса",
|
||||||
"pick": "Выбрать",
|
"pick": "Выбрать",
|
||||||
@ -287,7 +286,9 @@
|
|||||||
"normal": "Нормальный",
|
"normal": "Нормальный",
|
||||||
"shizuku": "Shizuku",
|
"shizuku": "Shizuku",
|
||||||
"root": "Суперпользователь",
|
"root": "Суперпользователь",
|
||||||
"shizukuBinderNotFound": "Shizuku не запущен",
|
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден",
|
||||||
|
"useSystemFont": "Использовать системный шрифт",
|
||||||
|
"systemFontError": "Ошибка загрузки системного шрифта: {}",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Importfel",
|
"importErrors": "Importfel",
|
||||||
"importedXOfYApps": "{} av {} Appar importerade.",
|
"importedXOfYApps": "{} av {} Appar importerade.",
|
||||||
"followingURLsHadErrors": "Följande URL:er hade fel:",
|
"followingURLsHadErrors": "Följande URL:er hade fel:",
|
||||||
"okay": "Okej",
|
|
||||||
"selectURL": "Välj URL",
|
"selectURL": "Välj URL",
|
||||||
"selectURLs": "Välj URL:er",
|
"selectURLs": "Välj URL:er",
|
||||||
"pick": "Välj",
|
"pick": "Välj",
|
||||||
@ -269,6 +268,11 @@
|
|||||||
"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",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Ta Bort App?",
|
"one": "Ta Bort App?",
|
||||||
"other": "Ta Bort Appar?"
|
"other": "Ta Bort Appar?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "İçe Aktarma Hataları",
|
"importErrors": "İçe Aktarma Hataları",
|
||||||
"importedXOfYApps": "{}'den {} Uygulama İçe Aktarıldı.",
|
"importedXOfYApps": "{}'den {} Uygulama İçe Aktarıldı.",
|
||||||
"followingURLsHadErrors": "Aşağıdaki URL'lerde hatalar oluştu:",
|
"followingURLsHadErrors": "Aşağıdaki URL'lerde hatalar oluştu:",
|
||||||
"okay": "Tamam",
|
|
||||||
"selectURL": "URL Seç",
|
"selectURL": "URL Seç",
|
||||||
"selectURLs": "URL'leri Seç",
|
"selectURLs": "URL'leri Seç",
|
||||||
"pick": "Seç",
|
"pick": "Seç",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Uygulamayı Kaldır?",
|
"one": "Uygulamayı Kaldır?",
|
||||||
"other": "Uygulamaları Kaldır?"
|
"other": "Uygulamaları Kaldır?"
|
||||||
@ -335,4 +339,4 @@
|
|||||||
"one": "{} ve 1 diğer uygulama muhtemelen güncellendi.",
|
"one": "{} ve 1 diğer uygulama muhtemelen güncellendi.",
|
||||||
"other": "{} ve {} daha fazla uygulama muhtemelen güncellendi."
|
"other": "{} ve {} daha fazla uygulama muhtemelen güncellendi."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "Lỗi nhập",
|
"importErrors": "Lỗi nhập",
|
||||||
"importedXOfYApps": "{} trong số {} Ứng dụng đã được nhập.",
|
"importedXOfYApps": "{} trong số {} Ứng dụng đã được nhập.",
|
||||||
"followingURLsHadErrors": "Các URL sau có lỗi:",
|
"followingURLsHadErrors": "Các URL sau có lỗi:",
|
||||||
"okay": "Ôkê",
|
|
||||||
"selectURL": "Chọn URL",
|
"selectURL": "Chọn URL",
|
||||||
"selectURLs": "Chọn URL",
|
"selectURLs": "Chọn URL",
|
||||||
"pick": "Chọn",
|
"pick": "Chọn",
|
||||||
@ -283,6 +282,11 @@
|
|||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion":{
|
"removeAppQuestion":{
|
||||||
"one": "Gỡ ứng dụng?",
|
"one": "Gỡ ứng dụng?",
|
||||||
"other": "Gỡ ứng dụng?"
|
"other": "Gỡ ứng dụng?"
|
||||||
|
@ -103,7 +103,6 @@
|
|||||||
"importErrors": "导入错误",
|
"importErrors": "导入错误",
|
||||||
"importedXOfYApps": "已导入 {} 中的 {} 个应用。",
|
"importedXOfYApps": "已导入 {} 中的 {} 个应用。",
|
||||||
"followingURLsHadErrors": "下列 URL 存在错误:",
|
"followingURLsHadErrors": "下列 URL 存在错误:",
|
||||||
"okay": "好的",
|
|
||||||
"selectURL": "选择 URL",
|
"selectURL": "选择 URL",
|
||||||
"selectURLs": "选择 URL",
|
"selectURLs": "选择 URL",
|
||||||
"pick": "选择",
|
"pick": "选择",
|
||||||
@ -223,7 +222,7 @@
|
|||||||
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
"moveNonInstalledAppsToBottom": "将未安装应用置底",
|
||||||
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
|
"gitlabPATLabel": "GitLab 个人访问令牌(启用搜索功能并增强 APK 发现)",
|
||||||
"about": "相关文档",
|
"about": "相关文档",
|
||||||
"requiresCredentialsInSettings": "{}: 此功能需要额外的凭据(在“设置”中添加)",
|
"requiresCredentialsInSettings": "{}:此功能需要额外的凭据(在“设置”中添加)",
|
||||||
"checkOnStart": "启动时进行一次检查",
|
"checkOnStart": "启动时进行一次检查",
|
||||||
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
"tryInferAppIdFromCode": "尝试从源代码推断应用 ID",
|
||||||
"removeOnExternalUninstall": "自动删除已卸载的外部应用",
|
"removeOnExternalUninstall": "自动删除已卸载的外部应用",
|
||||||
@ -236,9 +235,9 @@
|
|||||||
"addInfoInSettings": "在“设置”中添加此凭据。",
|
"addInfoInSettings": "在“设置”中添加此凭据。",
|
||||||
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
|
"githubSourceNote": "使用访问令牌可避免触发 GitHub 的 API 请求限制。",
|
||||||
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
"gitlabSourceNote": "未使用访问令牌时可能无法从 GitLab 获取 APK 文件。",
|
||||||
"sortByLastLinkSegment": "Sort by only the last segment of the link",
|
"sortByLastLinkSegment": "仅根据链接的末尾部分进行筛选",
|
||||||
"filterReleaseNotesByRegEx": "筛选发行说明(正则表达式)",
|
"filterReleaseNotesByRegEx": "筛选发行说明(正则表达式)",
|
||||||
"customLinkFilterRegex": "筛选自定义来源 APK 文件链接\n(正则表达式,默认匹配模式为“.apk$”)",
|
"customLinkFilterRegex": "筛选自定义来源的 APK 文件链接\n(正则表达式,默认匹配模式为“.apk$”)",
|
||||||
"appsPossiblyUpdated": "已尝试更新应用",
|
"appsPossiblyUpdated": "已尝试更新应用",
|
||||||
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
|
"appsPossiblyUpdatedNotifDescription": "当应用已尝试在后台更新时发送通知",
|
||||||
"xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。",
|
"xWasPossiblyUpdatedToY": "已尝试将“{}”更新至 {}。",
|
||||||
@ -246,29 +245,29 @@
|
|||||||
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
|
"backgroundUpdateReqsExplanation": "后台更新未必适用于所有的应用。",
|
||||||
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
|
"backgroundUpdateLimitsExplanation": "只有在启动 Obtainium 时才能确认安装是否成功。",
|
||||||
"verifyLatestTag": "验证“Latest”标签",
|
"verifyLatestTag": "验证“Latest”标签",
|
||||||
"intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit",
|
"intermediateLinkRegex": "筛选中转链接(正则表达式)",
|
||||||
"filterByLinkText": "Filter links by link text",
|
"filterByLinkText": "根据链接文本进行筛选",
|
||||||
"intermediateLinkNotFound": "未找到“中转”链接",
|
"intermediateLinkNotFound": "未找到中转链接",
|
||||||
"intermediateLink": "Intermediate link",
|
"intermediateLink": "中转链接",
|
||||||
"exemptFromBackgroundUpdates": "禁用后台更新\n(如果已经全局启用)",
|
"exemptFromBackgroundUpdates": "禁用后台更新(如果已经全局启用)",
|
||||||
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
|
"bgUpdatesOnWiFiOnly": "未连接 Wi-Fi 时禁用后台更新",
|
||||||
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
"autoSelectHighestVersionCode": "自动选择版本号最高的 APK 文件",
|
||||||
"versionExtractionRegEx": "提取版本号(正则表达式)",
|
"versionExtractionRegEx": "版本号提取规则(正则表达式)",
|
||||||
"matchGroupToUse": "引用的捕获组",
|
"matchGroupToUse": "引用的捕获组",
|
||||||
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
"highlightTouchTargets": "突出展示不明显的触摸区域",
|
||||||
"pickExportDir": "选择导出文件夹",
|
"pickExportDir": "选择导出文件夹",
|
||||||
"autoExportOnChanges": "数据变更时自动导出",
|
"autoExportOnChanges": "数据变更时自动导出",
|
||||||
"includeSettings": "Include settings",
|
"includeSettings": "同时导出应用设置",
|
||||||
"filterVersionsByRegEx": "筛选版本号(正则表达式)",
|
"filterVersionsByRegEx": "筛选版本号(正则表达式)",
|
||||||
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
||||||
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
||||||
"reverseSort": "反转排序",
|
"reverseSort": "反转排序",
|
||||||
"takeFirstLink": "Take first link",
|
"takeFirstLink": "选取第一个链接",
|
||||||
"skipSort": "Skip sorting",
|
"skipSort": "不进行排序",
|
||||||
"debugMenu": "调试选项",
|
"debugMenu": "调试选项",
|
||||||
"bgTaskStarted": "后台任务已启动 - 详见日志",
|
"bgTaskStarted": "后台任务已启动 - 详见日志",
|
||||||
"runBgCheckNow": "立即进行后台更新检查",
|
"runBgCheckNow": "立即进行后台更新检查",
|
||||||
"versionExtractWholePage": "将提取版本号的正则表达式应用于整个页面",
|
"versionExtractWholePage": "将版本号提取规则应用于完整页面",
|
||||||
"installing": "正在安装",
|
"installing": "正在安装",
|
||||||
"skipUpdateNotifications": "忽略更新通知",
|
"skipUpdateNotifications": "忽略更新通知",
|
||||||
"updatesAvailableNotifChannel": "更新可用",
|
"updatesAvailableNotifChannel": "更新可用",
|
||||||
@ -280,9 +279,14 @@
|
|||||||
"completeAppInstallationNotifChannel": "完成应用安装",
|
"completeAppInstallationNotifChannel": "完成应用安装",
|
||||||
"checkingForUpdatesNotifChannel": "正在检查更新",
|
"checkingForUpdatesNotifChannel": "正在检查更新",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
|
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "支持固定的 APK 文件链接",
|
||||||
"selectX": "Select {}",
|
"selectX": "选择 {}",
|
||||||
"parallelDownloads": "Allow parallel downloads",
|
"parallelDownloads": "启用并行下载",
|
||||||
|
"installMethod": "安装方式",
|
||||||
|
"normal": "常规",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku 服务未运行",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "是否删除应用?",
|
"one": "是否删除应用?",
|
||||||
"other": "是否删除应用?"
|
"other": "是否删除应用?"
|
||||||
|
@ -10,7 +10,7 @@ class APKCombo extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/+[^/]+/+[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/+[^/]+/+[^/]+');
|
||||||
var match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
var match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -34,7 +34,7 @@ class APKPure extends AppSource {
|
|||||||
url = 'https://$host${Uri.parse(url).path}';
|
url = 'https://$host${Uri.parse(url).path}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA =
|
RegExp standardUrlRegExA =
|
||||||
RegExp('^https?://$host/+[^/]+/+[^/]+(/+[^/]+)?');
|
RegExp('^https?://(www\\.)?$host/+[^/]+/+[^/]+(/+[^/]+)?');
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -16,7 +16,7 @@ class Codeberg extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -38,13 +38,14 @@ class FDroid extends AppSource {
|
|||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB =
|
RegExp standardUrlRegExB =
|
||||||
RegExp('^https?://$host/+[^/]+/+packages/+[^/]+');
|
RegExp('^https?://(www\\.)?$host/+[^/]+/+packages/+[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url =
|
url =
|
||||||
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}';
|
'https://${Uri.parse(url.substring(0, match.end)).host}/packages/${Uri.parse(url).pathSegments.last}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://$host/+packages/+[^/]+');
|
RegExp standardUrlRegExA =
|
||||||
|
RegExp('^https?://(www\\.)?$host/+packages/+[^/]+');
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -149,7 +149,7 @@ class GitHub extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
@ -346,6 +346,11 @@ class GitHub extends AppSource {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var apkUrls = getReleaseAPKUrls(releases[i]);
|
var apkUrls = getReleaseAPKUrls(releases[i]);
|
||||||
|
if (additionalSettings['apkFilterRegEx'] != null) {
|
||||||
|
var reg = RegExp(additionalSettings['apkFilterRegEx']);
|
||||||
|
apkUrls =
|
||||||
|
apkUrls.where((element) => reg.hasMatch(element.key)).toList();
|
||||||
|
}
|
||||||
if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) {
|
if (apkUrls.isEmpty && additionalSettings['trackOnly'] != true) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ class GitLab extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:html/parser.dart';
|
import 'package:html/parser.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
import 'package:obtainium/components/generated_form.dart';
|
import 'package:obtainium/components/generated_form.dart';
|
||||||
@ -100,28 +99,6 @@ class HTML extends AppSource {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
[
|
|
||||||
GeneratedFormTextField('versionExtractionRegEx',
|
|
||||||
label: tr('versionExtractionRegEx'),
|
|
||||||
required: false,
|
|
||||||
additionalValidators: [(value) => regExValidator(value)]),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
GeneratedFormTextField('matchGroupToUse',
|
|
||||||
label: tr('matchGroupToUse'),
|
|
||||||
required: false,
|
|
||||||
hint: '0',
|
|
||||||
textInputType: const TextInputType.numberWithOptions(),
|
|
||||||
additionalValidators: [
|
|
||||||
(value) {
|
|
||||||
if (value?.isEmpty == true) {
|
|
||||||
value = null;
|
|
||||||
}
|
|
||||||
value ??= '0';
|
|
||||||
return intValidator(value);
|
|
||||||
}
|
|
||||||
])
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
GeneratedFormSwitch('versionExtractWholePage',
|
GeneratedFormSwitch('versionExtractWholePage',
|
||||||
label: tr('versionExtractWholePage'))
|
label: tr('versionExtractWholePage'))
|
||||||
@ -242,9 +219,14 @@ class HTML extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
var currentUrl = standardUrl;
|
var currentUrl = standardUrl;
|
||||||
for (int i = 0;
|
if (additionalSettings['intermediateLink']?.isNotEmpty != true) {
|
||||||
i < (additionalSettings['intermediateLink']?.length ?? 0);
|
additionalSettings['intermediateLink'] = [];
|
||||||
i++) {
|
}
|
||||||
|
additionalSettings['intermediateLink'] =
|
||||||
|
additionalSettings['intermediateLink']
|
||||||
|
.where((l) => l['customLinkFilterRegex'].isNotEmpty == true)
|
||||||
|
.toList();
|
||||||
|
for (int i = 0; i < (additionalSettings['intermediateLink'].length); i++) {
|
||||||
var intLinks = await grabLinksCommon(await sourceRequest(currentUrl),
|
var intLinks = await grabLinksCommon(await sourceRequest(currentUrl),
|
||||||
additionalSettings['intermediateLink'][i]);
|
additionalSettings['intermediateLink'][i]);
|
||||||
if (intLinks.isEmpty) {
|
if (intLinks.isEmpty) {
|
||||||
@ -270,26 +252,12 @@ class HTML extends AppSource {
|
|||||||
if (additionalSettings['supportFixedAPKURL'] != true) {
|
if (additionalSettings['supportFixedAPKURL'] != true) {
|
||||||
version = rel.hashCode.toString();
|
version = rel.hashCode.toString();
|
||||||
}
|
}
|
||||||
var versionExtractionRegEx =
|
version = extractVersion(
|
||||||
additionalSettings['versionExtractionRegEx'] as String?;
|
additionalSettings['versionExtractionRegEx'] as String?,
|
||||||
if (versionExtractionRegEx?.isNotEmpty == true) {
|
additionalSettings['matchGroupToUse'] as String?,
|
||||||
var match = RegExp(versionExtractionRegEx!).allMatches(
|
additionalSettings['versionExtractWholePage'] == true
|
||||||
additionalSettings['versionExtractWholePage'] == true
|
? res.body.split('\r\n').join('\n').split('\n').join('\\n')
|
||||||
? res.body.split('\r\n').join('\n').split('\n').join('\\n')
|
: rel);
|
||||||
: rel);
|
|
||||||
if (match.isEmpty) {
|
|
||||||
throw NoVersionError();
|
|
||||||
}
|
|
||||||
String matchGroupString =
|
|
||||||
(additionalSettings['matchGroupToUse'] as String).trim();
|
|
||||||
if (matchGroupString.isEmpty) {
|
|
||||||
matchGroupString = "0";
|
|
||||||
}
|
|
||||||
version = match.last.group(int.parse(matchGroupString));
|
|
||||||
if (version?.isEmpty == true) {
|
|
||||||
throw NoVersionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rel = ensureAbsoluteUrl(rel, uri);
|
rel = ensureAbsoluteUrl(rel, uri);
|
||||||
version ??= (await checkDownloadHash(rel)).toString();
|
version ??= (await checkDownloadHash(rel)).toString();
|
||||||
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
|
return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(),
|
||||||
|
@ -13,7 +13,7 @@ class HuaweiAppGallery extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/app/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/app/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -11,7 +11,7 @@ class Mullvad extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -10,7 +10,8 @@ class NeutronCode extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/downloads/file/[^/]+');
|
RegExp standardUrlRegEx =
|
||||||
|
RegExp('^https?://(www\\.)?$host/downloads/file/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -10,13 +10,14 @@ class SourceForge extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegExB = RegExp('^https?://$host/p/[^/]+');
|
RegExp standardUrlRegExB = RegExp('^https?://(www\\.)?$host/p/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegExB.firstMatch(url.toLowerCase());
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
url =
|
url =
|
||||||
'https://${Uri.parse(url.substring(0, match.end)).host}/projects/${url.substring(Uri.parse(url.substring(0, match.end)).host.length + '/projects/'.length + 1)}';
|
'https://${Uri.parse(url.substring(0, match.end)).host}/projects/${url.substring(Uri.parse(url.substring(0, match.end)).host.length + '/projects/'.length + 1)}';
|
||||||
}
|
}
|
||||||
RegExp standardUrlRegExA = RegExp('^https?://$host/projects/[^/]+');
|
RegExp standardUrlRegExA =
|
||||||
|
RegExp('^https?://(www\\.)?$host/projects/[^/]+');
|
||||||
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
match = standardUrlRegExA.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -20,7 +20,7 @@ class SourceHut extends AppSource {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String sourceSpecificStandardizeURL(String url) {
|
String sourceSpecificStandardizeURL(String url) {
|
||||||
RegExp standardUrlRegEx = RegExp('^https?://$host/[^/]+/[^/]+');
|
RegExp standardUrlRegEx = RegExp('^https?://(www\\.)?$host/[^/]+/[^/]+');
|
||||||
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
RegExpMatch? match = standardUrlRegEx.firstMatch(url.toLowerCase());
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw InvalidURLError(name);
|
throw InvalidURLError(name);
|
||||||
|
@ -6,6 +6,8 @@ import 'package:obtainium/providers/source_provider.dart';
|
|||||||
class WhatsApp extends AppSource {
|
class WhatsApp extends AppSource {
|
||||||
WhatsApp() {
|
WhatsApp() {
|
||||||
host = 'whatsapp.com';
|
host = 'whatsapp.com';
|
||||||
|
overrideVersionDetectionFormDefault('noVersionDetection',
|
||||||
|
disableStandard: true, disableRelDate: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -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.15.1';
|
const String currentVersion = '0.15.4';
|
||||||
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
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ List<MapEntry<Locale, String>> supportedLocales = const [
|
|||||||
MapEntry(Locale('fr'), 'Français'),
|
MapEntry(Locale('fr'), 'Français'),
|
||||||
MapEntry(Locale('es'), 'Español'),
|
MapEntry(Locale('es'), 'Español'),
|
||||||
MapEntry(Locale('pl'), 'Polski'),
|
MapEntry(Locale('pl'), 'Polski'),
|
||||||
MapEntry(Locale('ru'), 'Русский язык'),
|
MapEntry(Locale('ru'), 'Русский'),
|
||||||
MapEntry(Locale('bs'), 'Bosanski'),
|
MapEntry(Locale('bs'), 'Bosanski'),
|
||||||
MapEntry(Locale('pt'), 'Brasileiro'),
|
MapEntry(Locale('pt'), 'Brasileiro'),
|
||||||
MapEntry(Locale('cs'), 'Česky'),
|
MapEntry(Locale('cs'), 'Česky'),
|
||||||
@ -152,7 +152,6 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
requiresStorageNotLow: false,
|
requiresStorageNotLow: false,
|
||||||
requiresDeviceIdle: false,
|
requiresDeviceIdle: false,
|
||||||
requiredNetworkType: NetworkType.ANY), (String taskId) async {
|
requiredNetworkType: NetworkType.ANY), (String taskId) async {
|
||||||
// We don't want periodic tasks in the foreground - ignore
|
|
||||||
await bgUpdateCheck(taskId, null);
|
await bgUpdateCheck(taskId, null);
|
||||||
BackgroundFetch.finish(taskId);
|
BackgroundFetch.finish(taskId);
|
||||||
}, (String taskId) async {
|
}, (String taskId) async {
|
||||||
@ -237,13 +236,17 @@ class _ObtainiumState extends State<Obtainium> {
|
|||||||
colorScheme: settingsProvider.theme == ThemeSettings.dark
|
colorScheme: settingsProvider.theme == ThemeSettings.dark
|
||||||
? darkColorScheme
|
? darkColorScheme
|
||||||
: lightColorScheme,
|
: lightColorScheme,
|
||||||
fontFamily: 'Metropolis'),
|
fontFamily: settingsProvider.useSystemFont
|
||||||
|
? 'SystemFont'
|
||||||
|
: 'Metropolis'),
|
||||||
darkTheme: ThemeData(
|
darkTheme: ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
colorScheme: settingsProvider.theme == ThemeSettings.light
|
colorScheme: settingsProvider.theme == ThemeSettings.light
|
||||||
? lightColorScheme
|
? lightColorScheme
|
||||||
: darkColorScheme,
|
: darkColorScheme,
|
||||||
fontFamily: 'Metropolis'),
|
fontFamily: settingsProvider.useSystemFont
|
||||||
|
? 'SystemFont'
|
||||||
|
: 'Metropolis'),
|
||||||
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
|
home: Shortcuts(shortcuts: <LogicalKeySet, Intent>{
|
||||||
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
|
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
|
||||||
}, child: const HomePage()));
|
}, child: const HomePage()));
|
||||||
|
@ -286,10 +286,14 @@ class AddAppPageState extends State<AddAppPage> {
|
|||||||
selectedByDefault: true,
|
selectedByDefault: true,
|
||||||
onlyOneSelectionAllowed: false,
|
onlyOneSelectionAllowed: false,
|
||||||
titlesAreLinks: false,
|
titlesAreLinks: false,
|
||||||
|
deselectThese: settingsProvider.searchDeselected,
|
||||||
);
|
);
|
||||||
}) ??
|
}) ??
|
||||||
[];
|
[];
|
||||||
if (searchSources.isNotEmpty) {
|
if (searchSources.isNotEmpty) {
|
||||||
|
settingsProvider.searchDeselected = sourceStrings.keys
|
||||||
|
.where((s) => !searchSources.contains(s))
|
||||||
|
.toList();
|
||||||
var results = await Future.wait(sourceProvider.sources
|
var results = await Future.wait(sourceProvider.sources
|
||||||
.where((e) => searchSources.contains(e.name))
|
.where((e) => searchSources.contains(e.name))
|
||||||
.map((e) async {
|
.map((e) async {
|
||||||
@ -306,7 +310,6 @@ class AddAppPageState extends State<AddAppPage> {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// .then((results) async {
|
|
||||||
// Interleave results instead of simple reduce
|
// Interleave results instead of simple reduce
|
||||||
Map<String, List<String>> res = {};
|
Map<String, List<String>> res = {};
|
||||||
var si = 0;
|
var si = 0;
|
||||||
|
@ -5,6 +5,7 @@ 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/components/generated_form_modal.dart';
|
||||||
import 'package:obtainium/custom_errors.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';
|
||||||
@ -76,14 +77,39 @@ class _HomePageState extends State<HomePage> {
|
|||||||
try {
|
try {
|
||||||
if (action == 'add') {
|
if (action == 'add') {
|
||||||
await goToAddApp(data);
|
await goToAddApp(data);
|
||||||
} else if (action == 'app') {
|
} else if (action == 'app' || action == 'apps') {
|
||||||
await context
|
var dataStr = Uri.decodeComponent(data);
|
||||||
.read<AppsProvider>()
|
if (await showDialog(
|
||||||
.import('{ "apps": [${Uri.decodeComponent(data)}] }');
|
context: context,
|
||||||
} else if (action == 'apps') {
|
builder: (BuildContext ctx) {
|
||||||
await context
|
return GeneratedFormModal(
|
||||||
.read<AppsProvider>()
|
title: tr('importX', args: [
|
||||||
.import('{ "apps": ${Uri.decodeComponent(data)} }');
|
action == 'app' ? tr('app') : tr('appsString')
|
||||||
|
]),
|
||||||
|
items: const [],
|
||||||
|
additionalWidgets: [
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('Raw JSON'),
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
dataStr,
|
||||||
|
style: const TextStyle(fontFamily: 'monospace'),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}) !=
|
||||||
|
null) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
var result = await context.read<AppsProvider>().import(
|
||||||
|
action == 'app'
|
||||||
|
? '{ "apps": [$dataStr] }'
|
||||||
|
: '{ "apps": $dataStr }');
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
showMessage(
|
||||||
|
tr('importedX', args: [plural('apps', result.key)]), context);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw ObtainiumError(tr('unknown'));
|
throw ObtainiumError(tr('unknown'));
|
||||||
}
|
}
|
||||||
|
@ -590,7 +590,7 @@ class _ImportErrorDialogState extends State<ImportErrorDialog> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(null);
|
Navigator.of(context).pop(null);
|
||||||
},
|
},
|
||||||
child: Text(tr('okay')))
|
child: Text(tr('ok')))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -604,11 +604,13 @@ class SelectionModal extends StatefulWidget {
|
|||||||
this.selectedByDefault = true,
|
this.selectedByDefault = true,
|
||||||
this.onlyOneSelectionAllowed = false,
|
this.onlyOneSelectionAllowed = false,
|
||||||
this.titlesAreLinks = true,
|
this.titlesAreLinks = true,
|
||||||
this.title});
|
this.title,
|
||||||
|
this.deselectThese = const []});
|
||||||
|
|
||||||
String? title;
|
String? title;
|
||||||
Map<String, List<String>> entries;
|
Map<String, List<String>> entries;
|
||||||
bool selectedByDefault;
|
bool selectedByDefault;
|
||||||
|
List<String> deselectThese;
|
||||||
bool onlyOneSelectionAllowed;
|
bool onlyOneSelectionAllowed;
|
||||||
bool titlesAreLinks;
|
bool titlesAreLinks;
|
||||||
|
|
||||||
@ -622,9 +624,13 @@ class _SelectionModalState extends State<SelectionModal> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
for (var url in widget.entries.entries) {
|
for (var entry in widget.entries.entries) {
|
||||||
entrySelections.putIfAbsent(url,
|
entrySelections.putIfAbsent(
|
||||||
() => widget.selectedByDefault && !widget.onlyOneSelectionAllowed);
|
entry,
|
||||||
|
() =>
|
||||||
|
widget.selectedByDefault &&
|
||||||
|
!widget.onlyOneSelectionAllowed &&
|
||||||
|
!widget.deselectThese.contains(entry.key));
|
||||||
}
|
}
|
||||||
if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) {
|
if (widget.selectedByDefault && widget.onlyOneSelectionAllowed) {
|
||||||
selectOnlyOne(widget.entries.entries.first.key);
|
selectOnlyOne(widget.entries.entries.first.key);
|
||||||
|
@ -7,6 +7,7 @@ import 'package:obtainium/custom_errors.dart';
|
|||||||
import 'package:obtainium/main.dart';
|
import 'package:obtainium/main.dart';
|
||||||
import 'package:obtainium/providers/apps_provider.dart';
|
import 'package:obtainium/providers/apps_provider.dart';
|
||||||
import 'package:obtainium/providers/logs_provider.dart';
|
import 'package:obtainium/providers/logs_provider.dart';
|
||||||
|
import 'package:obtainium/providers/native_provider.dart';
|
||||||
import 'package:obtainium/providers/settings_provider.dart';
|
import 'package:obtainium/providers/settings_provider.dart';
|
||||||
import 'package:obtainium/providers/source_provider.dart';
|
import 'package:obtainium/providers/source_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -350,8 +351,6 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
height16,
|
height16,
|
||||||
installMethodDropdown,
|
|
||||||
height16,
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@ -364,6 +363,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
installMethodDropdown,
|
||||||
height32,
|
height32,
|
||||||
Text(
|
Text(
|
||||||
tr('sourceSpecific'),
|
tr('sourceSpecific'),
|
||||||
@ -408,6 +408,30 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
height16,
|
height16,
|
||||||
localeDropdown,
|
localeDropdown,
|
||||||
height16,
|
height16,
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(child: Text(tr('useSystemFont'))),
|
||||||
|
Switch(
|
||||||
|
value: settingsProvider.useSystemFont,
|
||||||
|
onChanged: (useSystemFont) {
|
||||||
|
if (useSystemFont) {
|
||||||
|
NativeFeatures.loadSystemFont().then((fontLoadRes) {
|
||||||
|
if (fontLoadRes == 'ok') {
|
||||||
|
settingsProvider.useSystemFont = true;
|
||||||
|
} else {
|
||||||
|
showError(ObtainiumError(
|
||||||
|
tr('systemFontError', args: [fontLoadRes])
|
||||||
|
), context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
settingsProvider.useSystemFont = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
|
height16,
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
@ -32,7 +32,7 @@ import 'package:http/http.dart';
|
|||||||
import 'package:android_intent_plus/android_intent.dart';
|
import 'package:android_intent_plus/android_intent.dart';
|
||||||
import 'package:flutter_archive/flutter_archive.dart';
|
import 'package:flutter_archive/flutter_archive.dart';
|
||||||
import 'package:shared_storage/shared_storage.dart' as saf;
|
import 'package:shared_storage/shared_storage.dart' as saf;
|
||||||
import 'installers_provider.dart';
|
import 'native_provider.dart';
|
||||||
|
|
||||||
final pm = AndroidPackageManager();
|
final pm = AndroidPackageManager();
|
||||||
|
|
||||||
@ -522,12 +522,12 @@ class AppsProvider with ChangeNotifier {
|
|||||||
code = await AndroidPackageInstaller.installApk(
|
code = await AndroidPackageInstaller.installApk(
|
||||||
apkFilePath: file.file.path);
|
apkFilePath: file.file.path);
|
||||||
case InstallMethodSettings.shizuku:
|
case InstallMethodSettings.shizuku:
|
||||||
code = (await Installers.installWithShizuku(
|
code = (await NativeFeatures.installWithShizuku(
|
||||||
apkFileUri: file.file.uri.toString()))
|
apkFileUri: file.file.uri.toString()))
|
||||||
? 0
|
? 0
|
||||||
: 1;
|
: 1;
|
||||||
case InstallMethodSettings.root:
|
case InstallMethodSettings.root:
|
||||||
code = (await Installers.installWithRoot(apkFilePath: file.file.path))
|
code = (await NativeFeatures.installWithRoot(apkFilePath: file.file.path))
|
||||||
? 0
|
? 0
|
||||||
: 1;
|
: 1;
|
||||||
}
|
}
|
||||||
@ -694,14 +694,14 @@ class AppsProvider with ChangeNotifier {
|
|||||||
throw ObtainiumError(tr('cancelled'));
|
throw ObtainiumError(tr('cancelled'));
|
||||||
}
|
}
|
||||||
case InstallMethodSettings.shizuku:
|
case InstallMethodSettings.shizuku:
|
||||||
int code = await Installers.checkPermissionShizuku();
|
int code = await NativeFeatures.checkPermissionShizuku();
|
||||||
if (code == -1) {
|
if (code == -1) {
|
||||||
throw ObtainiumError(tr('shizukuBinderNotFound'));
|
throw ObtainiumError(tr('shizukuBinderNotFound'));
|
||||||
} else if (code == 0) {
|
} else if (code == 0) {
|
||||||
throw ObtainiumError(tr('cancelled'));
|
throw ObtainiumError(tr('cancelled'));
|
||||||
}
|
}
|
||||||
case InstallMethodSettings.root:
|
case InstallMethodSettings.root:
|
||||||
if (!(await Installers.checkPermissionRoot())) {
|
if (!(await NativeFeatures.checkPermissionRoot())) {
|
||||||
throw ObtainiumError(tr('cancelled'));
|
throw ObtainiumError(tr('cancelled'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
class Installers {
|
class NativeFeatures {
|
||||||
static const MethodChannel _channel = MethodChannel('installers');
|
static const MethodChannel _channel = MethodChannel('native');
|
||||||
|
static bool _systemFontLoaded = false;
|
||||||
static bool _callbacksApplied = false;
|
static bool _callbacksApplied = false;
|
||||||
static int _resPermShizuku = -2; // not set
|
static int _resPermShizuku = -2; // not set
|
||||||
|
|
||||||
static Future waitWhile(bool Function() test,
|
static Future<ByteData> _readFileBytes(String path) async {
|
||||||
|
var file = File(path);
|
||||||
|
var bytes = await file.readAsBytes();
|
||||||
|
return ByteData.view(bytes.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future _handleCalls(MethodCall call) async {
|
||||||
|
if (call.method == 'resPermShizuku') {
|
||||||
|
_resPermShizuku = call.arguments['res'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future _waitWhile(bool Function() test,
|
||||||
[Duration pollInterval = const Duration(milliseconds: 250)]) {
|
[Duration pollInterval = const Duration(milliseconds: 250)]) {
|
||||||
var completer = Completer();
|
var completer = Completer();
|
||||||
check() {
|
check() {
|
||||||
@ -20,20 +34,25 @@ class Installers {
|
|||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future handleCalls(MethodCall call) async {
|
static Future<String> loadSystemFont() async {
|
||||||
if (call.method == 'resPermShizuku') {
|
if (_systemFontLoaded) { return "ok"; }
|
||||||
_resPermShizuku = call.arguments['res'];
|
var getFontRes = await _channel.invokeMethod('getSystemFont');
|
||||||
}
|
if (getFontRes[0] != '/') { return getFontRes; } // Error
|
||||||
|
var fontLoader = FontLoader('SystemFont');
|
||||||
|
fontLoader.addFont(_readFileBytes(getFontRes));
|
||||||
|
await fontLoader.load();
|
||||||
|
_systemFontLoaded = true;
|
||||||
|
return "ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<int> checkPermissionShizuku() async {
|
static Future<int> checkPermissionShizuku() async {
|
||||||
if (!_callbacksApplied) {
|
if (!_callbacksApplied) {
|
||||||
_channel.setMethodCallHandler(handleCalls);
|
_channel.setMethodCallHandler(_handleCalls);
|
||||||
_callbacksApplied = true;
|
_callbacksApplied = true;
|
||||||
}
|
}
|
||||||
int res = await _channel.invokeMethod('checkPermissionShizuku');
|
int res = await _channel.invokeMethod('checkPermissionShizuku');
|
||||||
if(res == -2) {
|
if (res == -2) {
|
||||||
await waitWhile(() => _resPermShizuku == -2);
|
await _waitWhile(() => _resPermShizuku == -2);
|
||||||
res = _resPermShizuku;
|
res = _resPermShizuku;
|
||||||
_resPermShizuku = -2;
|
_resPermShizuku = -2;
|
||||||
}
|
}
|
@ -51,6 +51,15 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get useSystemFont {
|
||||||
|
return prefs?.getBool('useSystemFont') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set useSystemFont(bool useSystemFont) {
|
||||||
|
prefs?.setBool('useSystemFont', useSystemFont);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
InstallMethodSettings get installMethod {
|
InstallMethodSettings get installMethod {
|
||||||
return InstallMethodSettings.values[
|
return InstallMethodSettings.values[
|
||||||
prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];
|
prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];
|
||||||
@ -446,4 +455,13 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
prefs?.setBool('parallelDownloads', val);
|
prefs?.setBool('parallelDownloads', val);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> get searchDeselected {
|
||||||
|
return prefs?.getStringList('searchDeselected') ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
set searchDeselected(List<String> list) {
|
||||||
|
prefs?.setStringList('searchDeselected', list);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ appJSONCompatibilityModifiers(Map<String, dynamic> json) {
|
|||||||
if ((additionalSettings['intermediateLink']?.length ?? 0) > 0) {
|
if ((additionalSettings['intermediateLink']?.length ?? 0) > 0) {
|
||||||
additionalSettings['intermediateLink'] =
|
additionalSettings['intermediateLink'] =
|
||||||
additionalSettings['intermediateLink'].where((e) {
|
additionalSettings['intermediateLink'].where((e) {
|
||||||
return e['intermediateLinkRegex']?.isNotEmpty == true;
|
return e['customLinkFilterRegex']?.isNotEmpty == true;
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,6 +444,16 @@ abstract class AppSource {
|
|||||||
label: tr('trackOnly'),
|
label: tr('trackOnly'),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('versionExtractionRegEx',
|
||||||
|
label: tr('versionExtractionRegEx'),
|
||||||
|
required: false,
|
||||||
|
additionalValidators: [(value) => regExValidator(value)]),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
GeneratedFormTextField('matchGroupToUse',
|
||||||
|
label: tr('matchGroupToUse'), required: false, hint: '\$0')
|
||||||
|
],
|
||||||
[
|
[
|
||||||
GeneratedFormDropdown(
|
GeneratedFormDropdown(
|
||||||
'versionDetection',
|
'versionDetection',
|
||||||
@ -580,6 +590,57 @@ bool isTempId(App app) {
|
|||||||
return RegExp('^[0-9]+\$').hasMatch(app.id);
|
return RegExp('^[0-9]+\$').hasMatch(app.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replaceMatchGroupsInString(RegExpMatch match, String matchGroupString) {
|
||||||
|
if (RegExp('^\\d+\$').hasMatch(matchGroupString)) {
|
||||||
|
matchGroupString = '\$$matchGroupString';
|
||||||
|
}
|
||||||
|
// Regular expression to match numbers in the input string
|
||||||
|
final numberRegex = RegExp(r'\$\d+');
|
||||||
|
// Extract all numbers from the input string
|
||||||
|
final numbers = numberRegex.allMatches(matchGroupString);
|
||||||
|
if (numbers.isEmpty) {
|
||||||
|
// If no numbers found, return the original string
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Replace numbers with corresponding match groups
|
||||||
|
var outputString = matchGroupString;
|
||||||
|
for (final numberMatch in numbers) {
|
||||||
|
final number = numberMatch.group(0)!;
|
||||||
|
final matchGroup = match.group(int.parse(number.substring(1))) ?? '';
|
||||||
|
// Check if the number is preceded by a single backslash
|
||||||
|
final isEscaped = outputString.contains('\\$number');
|
||||||
|
// Replace the number with the corresponding match group
|
||||||
|
if (!isEscaped) {
|
||||||
|
outputString = outputString.replaceAll(number, matchGroup);
|
||||||
|
} else {
|
||||||
|
outputString = outputString.replaceAll('\\$number', number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outputString;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? extractVersion(String? versionExtractionRegEx, String? matchGroupString,
|
||||||
|
String stringToCheck) {
|
||||||
|
if (versionExtractionRegEx?.isNotEmpty == true) {
|
||||||
|
String? version = stringToCheck;
|
||||||
|
var match = RegExp(versionExtractionRegEx!).allMatches(version);
|
||||||
|
if (match.isEmpty) {
|
||||||
|
throw NoVersionError();
|
||||||
|
}
|
||||||
|
matchGroupString = matchGroupString?.trim() ?? '';
|
||||||
|
if (matchGroupString.isEmpty) {
|
||||||
|
matchGroupString = "0";
|
||||||
|
}
|
||||||
|
version = replaceMatchGroupsInString(match.last, matchGroupString);
|
||||||
|
if (version?.isNotEmpty != true) {
|
||||||
|
throw NoVersionError();
|
||||||
|
}
|
||||||
|
return version!;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SourceProvider {
|
class SourceProvider {
|
||||||
// Add more source classes here so they are available via the service
|
// Add more source classes here so they are available via the service
|
||||||
List<AppSource> get sources => [
|
List<AppSource> get sources => [
|
||||||
@ -679,6 +740,18 @@ class SourceProvider {
|
|||||||
String standardUrl = source.standardizeUrl(url);
|
String standardUrl = source.standardizeUrl(url);
|
||||||
APKDetails apk =
|
APKDetails apk =
|
||||||
await source.getLatestAPKDetails(standardUrl, additionalSettings);
|
await source.getLatestAPKDetails(standardUrl, additionalSettings);
|
||||||
|
|
||||||
|
if (source.runtimeType != HTML().runtimeType) {
|
||||||
|
// HTML does it separately
|
||||||
|
String? extractedVersion = extractVersion(
|
||||||
|
additionalSettings['versionExtractionRegEx'] as String?,
|
||||||
|
additionalSettings['matchGroupToUse'] as String?,
|
||||||
|
apk.version);
|
||||||
|
if (extractedVersion != null) {
|
||||||
|
apk.version = extractedVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (additionalSettings['versionDetection'] == 'releaseDateAsVersion' &&
|
if (additionalSettings['versionDetection'] == 'releaseDateAsVersion' &&
|
||||||
apk.releaseDate != null) {
|
apk.releaseDate != null) {
|
||||||
apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString();
|
apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString();
|
||||||
|
@ -46,10 +46,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b"
|
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.9"
|
version: "3.4.10"
|
||||||
args:
|
args:
|
||||||
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.15.1+237 # When changing this, update the tag in main() accordingly
|
version: 0.15.4+240 # 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'
|
||||||
|
Reference in New Issue
Block a user