mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-16 14:46:44 +02:00
Compare commits
40 Commits
v0.14.37-b
...
v0.14.41-b
Author | SHA1 | Date | |
---|---|---|---|
45fa0a165a | |||
0e5c07a078 | |||
601a742c71 | |||
c972401b6e | |||
024e81cf01 | |||
975ed402d5 | |||
b9e8083744 | |||
bb859708bc | |||
3cf2c221ac | |||
6edd7edcd2 | |||
4e26a02d78 | |||
bb36a57053 | |||
b291c800f1 | |||
b63a798d86 | |||
eacf3777a4 | |||
a5a7436bb1 | |||
2a4cc35df7 | |||
cdccf58b76 | |||
27300383a1 | |||
375b9bce30 | |||
b6b8db48df | |||
36e6c267b9 | |||
13066b3b4a | |||
ccbe9d00c8 | |||
ce291582cb | |||
bb37bc3b51 | |||
5a7747acd1 | |||
1bc2ec9461 | |||
2b977fc2b0 | |||
de60c4ee9e | |||
de67e40c00 | |||
cc4b016c64 | |||
f64f561d6f | |||
80bddf8a6b | |||
cbaaec961c | |||
5477b3f936 | |||
fd59a93ede | |||
cd316b7138 | |||
d1955192ed | |||
9beb839bf4 |
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -9,6 +9,7 @@ assignees: ''
|
|||||||
|
|
||||||
**Prerequisites**
|
**Prerequisites**
|
||||||
<!-- Please ensure your request is not part of an existing issue. -->
|
<!-- Please ensure your request is not part of an existing issue. -->
|
||||||
|
<!-- Please ensure you have checked the Obtainium Wiki. -->
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
<!-- A clear and concise description of what the bug is. -->
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -9,6 +9,7 @@ assignees: ''
|
|||||||
|
|
||||||
**Prerequisites**
|
**Prerequisites**
|
||||||
<!-- Please ensure your request is not part of an existing issue. -->
|
<!-- Please ensure your request is not part of an existing issue. -->
|
||||||
|
<!-- Please ensure you have checked the Obtainium Wiki. -->
|
||||||
|
|
||||||
**Describe the feature**
|
**Describe the feature**
|
||||||
<!-- A clear and concise description of what you want to happen.
|
<!-- A clear and concise description of what you want to happen.
|
||||||
|
@ -6,6 +6,8 @@ Obtainium allows you to install and update Apps directly from their releases pag
|
|||||||
|
|
||||||
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
|
Motivation: [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0)
|
||||||
|
|
||||||
|
Wiki: [https://github.com/ImranR98/Obtainium/wiki](https://github.com/ImranR98/Obtainium/wiki)
|
||||||
|
|
||||||
Currently supported App sources:
|
Currently supported App sources:
|
||||||
- Open Source - General:
|
- Open Source - General:
|
||||||
- [GitHub](https://github.com/)
|
- [GitHub](https://github.com/)
|
||||||
|
@ -23,6 +23,7 @@ if (flutterVersionName == null) {
|
|||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'dev.rikka.tools.refine'
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
def keystoreProperties = new Properties()
|
def keystoreProperties = new Properties()
|
||||||
@ -32,7 +33,7 @@ if (keystorePropertiesFile.exists()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 33
|
compileSdkVersion 34
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@ -52,8 +53,8 @@ android {
|
|||||||
applicationId "dev.imranr.obtainium"
|
applicationId "dev.imranr.obtainium"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
|
||||||
minSdkVersion 23
|
minSdkVersion 24
|
||||||
targetSdkVersion 33
|
targetSdkVersion 34
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
@ -90,6 +91,24 @@ flutter {
|
|||||||
source '../..'
|
source '../..'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven { url 'https://jitpack.io' }
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
|
||||||
|
def shizuku_version = '13.1.5'
|
||||||
|
implementation "dev.rikka.shizuku:api:$shizuku_version"
|
||||||
|
implementation "dev.rikka.shizuku:provider:$shizuku_version"
|
||||||
|
|
||||||
|
def hidden_api_version = '4.1.0'
|
||||||
|
// 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.hidden:compat:$hidden_api_version"
|
||||||
|
compileOnly "dev.rikka.hidden:stub:$hidden_api_version"
|
||||||
|
|
||||||
|
implementation "com.github.topjohnwu.libsu:core:5.2.2"
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,13 @@
|
|||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
<provider
|
||||||
|
android:name="rikka.shizuku.ShizukuProvider"
|
||||||
|
android:authorities="${applicationId}.shizuku"
|
||||||
|
android:multiprocess="false"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package dev.imranr.obtainium
|
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
|
||||||
}
|
|
171
android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt
Normal file
171
android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
package dev.imranr.obtainium
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentSender
|
||||||
|
import android.content.pm.IPackageInstaller
|
||||||
|
import android.content.pm.IPackageInstallerSession
|
||||||
|
import android.content.pm.PackageInstaller
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Process
|
||||||
|
import androidx.annotation.NonNull
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import dev.imranr.obtainium.util.IIntentSenderAdaptor
|
||||||
|
import dev.imranr.obtainium.util.IntentSenderUtils
|
||||||
|
import dev.imranr.obtainium.util.PackageInstallerUtils
|
||||||
|
import dev.imranr.obtainium.util.ShizukuSystemServerApi
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
|
import java.io.IOException
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
import rikka.shizuku.Shizuku
|
||||||
|
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
||||||
|
import rikka.shizuku.ShizukuBinderWrapper
|
||||||
|
|
||||||
|
class MainActivity: FlutterActivity() {
|
||||||
|
private var installersChannel: MethodChannel? = null
|
||||||
|
private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random()
|
||||||
|
|
||||||
|
private fun shizukuCheckPermission(result: Result) {
|
||||||
|
try {
|
||||||
|
if (Shizuku.isPreV11()) { // Unsupported
|
||||||
|
result.success(-1)
|
||||||
|
} else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
result.success(1)
|
||||||
|
} else if (Shizuku.shouldShowRequestPermissionRationale()) { // Deny and don't ask again
|
||||||
|
result.success(0)
|
||||||
|
} else {
|
||||||
|
Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE)
|
||||||
|
result.success(-2)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { // If shizuku not running
|
||||||
|
result.success(-1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val shizukuRequestPermissionResultListener = OnRequestPermissionResultListener {
|
||||||
|
requestCode: Int, grantResult: Int ->
|
||||||
|
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
|
||||||
|
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
|
||||||
|
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shizukuInstallApk(apkFileUri: String, result: Result) {
|
||||||
|
val uri = Uri.parse(apkFileUri)
|
||||||
|
var res = false
|
||||||
|
var session: PackageInstaller.Session? = null
|
||||||
|
try {
|
||||||
|
val iPackageInstaller: IPackageInstaller =
|
||||||
|
ShizukuSystemServerApi.PackageManager_getPackageInstaller()
|
||||||
|
val isRoot = Shizuku.getUid() == 0
|
||||||
|
// The reason for use "com.android.shell" as installer package under adb
|
||||||
|
// is that getMySessions will check installer package's owner
|
||||||
|
val installerPackageName = if (isRoot) packageName else "com.android.shell"
|
||||||
|
var installerAttributionTag: String? = null
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
installerAttributionTag = attributionTag
|
||||||
|
}
|
||||||
|
val userId = if (isRoot) Process.myUserHandle().hashCode() else 0
|
||||||
|
val packageInstaller = PackageInstallerUtils.createPackageInstaller(
|
||||||
|
iPackageInstaller, installerPackageName, installerAttributionTag, userId)
|
||||||
|
val params =
|
||||||
|
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
|
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
|
||||||
|
installFlags = installFlags or 0x00000004 // PackageManager.INSTALL_ALLOW_TEST
|
||||||
|
PackageInstallerUtils.setInstallFlags(params, installFlags)
|
||||||
|
val sessionId = packageInstaller.createSession(params)
|
||||||
|
val iSession = IPackageInstallerSession.Stub.asInterface(
|
||||||
|
ShizukuBinderWrapper(iPackageInstaller.openSession(sessionId).asBinder()))
|
||||||
|
session = PackageInstallerUtils.createSession(iSession)
|
||||||
|
val inputStream = contentResolver.openInputStream(uri)
|
||||||
|
val openedSession = session.openWrite("apk.apk", 0, -1)
|
||||||
|
val buffer = ByteArray(8192)
|
||||||
|
var length: Int
|
||||||
|
try {
|
||||||
|
while (inputStream!!.read(buffer).also { length = it } > 0) {
|
||||||
|
openedSession.write(buffer, 0, length)
|
||||||
|
openedSession.flush()
|
||||||
|
session.fsync(openedSession)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
inputStream!!.close()
|
||||||
|
openedSession.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val results = arrayOf<Intent?>(null)
|
||||||
|
val countDownLatch = CountDownLatch(1)
|
||||||
|
val intentSender: IntentSender =
|
||||||
|
IntentSenderUtils.newInstance(object : IIntentSenderAdaptor() {
|
||||||
|
override fun send(intent: Intent?) {
|
||||||
|
results[0] = intent
|
||||||
|
countDownLatch.countDown()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
session.commit(intentSender)
|
||||||
|
countDownLatch.await()
|
||||||
|
res = results[0]!!.getIntExtra(
|
||||||
|
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE) == 0
|
||||||
|
} catch (_: Exception) {
|
||||||
|
res = false
|
||||||
|
} finally {
|
||||||
|
if (session != null) {
|
||||||
|
try {
|
||||||
|
session.close()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
res = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.success(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rootCheckPermission(result: Result) {
|
||||||
|
Shell.getShell(Shell.GetShellCallback(
|
||||||
|
fun(shell: Shell) {
|
||||||
|
result.success(shell.isRoot)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rootInstallApk(apkFilePath: String, result: Result) {
|
||||||
|
Shell.sh("pm install -R -t " + apkFilePath).submit { out ->
|
||||||
|
val builder = StringBuilder()
|
||||||
|
for (data in out.getOut()) { builder.append(data) }
|
||||||
|
result.success(builder.toString().endsWith("Success"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
|
installersChannel = MethodChannel(
|
||||||
|
flutterEngine.dartExecutor.binaryMessenger, "installers")
|
||||||
|
installersChannel!!.setMethodCallHandler {
|
||||||
|
call, result ->
|
||||||
|
if (call.method == "checkPermissionShizuku") {
|
||||||
|
shizukuCheckPermission(result)
|
||||||
|
} else if (call.method == "checkPermissionRoot") {
|
||||||
|
rootCheckPermission(result)
|
||||||
|
} else if (call.method == "installWithShizuku") {
|
||||||
|
val apkFileUri: String? = call.argument("apkFileUri")
|
||||||
|
shizukuInstallApk(apkFileUri!!, result)
|
||||||
|
} else if (call.method == "installWithRoot") {
|
||||||
|
val apkFilePath: String? = call.argument("apkFilePath")
|
||||||
|
rootInstallApk(apkFilePath!!, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Application;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
public class ApplicationUtils {
|
||||||
|
|
||||||
|
private static Application application;
|
||||||
|
|
||||||
|
public static Application getApplication() {
|
||||||
|
return application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setApplication(Application application) {
|
||||||
|
ApplicationUtils.application = application;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getProcessName() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 28)
|
||||||
|
return Application.getProcessName();
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
Class<?> activityThread = Class.forName("android.app.ActivityThread");
|
||||||
|
@SuppressLint("DiscouragedPrivateApi")
|
||||||
|
Method method = activityThread.getDeclaredMethod("currentProcessName");
|
||||||
|
return (String) method.invoke(null);
|
||||||
|
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.IIntentReceiver;
|
||||||
|
import android.content.IIntentSender;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.IBinder;
|
||||||
|
|
||||||
|
public abstract class IIntentSenderAdaptor extends IIntentSender.Stub {
|
||||||
|
|
||||||
|
public abstract void send(Intent intent);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||||
|
send(intent);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
|
||||||
|
send(intent);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.IIntentSender;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
public class IntentSenderUtils {
|
||||||
|
|
||||||
|
public static IntentSender newInstance(IIntentSender binder) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||||
|
//noinspection JavaReflectionMemberAccess
|
||||||
|
return IntentSender.class.getConstructor(IIntentSender.class).newInstance(binder);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.IPackageInstaller;
|
||||||
|
import android.content.pm.IPackageInstallerSession;
|
||||||
|
import android.content.pm.PackageInstaller;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
@SuppressWarnings({"JavaReflectionMemberAccess"})
|
||||||
|
public class PackageInstallerUtils {
|
||||||
|
|
||||||
|
public static PackageInstaller createPackageInstaller(IPackageInstaller installer, String installerPackageName, String installerAttributionTag, int userId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, String.class, int.class)
|
||||||
|
.newInstance(installer, installerPackageName, installerAttributionTag, userId);
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class)
|
||||||
|
.newInstance(installer, installerPackageName, userId);
|
||||||
|
} else {
|
||||||
|
return PackageInstaller.class.getConstructor(Context.class, PackageManager.class, IPackageInstaller.class, String.class, int.class)
|
||||||
|
.newInstance(ApplicationUtils.getApplication(), ApplicationUtils.getApplication().getPackageManager(), installer, installerPackageName, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PackageInstaller.Session createSession(IPackageInstallerSession session) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||||
|
return PackageInstaller.Session.class.getConstructor(IPackageInstallerSession.class)
|
||||||
|
.newInstance(session);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getInstallFlags(PackageInstaller.SessionParams params) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
return (int) PackageInstaller.SessionParams.class.getDeclaredField("installFlags").get(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setInstallFlags(PackageInstaller.SessionParams params, int newValue) throws NoSuchFieldException, IllegalAccessException {
|
||||||
|
PackageInstaller.SessionParams.class.getDeclaredField("installFlags").set(params, newValue);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.IPackageInstaller;
|
||||||
|
import android.content.pm.IPackageManager;
|
||||||
|
import android.content.pm.UserInfo;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.IUserManager;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import rikka.shizuku.ShizukuBinderWrapper;
|
||||||
|
import rikka.shizuku.SystemServiceHelper;
|
||||||
|
|
||||||
|
public class ShizukuSystemServerApi {
|
||||||
|
|
||||||
|
private static final Singleton<IPackageManager> PACKAGE_MANAGER = new Singleton<IPackageManager>() {
|
||||||
|
@Override
|
||||||
|
protected IPackageManager create() {
|
||||||
|
return IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Singleton<IUserManager> USER_MANAGER = new Singleton<IUserManager>() {
|
||||||
|
@Override
|
||||||
|
protected IUserManager create() {
|
||||||
|
return IUserManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService(Context.USER_SERVICE)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IPackageInstaller PackageManager_getPackageInstaller() throws RemoteException {
|
||||||
|
IPackageInstaller packageInstaller = PACKAGE_MANAGER.get().getPackageInstaller();
|
||||||
|
return IPackageInstaller.Stub.asInterface(new ShizukuBinderWrapper(packageInstaller.asBinder()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<UserInfo> UserManager_getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated) throws RemoteException {
|
||||||
|
if (Build.VERSION.SDK_INT >= 30) {
|
||||||
|
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
return USER_MANAGER.get().getUsers(excludeDying);
|
||||||
|
} catch (NoSuchFieldError e) {
|
||||||
|
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// method 2: use transactRemote directly
|
||||||
|
/*public static List<UserInfo> UserManager_getUsers(boolean excludeDying) {
|
||||||
|
Parcel data = SystemServiceHelper.obtainParcel(Context.USER_SERVICE, "android.os.IUserManager", "getUsers");
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
data.writeInt(excludeDying ? 1 : 0);
|
||||||
|
|
||||||
|
List<UserInfo> res = null;
|
||||||
|
try {
|
||||||
|
ShizukuService.transactRemote(data, reply, 0);
|
||||||
|
reply.readException();
|
||||||
|
res = reply.createTypedArrayList(UserInfo.CREATOR);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e("ShizukuSample", "UserManager#getUsers", e);
|
||||||
|
} finally {
|
||||||
|
data.recycle();
|
||||||
|
reply.recycle();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}*/
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package dev.imranr.obtainium.util;
|
||||||
|
|
||||||
|
public abstract class Singleton<T> {
|
||||||
|
|
||||||
|
private T mInstance;
|
||||||
|
|
||||||
|
protected abstract T create();
|
||||||
|
|
||||||
|
public final T get() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (mInstance == null) {
|
||||||
|
mInstance = create();
|
||||||
|
}
|
||||||
|
return mInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,8 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
android:viewportWidth="142.129"
|
android:viewportWidth="142.129"
|
||||||
android:viewportHeight="142.129"
|
android:viewportHeight="142.129"
|
||||||
android:width="503.6066dp"
|
android:width="108dp"
|
||||||
android:height="503.6066dp">
|
android:height="108dp">
|
||||||
<group
|
<group
|
||||||
android:translateX="-30.39437"
|
android:translateX="-30.39437"
|
||||||
android:translateY="-54.68043">
|
android:translateY="-54.68043">
|
||||||
|
@ -8,6 +8,7 @@ buildscript {
|
|||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||||
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!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
|
"trySelectingSuggestedVersionCode": "Probajte izabrati preloženu (verziju) versionCode APK-a",
|
||||||
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
|
"dontSortReleasesList": "Zadrži redosled izdanja iz API-a",
|
||||||
"reverseSort": "Obrni redosled",
|
"reverseSort": "Obrni redosled",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Meni za otkrivanje grešaka",
|
"debugMenu": "Meni za otkrivanje grešaka",
|
||||||
"bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.",
|
"bgTaskStarted": "Rad u pozadini pokrenut - provjerite log-ove.",
|
||||||
"runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad",
|
"runBgCheckNow": "Pokrenite pozadinsku provjeru ažuriranja sad",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'",
|
"onlyCheckInstalledOrTrackOnlyApps": "Isključivo provjerite ažuriranje za instalirane i aplikacije 'samo za praćenje'",
|
||||||
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
|
"supportFixedAPKURL": "Podržite fiksne APK URL-ove",
|
||||||
"selectX": "Izaberite {}",
|
"selectX": "Izaberite {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Želite li ukloniti aplikaciju?",
|
"one": "Želite li ukloniti aplikaciju?",
|
||||||
"other": "Želite li ukloniti aplikacije?"
|
"other": "Želite li ukloniti aplikacije?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
|
"trySelectingSuggestedVersionCode": "Zkusit vybrat navrhovaný kód verze APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Reverse sorting",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Debug Menu",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Background task started - check logs.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Run Background Update Check Now",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Odstranit Apku?",
|
"one": "Odstranit Apku?",
|
||||||
"other": "Odstranit Apky?"
|
"other": "Odstranit Apky?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
|
"trySelectingSuggestedVersionCode": "Versuchen, den vorgeschlagenen APK-Versionscode auszuwählen",
|
||||||
"dontSortReleasesList": "Freigaberelease von der API ordern",
|
"dontSortReleasesList": "Freigaberelease von der API ordern",
|
||||||
"reverseSort": "Umgekehrtes Sortieren",
|
"reverseSort": "Umgekehrtes Sortieren",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Debug-Menü",
|
"debugMenu": "Debug-Menü",
|
||||||
"bgTaskStarted": "Hintergrundaufgabe gestartet – Logs prüfen.",
|
"bgTaskStarted": "Hintergrundaufgabe gestartet – Logs prüfen.",
|
||||||
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
|
"runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
|
"onlyCheckInstalledOrTrackOnlyApps": "Überprüfe nur installierte und mit „nur Nachverfolgen“ markierte Apps auf Aktualisierungen",
|
||||||
"supportFixedAPKURL": "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": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App entfernen?",
|
"one": "App entfernen?",
|
||||||
"other": "Apps entfernen?"
|
"other": "Apps entfernen?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Reverse sorting",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Debug Menu",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Background task started - check logs.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Run Background Update Check Now",
|
||||||
@ -278,6 +280,12 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
|
"installMethod": "Installation method",
|
||||||
|
"normal": "Normal",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
"unexpectedError": "Error Inesperado",
|
"unexpectedError": "Error Inesperado",
|
||||||
"ok": "Correcto",
|
"ok": "Correcto",
|
||||||
"and": "y",
|
"and": "y",
|
||||||
"githubPATLabel": "Token de Acceso Personal de GitHub (Reduce tiempos de espera)",
|
"githubPATLabel": "Token Github de Acceso Personal\n(Reduce tiempos de espera)",
|
||||||
"includePrereleases": "Incluir versiones preliminares",
|
"includePrereleases": "Incluir versiones preliminares",
|
||||||
"fallbackToOlderReleases": "Retorceder a versiones previas",
|
"fallbackToOlderReleases": "Retroceder a versiones previas",
|
||||||
"filterReleaseTitlesByRegEx": "Filtrar Títulos de Versiones",
|
"filterReleaseTitlesByRegEx": "Filtrar Títulos de Versiones",
|
||||||
"invalidRegEx": "Expresión inválida",
|
"invalidRegEx": "Expresión inválida",
|
||||||
"noDescription": "Sin descripción",
|
"noDescription": "Sin descripción",
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"requiredInBrackets": "(Requerido)",
|
"requiredInBrackets": "(Requerido)",
|
||||||
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN",
|
"dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN",
|
||||||
"colour": "Color",
|
"colour": "Color",
|
||||||
"githubStarredRepos": "Repositorios favoritos de GitHub",
|
"githubStarredRepos": "Repositorios favoritos GitHub",
|
||||||
"uname": "Nombre de usuario",
|
"uname": "Nombre de usuario",
|
||||||
"wrongArgNum": "Número de argumentos provistos inválido",
|
"wrongArgNum": "Número de argumentos provistos inválido",
|
||||||
"xIsTrackOnly": "{} es de 'Solo Seguimiento'",
|
"xIsTrackOnly": "{} es de 'Solo Seguimiento'",
|
||||||
@ -30,7 +30,7 @@
|
|||||||
"app": "Aplicación",
|
"app": "Aplicación",
|
||||||
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.",
|
"appsFromSourceAreTrackOnly": "Las aplicaciones de este origen son de 'Solo Seguimiento'.",
|
||||||
"youPickedTrackOnly": "Debes seleccionar la opción de 'Solo Seguimiento'.",
|
"youPickedTrackOnly": "Debes seleccionar la opción de 'Solo Seguimiento'.",
|
||||||
"trackOnlyAppDescription": "Se monitorizará la aplicación en busca de actualizaciones, pero Obtainium no será capaz de descargarla o actalizarla.",
|
"trackOnlyAppDescription": "Se hará el seguimiento de actualizaciones para la aplicación, pero Obtainium no será capaz de descargarla o actalizarla.",
|
||||||
"cancelled": "Cancelado",
|
"cancelled": "Cancelado",
|
||||||
"appAlreadyAdded": "Aplicación ya añadida",
|
"appAlreadyAdded": "Aplicación ya añadida",
|
||||||
"alreadyUpToDateQuestion": "¿Aplicación ya actualizada?",
|
"alreadyUpToDateQuestion": "¿Aplicación ya actualizada?",
|
||||||
@ -38,12 +38,12 @@
|
|||||||
"appSourceURL": "URL de Origen de la Aplicación",
|
"appSourceURL": "URL de Origen de la Aplicación",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"add": "Añadir",
|
"add": "Añadir",
|
||||||
"searchSomeSourcesLabel": "Buscar (Solo Algunas Fuentes)",
|
"searchSomeSourcesLabel": "Buscar (solo algunas fuentes)",
|
||||||
"search": "Buscar",
|
"search": "Buscar",
|
||||||
"additionalOptsFor": "Opciones Adicionales para {}",
|
"additionalOptsFor": "Opciones Adicionales para {}",
|
||||||
"supportedSources": "Fuentes Soportadas",
|
"supportedSources": "Fuentes Soportadas",
|
||||||
"trackOnlyInBrackets": "(Solo Seguimiento)",
|
"trackOnlyInBrackets": "(Solo Seguimiento)",
|
||||||
"searchableInBrackets": "(Soporta Búsquedas)",
|
"searchableInBrackets": "(soporta búsqueda)",
|
||||||
"appsString": "Aplicaciones",
|
"appsString": "Aplicaciones",
|
||||||
"noApps": "Sin Aplicaciones",
|
"noApps": "Sin Aplicaciones",
|
||||||
"noAppsForFilter": "Sin Aplicaciones para Filtrar",
|
"noAppsForFilter": "Sin Aplicaciones para Filtrar",
|
||||||
@ -56,9 +56,9 @@
|
|||||||
"estimateInBrackets": "(Aproximado)",
|
"estimateInBrackets": "(Aproximado)",
|
||||||
"selectAll": "Seleccionar Todo",
|
"selectAll": "Seleccionar Todo",
|
||||||
"deselectX": "Deseleccionar {}",
|
"deselectX": "Deseleccionar {}",
|
||||||
"xWillBeRemovedButRemainInstalled": "{} será borrada de Obtainium pero continuará instalada en el dispositivo.",
|
"xWillBeRemovedButRemainInstalled": "{} será eliminada de Obtainium pero continuará instalada en el dispositivo.",
|
||||||
"removeSelectedAppsQuestion": "¿Borrar aplicaciones seleccionadas?",
|
"removeSelectedAppsQuestion": "¿Eliminar aplicaciones seleccionadas?",
|
||||||
"removeSelectedApps": "Borrar Aplicaciones Seleccionadas",
|
"removeSelectedApps": "Eliminar Aplicaciones Seleccionadas",
|
||||||
"updateX": "Actualizar {}",
|
"updateX": "Actualizar {}",
|
||||||
"installX": "Instalar {}",
|
"installX": "Instalar {}",
|
||||||
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimiento)\ncomo Actualizada",
|
"markXTrackOnlyAsUpdated": "Marcar {}\n(Solo Seguimiento)\ncomo Actualizada",
|
||||||
@ -98,8 +98,8 @@
|
|||||||
"line": "Línea",
|
"line": "Línea",
|
||||||
"searchX": "Buscar {}",
|
"searchX": "Buscar {}",
|
||||||
"noResults": "Resultados no encontrados",
|
"noResults": "Resultados no encontrados",
|
||||||
"importX": "Importar {}",
|
"importX": "Importar desde {}",
|
||||||
"importedAppsIdDisclaimer": "Las Aplicaciones Importadas pueden mostrarse incorrectamente como \"No Instalada\".\nPara arreglar esto, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
|
"importedAppsIdDisclaimer": "Las aplicaciones importadas podrían mostrarse incorrectamente como \"No Instalada\".\nPara solucionarlo, reinstálalas a través de Obtainium.\nEsto no debería afectar a los datos de las aplicaciones.\n\nSolo afecta a las URLs y a los métodos de importación mediante terceros.",
|
||||||
"importErrors": "Errores de Importación",
|
"importErrors": "Errores de Importación",
|
||||||
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
"importedXOfYApps": "{} de {} Aplicaciones importadas.",
|
||||||
"followingURLsHadErrors": "Las siguientes URLs tuvieron problemas:",
|
"followingURLsHadErrors": "Las siguientes URLs tuvieron problemas:",
|
||||||
@ -113,12 +113,12 @@
|
|||||||
"followSystem": "Seguir al Sistema",
|
"followSystem": "Seguir al Sistema",
|
||||||
"obtainium": "Obtainium",
|
"obtainium": "Obtainium",
|
||||||
"materialYou": "Material You",
|
"materialYou": "Material You",
|
||||||
"useBlackTheme": "Usar tema oscuro con negros puros",
|
"useBlackTheme": "Usar negros puros en tema oscuro",
|
||||||
"appSortBy": "Ordenar Aplicaciones Por",
|
"appSortBy": "Ordenar Apps Por",
|
||||||
"authorName": "Autor/Nombre",
|
"authorName": "Autor/Nombre",
|
||||||
"nameAuthor": "Nombre/Autor",
|
"nameAuthor": "Nombre/Autor",
|
||||||
"asAdded": "Según se Añadieron",
|
"asAdded": "Según se Añadieron",
|
||||||
"appSortOrder": "Orden de Clasificación de Aplicaciones",
|
"appSortOrder": "Orden de Clasificación",
|
||||||
"ascending": "Ascendente",
|
"ascending": "Ascendente",
|
||||||
"descending": "Descendente",
|
"descending": "Descendente",
|
||||||
"bgUpdateCheckInterval": "Intervalo de Comprobación de Actualizaciones en Segundo Plano",
|
"bgUpdateCheckInterval": "Intervalo de Comprobación de Actualizaciones en Segundo Plano",
|
||||||
@ -170,12 +170,12 @@
|
|||||||
"lastUpdateCheckX": "Última Comprobación: {}",
|
"lastUpdateCheckX": "Última Comprobación: {}",
|
||||||
"remove": "Eliminar",
|
"remove": "Eliminar",
|
||||||
"yesMarkUpdated": "Sí, Marcar como Actualizada",
|
"yesMarkUpdated": "Sí, Marcar como Actualizada",
|
||||||
"fdroid": "Repositorio oficial de F-Droid",
|
"fdroid": "Repositorio oficial F-Droid",
|
||||||
"appIdOrName": "ID o Nombre de la Aplicación",
|
"appIdOrName": "ID o Nombre de la Aplicación",
|
||||||
"appId": "ID de la Aplicación",
|
"appId": "ID de la Aplicación",
|
||||||
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre",
|
"appWithIdOrNameNotFound": "No se han encontrado aplicaciones con esa ID o nombre",
|
||||||
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
|
"reposHaveMultipleApps": "Los repositorios pueden contener varias aplicaciones",
|
||||||
"fdroidThirdPartyRepo": "Rpositorios de terceros de F-Droid",
|
"fdroidThirdPartyRepo": "Rpositorios de terceros F-Droid",
|
||||||
"steam": "Steam",
|
"steam": "Steam",
|
||||||
"steamMobile": "Steam Mobile",
|
"steamMobile": "Steam Mobile",
|
||||||
"steamChat": "Steam Chat",
|
"steamChat": "Steam Chat",
|
||||||
@ -195,8 +195,8 @@
|
|||||||
"category": "Categoría",
|
"category": "Categoría",
|
||||||
"noCategory": "Sin Categoría",
|
"noCategory": "Sin Categoría",
|
||||||
"noCategories": "Sin Categorías",
|
"noCategories": "Sin Categorías",
|
||||||
"deleteCategoriesQuestion": "¿Borrar Categorías?",
|
"deleteCategoriesQuestion": "¿Eliminar Categorías?",
|
||||||
"categoryDeleteWarning": "Todas las aplicaciones en las categorías borradas serán marcadas como 'Sin Categoría'.",
|
"categoryDeleteWarning": "Todas las aplicaciones en las categorías eliminadas serán marcadas como 'Sin Categoría'.",
|
||||||
"addCategory": "Añadir Categoría",
|
"addCategory": "Añadir Categoría",
|
||||||
"label": "Nombre",
|
"label": "Nombre",
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
@ -211,7 +211,7 @@
|
|||||||
"releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
|
"releaseDateAsVersionExplanation": "Esta opción solo se debería usar con aplicaciones en las que la detección de versiones no funciona pero hay disponible una fecha de publicación.",
|
||||||
"changes": "Cambios",
|
"changes": "Cambios",
|
||||||
"releaseDate": "Fecha de Publicación",
|
"releaseDate": "Fecha de Publicación",
|
||||||
"importFromURLsInFile": "Importar de URls desde un Archivo (como OPML)",
|
"importFromURLsInFile": "Importar URLs desde archivo (como OPML)",
|
||||||
"versionDetection": "Detección de Versiones",
|
"versionDetection": "Detección de Versiones",
|
||||||
"standardVersionDetection": "Detección de versiones estándar",
|
"standardVersionDetection": "Detección de versiones estándar",
|
||||||
"groupByCategory": "Agrupar por Categoría",
|
"groupByCategory": "Agrupar por Categoría",
|
||||||
@ -219,8 +219,8 @@
|
|||||||
"overrideSource": "Sobrescribir Fuente",
|
"overrideSource": "Sobrescribir Fuente",
|
||||||
"dontShowAgain": "No mostrar de nuevo",
|
"dontShowAgain": "No mostrar de nuevo",
|
||||||
"dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'",
|
"dontShowTrackOnlyWarnings": "No mostrar avisos de 'Solo Seguimiento'",
|
||||||
"dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APks",
|
"dontShowAPKOriginWarnings": "No mostrar avisos de las fuentes de las APKs",
|
||||||
"moveNonInstalledAppsToBottom": "Mover las Apps no instaladas al final de la vista de Apps",
|
"moveNonInstalledAppsToBottom": "Mover Apps no instaladas en la Parte Inferior de la Vista de Aplicaciones",
|
||||||
"gitlabPATLabel": "Token GitLab de Acceso Personal\n(Habilita la Búsqueda y Mejor Detección de APKs)",
|
"gitlabPATLabel": "Token GitLab de Acceso Personal\n(Habilita la Búsqueda y Mejor Detección de APKs)",
|
||||||
"about": "Acerca",
|
"about": "Acerca",
|
||||||
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en Ajustes)",
|
"requiresCredentialsInSettings": "{}: Esto requiere credenciales adicionales (en Ajustes)",
|
||||||
@ -230,7 +230,7 @@
|
|||||||
"pickHighestVersionCode": "Auto selección versión superior del código APK",
|
"pickHighestVersionCode": "Auto selección versión superior del código APK",
|
||||||
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la App",
|
"checkUpdateOnDetailPage": "Comprobar actualizaciones al abrir detalles de la App",
|
||||||
"disablePageTransitions": "Deshabilitar animaciones de transición de la página",
|
"disablePageTransitions": "Deshabilitar animaciones de transición de la página",
|
||||||
"reversePageTransitions": "Invertir las animaciones de transición de la página",
|
"reversePageTransitions": "Invertir animaciones de transición de la página",
|
||||||
"minStarCount": "Número Mínimo de Estrellas",
|
"minStarCount": "Número Mínimo de Estrellas",
|
||||||
"addInfoBelow": "Añadir esta información debajo.",
|
"addInfoBelow": "Añadir esta información debajo.",
|
||||||
"addInfoInSettings": "Añadir esta información en Ajustes.",
|
"addInfoInSettings": "Añadir esta información en Ajustes.",
|
||||||
@ -244,23 +244,25 @@
|
|||||||
"xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.",
|
"xWasPossiblyUpdatedToY": "{} podría estar actualizada a {}.",
|
||||||
"enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
|
"enableBackgroundUpdates": "Habilitar actualizaciones en segundo plano",
|
||||||
"backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
|
"backgroundUpdateReqsExplanation": "Las actualizaciones en segundo plano pueden no estar disponibles para todas las aplicaciones.",
|
||||||
"backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede verificar con Obtainium abierto.",
|
"backgroundUpdateLimitsExplanation": "El éxito de las instalaciones en segundo plano solo se puede comprobar con Obtainium abierto.",
|
||||||
"verifyLatestTag": "Verifica la etiqueta 'latest'",
|
"verifyLatestTag": "Comprueba la etiqueta 'latest'",
|
||||||
"intermediateLinkRegex": "Filtrar por Enlace 'Intermedio' para Visitar Primero",
|
"intermediateLinkRegex": "Filtrar por Enlace 'Intermedio' para Visitar Primero",
|
||||||
"intermediateLinkNotFound": "Enlace Intermedio no encontrado",
|
"intermediateLinkNotFound": "Enlace Intermedio no encontrado",
|
||||||
"exemptFromBackgroundUpdates": "Exento de actualizciones en segundo plano (si están habilitadas)",
|
"exemptFromBackgroundUpdates": "Exento de actualizciones en segundo plano (si están habilitadas)",
|
||||||
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
|
"bgUpdatesOnWiFiOnly": "Deshabilitar las actualizaciones en segundo plano sin WiFi",
|
||||||
"autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior",
|
"autoSelectHighestVersionCode": "Auto Selección de la versionCode APK superior",
|
||||||
"versionExtractionRegEx": "Versión de Extracción de RegEx",
|
"versionExtractionRegEx": "Versión de Extracción de RegEx",
|
||||||
"matchGroupToUse": "Match Group to Use",
|
"matchGroupToUse": "Coincidir en Grupo a Usar",
|
||||||
"highlightTouchTargets": "Resaltar objetivos menos obvios",
|
"highlightTouchTargets": "Resaltar objetivos menos obvios",
|
||||||
"pickExportDir": "Selecciona el Directorio para Exportar",
|
"pickExportDir": "Selecciona el Directorio para Exportar",
|
||||||
"autoExportOnChanges": "Auto Exportar cuando haya cambios",
|
"autoExportOnChanges": "Auto Exportar cuando haya cambios",
|
||||||
"includeSettings": "Include settings",
|
"includeSettings": "Incluir ajustes",
|
||||||
"filterVersionsByRegEx": "Filtrar por Versiones",
|
"filterVersionsByRegEx": "Filtrar por Versiones",
|
||||||
"trySelectingSuggestedVersionCode": "Prueba seleccionando la versionCode APK sugerida",
|
"trySelectingSuggestedVersionCode": "Prueba seleccionando la versionCode APK sugerida",
|
||||||
"dontSortReleasesList": "Mantener el order de publicación desde API",
|
"dontSortReleasesList": "Mantener el order de publicación desde API",
|
||||||
"reverseSort": "Orden inverso",
|
"reverseSort": "Orden inverso",
|
||||||
|
"takeFirstLink": "Usar primer enlace",
|
||||||
|
"skipSort": "Omitir orden",
|
||||||
"debugMenu": "Menu Depurar",
|
"debugMenu": "Menu Depurar",
|
||||||
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
|
"bgTaskStarted": "Iniciada tarea en segundo plano - revisa los logs.",
|
||||||
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
|
"runBgCheckNow": "Ejecutar verficiación de actualizaciones en segundo plano",
|
||||||
@ -275,9 +277,10 @@
|
|||||||
"downloadingXNotifChannel": "Descargando {}",
|
"downloadingXNotifChannel": "Descargando {}",
|
||||||
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
|
"completeAppInstallationNotifChannel": "Instalación Completa de la Aplicación",
|
||||||
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
|
"checkingForUpdatesNotifChannel": "Buscando Actualizaciones",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Comprobar actualizaciones solo para apps instaladas y en seguimiento",
|
||||||
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
|
"supportFixedAPKURL": "Soporte para URLs fijas de APK",
|
||||||
"selectX": "Selecciona {}",
|
"selectX": "Selecciona {}",
|
||||||
|
"parallelDownloads": "Permitir descargas paralelas",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "¿Eliminar Aplicación?",
|
"one": "¿Eliminar Aplicación?",
|
||||||
"other": "¿Eliminar Aplicaciones?"
|
"other": "¿Eliminar Aplicaciones?"
|
||||||
@ -315,8 +318,8 @@
|
|||||||
"other": "{} Días"
|
"other": "{} Días"
|
||||||
},
|
},
|
||||||
"clearedNLogsBeforeXAfterY": {
|
"clearedNLogsBeforeXAfterY": {
|
||||||
"one": "Borrado {n} log (previo a = {before}, posterior a = {after})",
|
"one": "Eliminado {n} log (previo a = {before}, posterior a = {after})",
|
||||||
"other": "Borrados {n} logs (previos a = {before}, posteriores a = {after})"
|
"other": "Eliminados {n} logs (previos a = {before}, posteriores a = {after})"
|
||||||
},
|
},
|
||||||
"xAndNMoreUpdatesAvailable": {
|
"xAndNMoreUpdatesAvailable": {
|
||||||
"one": "{} y 1 aplicación más tiene actualizaciones.",
|
"one": "{} y 1 aplicación más tiene actualizaciones.",
|
||||||
|
@ -245,8 +245,8 @@
|
|||||||
"enableBackgroundUpdates": "به روز رسانی پس زمینه را فعال کنید",
|
"enableBackgroundUpdates": "به روز رسانی پس زمینه را فعال کنید",
|
||||||
"backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.",
|
"backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.",
|
||||||
"backgroundUpdateLimitsExplanation": "موفقیت نصب پسزمینه تنها زمانی مشخص میشود که Obtainium باز شود.",
|
"backgroundUpdateLimitsExplanation": "موفقیت نصب پسزمینه تنها زمانی مشخص میشود که Obtainium باز شود.",
|
||||||
"verifyLatestTag": "برچسب "آخرین" را تأیید کنید",
|
"verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید",
|
||||||
"intermediateLinkRegex": "برای اولین بار بازدید از لینک "متوسط" را فیلتر کنید",
|
"intermediateLinkRegex": "برای اولین بار بازدید از لینک \"متوسط\" را فیلتر کنید",
|
||||||
"intermediateLinkNotFound": "لینک میانی پیدا نشد",
|
"intermediateLinkNotFound": "لینک میانی پیدا نشد",
|
||||||
"exemptFromBackgroundUpdates": "معاف از بهروزرسانیهای پسزمینه (در صورت فعال بودن)",
|
"exemptFromBackgroundUpdates": "معاف از بهروزرسانیهای پسزمینه (در صورت فعال بودن)",
|
||||||
"bgUpdatesOnWiFiOnly": "بهروزرسانیهای پسزمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
|
"bgUpdatesOnWiFiOnly": "بهروزرسانیهای پسزمینه را در صورت عدم اتصال به WiFi غیرفعال کنید",
|
||||||
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
|
"trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید",
|
||||||
"dontSortReleasesList": "حفظ سفارش انتشار از API",
|
"dontSortReleasesList": "حفظ سفارش انتشار از API",
|
||||||
"reverseSort": "مرتب سازی معکوس",
|
"reverseSort": "مرتب سازی معکوس",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "منوی اشکال زدایی",
|
"debugMenu": "منوی اشکال زدایی",
|
||||||
"bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.",
|
"bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.",
|
||||||
"runBgCheckNow": "اکنون بهروزرسانی پسزمینه را بررسی کنید",
|
"runBgCheckNow": "اکنون بهروزرسانی پسزمینه را بررسی کنید",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید",
|
"onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید",
|
||||||
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
|
"supportFixedAPKURL": "پشتیبانی از URL های APK ثابت",
|
||||||
"selectX": "انتخاب کنید {}",
|
"selectX": "انتخاب کنید {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "برنامه حذف شود؟",
|
"one": "برنامه حذف شود؟",
|
||||||
"other": "برنامه ها حذف شوند؟"
|
"other": "برنامه ها حذف شوند؟"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
"reverseSort": "Reverse sorting",
|
"reverseSort": "Reverse sorting",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Debug Menu",
|
"debugMenu": "Debug Menu",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Background task started - check logs.",
|
||||||
"runBgCheckNow": "Run Background Update Check Now",
|
"runBgCheckNow": "Run Background Update Check Now",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Supprimer l'application ?",
|
"one": "Supprimer l'application ?",
|
||||||
"other": "Supprimer les applications ?"
|
"other": "Supprimer les applications ?"
|
||||||
|
@ -260,6 +260,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
|
"trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t",
|
||||||
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
|
"dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése",
|
||||||
"reverseSort": "Fordított rendezés",
|
"reverseSort": "Fordított rendezés",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Hibakereső menü",
|
"debugMenu": "Hibakereső menü",
|
||||||
"bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.",
|
"bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.",
|
||||||
"enableBackgroundUpdates": "Frissítések a háttérben",
|
"enableBackgroundUpdates": "Frissítések a háttérben",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"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",
|
||||||
"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?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
|
"trySelectingSuggestedVersionCode": "Prova a selezionare APK con versionCode suggerito",
|
||||||
"dontSortReleasesList": "Conserva l'ordine di release da API",
|
"dontSortReleasesList": "Conserva l'ordine di release da API",
|
||||||
"reverseSort": "Ordine inverso",
|
"reverseSort": "Ordine inverso",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Menu di debug",
|
"debugMenu": "Menu di debug",
|
||||||
"bgTaskStarted": "Attività in secondo piano iniziata - controllo log.",
|
"bgTaskStarted": "Attività in secondo piano iniziata - controllo log.",
|
||||||
"runBgCheckNow": "Inizia aggiornamento in secondo piano ora",
|
"runBgCheckNow": "Inizia aggiornamento in secondo piano ora",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
|
"onlyCheckInstalledOrTrackOnlyApps": "Cerca aggiornamenti solo per app installate e app in Solo-Monitoraggio",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Rimuovere l'app?",
|
"one": "Rimuovere l'app?",
|
||||||
"other": "Rimuovere le app?"
|
"other": "Rimuovere le app?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
|
"trySelectingSuggestedVersionCode": "提案されたバージョンコードのAPKを選択する",
|
||||||
"dontSortReleasesList": "APIからのリリース順を保持する",
|
"dontSortReleasesList": "APIからのリリース順を保持する",
|
||||||
"reverseSort": "逆順ソート",
|
"reverseSort": "逆順ソート",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "デバッグメニュー",
|
"debugMenu": "デバッグメニュー",
|
||||||
"bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
|
"bgTaskStarted": "バックグラウンドタスクが開始されました - ログを確認してください。",
|
||||||
"runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
|
"runBgCheckNow": "今すぐバックグラウンドでのアップデート確認を開始する",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
|
"onlyCheckInstalledOrTrackOnlyApps": "インストール済みのアプリと「追跡のみ」のアプリのアップデートのみを確認する",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "アプリを削除しますか?",
|
"one": "アプリを削除しますか?",
|
||||||
"other": "アプリを削除しますか?"
|
"other": "アプリを削除しますか?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
|
"trySelectingSuggestedVersionCode": "Probeer de voorgestelde versiecode APK te selecteren",
|
||||||
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
|
"dontSortReleasesList": "Volgorde van releases behouden vanuit de API",
|
||||||
"reverseSort": "Sortering omkeren",
|
"reverseSort": "Sortering omkeren",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Debug menu",
|
"debugMenu": "Debug menu",
|
||||||
"bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
|
"bgTaskStarted": "Achtergrondtaak gestart - controleer de logs.",
|
||||||
"runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
|
"runBgCheckNow": "Voer nu een achtergrondupdatecontrole uit",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Alleen geïnstalleerde en Track-Only apps controleren op updates",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "App verwijderen?",
|
"one": "App verwijderen?",
|
||||||
"other": "Apps verwijderen?"
|
"other": "Apps verwijderen?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
"trySelectingSuggestedVersionCode": "Spróbuj wybierać sugerowany kod wersji APK",
|
||||||
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
|
"dontSortReleasesList": "Utrzymaj kolejność wydań z interfejsu API",
|
||||||
"reverseSort": "Odwrotne sortowanie",
|
"reverseSort": "Odwrotne sortowanie",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Menu debugowania",
|
"debugMenu": "Menu debugowania",
|
||||||
"bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
|
"bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
|
||||||
"runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
|
"runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
|
"onlyCheckInstalledOrTrackOnlyApps": "Sprawdzaj tylko zainstalowane i obserwowane aplikacje pod kątem aktualizacji",
|
||||||
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
|
"supportFixedAPKURL": "Obsługuj stałe adresy URL APK",
|
||||||
"selectX": "Wybierz {}",
|
"selectX": "Wybierz {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Usunąć aplikację?",
|
"one": "Usunąć aplikację?",
|
||||||
"few": "Usunąć aplikacje?",
|
"few": "Usunąć aplikacje?",
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
|
"trySelectingSuggestedVersionCode": "Tente selecionar a versão sugerida",
|
||||||
"dontSortReleasesList": "Reter a ordem de lançamento da API",
|
"dontSortReleasesList": "Reter a ordem de lançamento da API",
|
||||||
"reverseSort": "Ordenação reversa",
|
"reverseSort": "Ordenação reversa",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Menu Debug",
|
"debugMenu": "Menu Debug",
|
||||||
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
|
"bgTaskStarted": "Tarefa em segundo plano iniciada - verifique os logs.",
|
||||||
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
|
"runBgCheckNow": "Execute a verificação de atualização em segundo plano agora",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remover App?",
|
"one": "Remover App?",
|
||||||
"other": "Remover Apps?"
|
"other": "Remover Apps?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
"trySelectingSuggestedVersionCode": "Попробуйте выбрать предложенный код версии APK",
|
||||||
"dontSortReleasesList": "Сохранить порядок релизов от API",
|
"dontSortReleasesList": "Сохранить порядок релизов от API",
|
||||||
"reverseSort": "Обратная сортировка",
|
"reverseSort": "Обратная сортировка",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Меню отладки",
|
"debugMenu": "Меню отладки",
|
||||||
"bgTaskStarted": "Фоновая задача начата — проверьте журналы",
|
"bgTaskStarted": "Фоновая задача начата — проверьте журналы",
|
||||||
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
|
"runBgCheckNow": "Запустить проверку фонового обновления сейчас",
|
||||||
@ -275,9 +277,15 @@
|
|||||||
"downloadingXNotifChannel": "Загрузка {}",
|
"downloadingXNotifChannel": "Загрузка {}",
|
||||||
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
||||||
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Проверять обновления только у установленных или отслеживаемых приложений",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK",
|
||||||
"selectX": "Select {}",
|
"selectX": "Выбрать {}",
|
||||||
|
"parallelDownloads": "Разрешить параллельные загрузки",
|
||||||
|
"installMethod": "Метод установки",
|
||||||
|
"normal": "Нормальный",
|
||||||
|
"shizuku": "Shizuku",
|
||||||
|
"root": "Суперпользователь",
|
||||||
|
"shizukuBinderNotFound": "Shizuku не запущен",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
|
@ -261,9 +261,12 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
"trySelectingSuggestedVersionCode": "Try selecting suggested versionCode APK",
|
||||||
"dontSortReleasesList": "Retain release order from API",
|
"dontSortReleasesList": "Retain release order from API",
|
||||||
"reverseSort": "Omvänd sortering",
|
"reverseSort": "Omvänd sortering",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Felsökningsmeny",
|
"debugMenu": "Felsökningsmeny",
|
||||||
"bgTaskStarted": "Background task started - check logs.",
|
"bgTaskStarted": "Background task started - check logs.",
|
||||||
"runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
|
"runBgCheckNow": "Kör Bakgrundsuppdateringskoll Nu",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Ta Bort App?",
|
"one": "Ta Bort App?",
|
||||||
"other": "Ta Bort Appar?"
|
"other": "Ta Bort Appar?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
|
"trySelectingSuggestedVersionCode": "Önerilen sürüm kodunu seçmeyi dene",
|
||||||
"dontSortReleasesList": "API'den sıralama düzenini koru",
|
"dontSortReleasesList": "API'den sıralama düzenini koru",
|
||||||
"reverseSort": "Ters sıralama",
|
"reverseSort": "Ters sıralama",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Hata Ayıklama Menüsü",
|
"debugMenu": "Hata Ayıklama Menüsü",
|
||||||
"bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
|
"bgTaskStarted": "Arka plan görevi başladı - günlükleri kontrol et.",
|
||||||
"runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
|
"runBgCheckNow": "Arka Plan Güncelleme Kontrolünü Şimdi Çalıştır",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
|
"onlyCheckInstalledOrTrackOnlyApps": "Yalnızca yüklü ve Yalnızca İzleme Uygulamalarını güncelleme",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Uygulamayı Kaldır?",
|
"one": "Uygulamayı Kaldır?",
|
||||||
"other": "Uygulamaları Kaldır?"
|
"other": "Uygulamaları Kaldır?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
|
"trySelectingSuggestedVersionCode": "Thử chọn APK Mã phiên bản được đề xuất",
|
||||||
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
|
"dontSortReleasesList": "Giữ lại thứ tự phát hành từ API",
|
||||||
"reverseSort": "Sắp xếp ngược",
|
"reverseSort": "Sắp xếp ngược",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "Danh sách gỡ lỗi",
|
"debugMenu": "Danh sách gỡ lỗi",
|
||||||
"bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
|
"bgTaskStarted": "Tác vụ nền đã bắt đầu - kiểm tra nhật ký.",
|
||||||
"runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
|
"runBgCheckNow": "Chạy kiểm tra cập nhật nền ngay bây giờ",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
|
"onlyCheckInstalledOrTrackOnlyApps": "Chỉ kiểm tra các ứng dụng đã cài đặt và Chỉ-Theo dõi để biết các bản cập nhật",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion":{
|
"removeAppQuestion":{
|
||||||
"one": "Gỡ ứng dụng?",
|
"one": "Gỡ ứng dụng?",
|
||||||
"other": "Gỡ ứng dụng?"
|
"other": "Gỡ ứng dụng?"
|
||||||
|
@ -261,6 +261,8 @@
|
|||||||
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
"trySelectingSuggestedVersionCode": "尝试选择推荐版本的 APK 文件",
|
||||||
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
"dontSortReleasesList": "保持来自 API 的发行顺序",
|
||||||
"reverseSort": "反转排序",
|
"reverseSort": "反转排序",
|
||||||
|
"takeFirstLink": "Take first link",
|
||||||
|
"skipSort": "Skip sorting",
|
||||||
"debugMenu": "调试选项",
|
"debugMenu": "调试选项",
|
||||||
"bgTaskStarted": "后台任务已启动 - 详见日志",
|
"bgTaskStarted": "后台任务已启动 - 详见日志",
|
||||||
"runBgCheckNow": "立即进行后台更新检查",
|
"runBgCheckNow": "立即进行后台更新检查",
|
||||||
@ -278,6 +280,7 @@
|
|||||||
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
|
"onlyCheckInstalledOrTrackOnlyApps": "只对已安装和“仅追踪”的应用进行更新检查",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Support fixed APK URLs",
|
||||||
"selectX": "Select {}",
|
"selectX": "Select {}",
|
||||||
|
"parallelDownloads": "Allow parallel downloads",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "是否删除应用?",
|
"one": "是否删除应用?",
|
||||||
"other": "是否删除应用?"
|
"other": "是否删除应用?"
|
||||||
|
@ -65,7 +65,7 @@ class FDroid extends AppSource {
|
|||||||
) async {
|
) async {
|
||||||
String? appId = await tryInferringAppId(standardUrl);
|
String? appId = await tryInferringAppId(standardUrl);
|
||||||
String host = Uri.parse(standardUrl).host;
|
String host = Uri.parse(standardUrl).host;
|
||||||
return getAPKUrlsFromFDroidPackagesAPIResponse(
|
var details = getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
await sourceRequest('https://$host/api/v1/packages/$appId'),
|
await sourceRequest('https://$host/api/v1/packages/$appId'),
|
||||||
'https://$host/repo/$appId',
|
'https://$host/repo/$appId',
|
||||||
standardUrl,
|
standardUrl,
|
||||||
@ -80,6 +80,23 @@ class FDroid extends AppSource {
|
|||||||
true
|
true
|
||||||
? additionalSettings['filterVersionsByRegEx']
|
? additionalSettings['filterVersionsByRegEx']
|
||||||
: null);
|
: null);
|
||||||
|
if (!hostChanged) {
|
||||||
|
try {
|
||||||
|
var res = await sourceRequest(
|
||||||
|
'https://gitlab.com/fdroid/fdroiddata/-/raw/master/metadata/$appId.yml');
|
||||||
|
String author = res.body
|
||||||
|
.split('\n')
|
||||||
|
.where((l) => l.startsWith('AuthorName: '))
|
||||||
|
.first
|
||||||
|
.split(': ')
|
||||||
|
.sublist(1)
|
||||||
|
.join(': ');
|
||||||
|
details.names.author = author;
|
||||||
|
} catch (e) {
|
||||||
|
// Fail silently
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return details;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -111,7 +128,6 @@ class FDroid extends AppSource {
|
|||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
|
Response res, String apkUrlPrefix, String standardUrl, String sourceName,
|
||||||
@ -187,3 +203,4 @@ APKDetails getAPKUrlsFromFDroidPackagesAPIResponse(
|
|||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -234,7 +234,7 @@ class GitHub extends AppSource {
|
|||||||
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
|
bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true;
|
||||||
bool dontSortReleasesList =
|
bool dontSortReleasesList =
|
||||||
additionalSettings['dontSortReleasesList'] == true;
|
additionalSettings['dontSortReleasesList'] == true;
|
||||||
String? latestTag;
|
dynamic latestRelease;
|
||||||
if (verifyLatestTag) {
|
if (verifyLatestTag) {
|
||||||
var temp = requestUrl.split('?');
|
var temp = requestUrl.split('?');
|
||||||
Response res = await sourceRequest(
|
Response res = await sourceRequest(
|
||||||
@ -245,12 +245,20 @@ class GitHub extends AppSource {
|
|||||||
}
|
}
|
||||||
throw getObtainiumHttpError(res);
|
throw getObtainiumHttpError(res);
|
||||||
}
|
}
|
||||||
var jsres = jsonDecode(res.body);
|
latestRelease = jsonDecode(res.body);
|
||||||
latestTag = jsres['tag_name'] ?? jsres['name'];
|
|
||||||
}
|
}
|
||||||
Response res = await sourceRequest(requestUrl);
|
Response res = await sourceRequest(requestUrl);
|
||||||
if (res.statusCode == 200) {
|
if (res.statusCode == 200) {
|
||||||
var releases = jsonDecode(res.body) as List<dynamic>;
|
var releases = jsonDecode(res.body) as List<dynamic>;
|
||||||
|
if (latestRelease != null) {
|
||||||
|
var latestTag = latestRelease['tag_name'] ?? latestRelease['name'];
|
||||||
|
if (releases
|
||||||
|
.where((element) =>
|
||||||
|
(element['tag_name'] ?? element['name']) == latestTag)
|
||||||
|
.isEmpty) {
|
||||||
|
releases = [latestRelease, ...releases];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
|
List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) =>
|
||||||
(release['assets'] as List<dynamic>?)
|
(release['assets'] as List<dynamic>?)
|
||||||
@ -299,13 +307,13 @@ class GitHub extends AppSource {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (latestTag != null &&
|
if (latestRelease != null &&
|
||||||
releases.isNotEmpty &&
|
releases.isNotEmpty &&
|
||||||
latestTag !=
|
latestRelease !=
|
||||||
(releases[releases.length - 1]['tag_name'] ??
|
(releases[releases.length - 1]['tag_name'] ??
|
||||||
releases[0]['name'])) {
|
releases[0]['name'])) {
|
||||||
var ind = releases.indexWhere(
|
var ind = releases.indexWhere((element) =>
|
||||||
(element) => latestTag == (element['tag_name'] ?? element['name']));
|
latestRelease == (element['tag_name'] ?? element['name']));
|
||||||
if (ind >= 0) {
|
if (ind >= 0) {
|
||||||
releases.add(releases.removeAt(ind));
|
releases.add(releases.removeAt(ind));
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,8 @@ class HTML extends AppSource {
|
|||||||
GeneratedFormSwitch('sortByFileNamesNotLinks',
|
GeneratedFormSwitch('sortByFileNamesNotLinks',
|
||||||
label: tr('sortByFileNamesNotLinks'))
|
label: tr('sortByFileNamesNotLinks'))
|
||||||
],
|
],
|
||||||
[GeneratedFormSwitch('reverseSort', label: tr('reverseSort'))],
|
[GeneratedFormSwitch('skipSort', label: tr('skipSort'))],
|
||||||
|
[GeneratedFormSwitch('reverseSort', label: tr('takeFirstLink'))],
|
||||||
[
|
[
|
||||||
GeneratedFormSwitch('supportFixedAPKURL',
|
GeneratedFormSwitch('supportFixedAPKURL',
|
||||||
defaultValue: true, label: tr('supportFixedAPKURL')),
|
defaultValue: true, label: tr('supportFixedAPKURL')),
|
||||||
@ -185,12 +186,15 @@ class HTML extends AppSource {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
List<String> links = [];
|
List<String> links = [];
|
||||||
|
bool skipSort = additionalSettings['skipSort'] == true;
|
||||||
if ((additionalSettings['intermediateLinkRegex'] as String?)
|
if ((additionalSettings['intermediateLinkRegex'] as String?)
|
||||||
?.isNotEmpty ==
|
?.isNotEmpty ==
|
||||||
true) {
|
true) {
|
||||||
var reg = RegExp(additionalSettings['intermediateLinkRegex']);
|
var reg = RegExp(additionalSettings['intermediateLinkRegex']);
|
||||||
links = allLinks.where((element) => reg.hasMatch(element)).toList();
|
links = allLinks.where((element) => reg.hasMatch(element)).toList();
|
||||||
|
if (!skipSort) {
|
||||||
links.sort((a, b) => compareAlphaNumeric(a, b));
|
links.sort((a, b) => compareAlphaNumeric(a, b));
|
||||||
|
}
|
||||||
if (links.isEmpty) {
|
if (links.isEmpty) {
|
||||||
throw ObtainiumError(tr('intermediateLinkNotFound'));
|
throw ObtainiumError(tr('intermediateLinkNotFound'));
|
||||||
}
|
}
|
||||||
@ -211,10 +215,14 @@ class HTML extends AppSource {
|
|||||||
Uri.parse(element).path.toLowerCase().endsWith('.apk'))
|
Uri.parse(element).path.toLowerCase().endsWith('.apk'))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
links.sort((a, b) => additionalSettings['sortByFileNamesNotLinks'] == true
|
if (!skipSort) {
|
||||||
? compareAlphaNumeric(a.split('/').where((e) => e.isNotEmpty).last,
|
links.sort((a, b) =>
|
||||||
|
additionalSettings['sortByFileNamesNotLinks'] == true
|
||||||
|
? compareAlphaNumeric(
|
||||||
|
a.split('/').where((e) => e.isNotEmpty).last,
|
||||||
b.split('/').where((e) => e.isNotEmpty).last)
|
b.split('/').where((e) => e.isNotEmpty).last)
|
||||||
: compareAlphaNumeric(a, b));
|
: compareAlphaNumeric(a, b));
|
||||||
|
}
|
||||||
if (additionalSettings['reverseSort'] == true) {
|
if (additionalSettings['reverseSort'] == true) {
|
||||||
links = links.reversed.toList();
|
links = links.reversed.toList();
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class IzzyOnDroid extends AppSource {
|
|||||||
Map<String, dynamic> additionalSettings,
|
Map<String, dynamic> additionalSettings,
|
||||||
) async {
|
) async {
|
||||||
String? appId = await tryInferringAppId(standardUrl);
|
String? appId = await tryInferringAppId(standardUrl);
|
||||||
return getAPKUrlsFromFDroidPackagesAPIResponse(
|
return fd.getAPKUrlsFromFDroidPackagesAPIResponse(
|
||||||
await sourceRequest(
|
await sourceRequest(
|
||||||
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
|
'https://apt.izzysoft.de/fdroid/api/v1/packages/$appId'),
|
||||||
'https://android.izzysoft.de/frepo/$appId',
|
'https://android.izzysoft.de/frepo/$appId',
|
||||||
|
@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
|
|||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
|
|
||||||
const String currentVersion = '0.14.37';
|
const String currentVersion = '0.14.41';
|
||||||
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
|
||||||
|
|
||||||
|
@ -496,14 +496,8 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
var transparent =
|
var transparent =
|
||||||
Theme.of(context).colorScheme.background.withAlpha(0).value;
|
Theme.of(context).colorScheme.background.withAlpha(0).value;
|
||||||
List<double> stops = [
|
List<double> stops = [
|
||||||
...listedApps[index]
|
...listedApps[index].app.categories.asMap().entries.map(
|
||||||
.app
|
(e) => ((e.key / (listedApps[index].app.categories.length - 1)))),
|
||||||
.categories
|
|
||||||
.asMap()
|
|
||||||
.entries
|
|
||||||
.map((e) =>
|
|
||||||
((e.key / (listedApps[index].app.categories.length - 1))))
|
|
||||||
,
|
|
||||||
1
|
1
|
||||||
];
|
];
|
||||||
if (stops.length == 2) {
|
if (stops.length == 2) {
|
||||||
@ -516,13 +510,9 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
begin: const Alignment(-1, 0),
|
begin: const Alignment(-1, 0),
|
||||||
end: const Alignment(-0.97, 0),
|
end: const Alignment(-0.97, 0),
|
||||||
colors: [
|
colors: [
|
||||||
...listedApps[index]
|
...listedApps[index].app.categories.map((e) =>
|
||||||
.app
|
|
||||||
.categories
|
|
||||||
.map((e) =>
|
|
||||||
Color(settingsProvider.categories[e] ?? transparent)
|
Color(settingsProvider.categories[e] ?? transparent)
|
||||||
.withAlpha(255))
|
.withAlpha(255)),
|
||||||
,
|
|
||||||
Color(transparent)
|
Color(transparent)
|
||||||
])),
|
])),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@ -881,7 +871,7 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
String urls = '';
|
String urls = '';
|
||||||
for (var a in selectedApps) {
|
for (var a in selectedApps) {
|
||||||
urls += '${a.url}\n';
|
urls += 'obtainium://add/${a.url}\n';
|
||||||
}
|
}
|
||||||
urls = urls.substring(0, urls.length - 1);
|
urls = urls.substring(0, urls.length - 1);
|
||||||
Share.share(urls,
|
Share.share(urls,
|
||||||
@ -981,10 +971,8 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
defaultValue: filter.sourceFilter,
|
defaultValue: filter.sourceFilter,
|
||||||
[
|
[
|
||||||
MapEntry('', tr('none')),
|
MapEntry('', tr('none')),
|
||||||
...sourceProvider.sources
|
...sourceProvider.sources.map(
|
||||||
.map((e) =>
|
(e) => MapEntry(e.runtimeType.toString(), e.name))
|
||||||
MapEntry(e.runtimeType.toString(), e.name))
|
|
||||||
|
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
@ -347,7 +347,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
: () {
|
: () {
|
||||||
runObtainiumExport(pickOnly: true);
|
runObtainiumExport(pickOnly: true);
|
||||||
},
|
},
|
||||||
child: Text(tr('pickExportDir')),
|
child: Text(tr('pickExportDir'),
|
||||||
|
textAlign: TextAlign.center),
|
||||||
)),
|
)),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 16,
|
width: 16,
|
||||||
@ -360,7 +361,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
snapshot.data == null
|
snapshot.data == null
|
||||||
? null
|
? null
|
||||||
: runObtainiumExport,
|
: runObtainiumExport,
|
||||||
child: Text(tr('obtainiumExport')),
|
child: Text(tr('obtainiumExport'),
|
||||||
|
textAlign: TextAlign.center),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -375,7 +377,8 @@ class _ImportExportPageState extends State<ImportExportPage> {
|
|||||||
onPressed: importInProgress
|
onPressed: importInProgress
|
||||||
? null
|
? null
|
||||||
: runObtainiumImport,
|
: runObtainiumImport,
|
||||||
child: Text(tr('obtainiumImport')))),
|
child: Text(tr('obtainiumImport'),
|
||||||
|
textAlign: TextAlign.center))),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (snapshot.data != null)
|
if (snapshot.data != null)
|
||||||
|
@ -30,6 +30,29 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
settingsProvider.initializeSettings();
|
settingsProvider.initializeSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var installMethodDropdown = DropdownButtonFormField(
|
||||||
|
decoration: InputDecoration(labelText: tr('installMethod')),
|
||||||
|
value: settingsProvider.installMethod,
|
||||||
|
items: [
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: InstallMethodSettings.normal,
|
||||||
|
child: Text(tr('normal')),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: InstallMethodSettings.shizuku,
|
||||||
|
child: Text(tr('shizuku')),
|
||||||
|
),
|
||||||
|
DropdownMenuItem(
|
||||||
|
value: InstallMethodSettings.root,
|
||||||
|
child: Text(tr('root')),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
settingsProvider.installMethod = value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var themeDropdown = DropdownButtonFormField(
|
var themeDropdown = DropdownButtonFormField(
|
||||||
decoration: InputDecoration(labelText: tr('theme')),
|
decoration: InputDecoration(labelText: tr('theme')),
|
||||||
value: settingsProvider.theme,
|
value: settingsProvider.theme,
|
||||||
@ -327,6 +350,21 @@ class _SettingsPageState extends State<SettingsPage> {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
height16,
|
||||||
|
installMethodDropdown,
|
||||||
|
height16,
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Flexible(child: Text(tr('parallelDownloads'))),
|
||||||
|
Switch(
|
||||||
|
value: settingsProvider.parallelDownloads,
|
||||||
|
onChanged: (value) {
|
||||||
|
settingsProvider.parallelDownloads =
|
||||||
|
value;
|
||||||
|
})
|
||||||
|
],
|
||||||
|
),
|
||||||
height32,
|
height32,
|
||||||
Text(
|
Text(
|
||||||
tr('sourceSpecific'),
|
tr('sourceSpecific'),
|
||||||
|
@ -33,6 +33,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';
|
||||||
|
|
||||||
final pm = AndroidPackageManager();
|
final pm = AndroidPackageManager();
|
||||||
|
|
||||||
@ -504,7 +505,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
!(await canDowngradeApps())) {
|
!(await canDowngradeApps())) {
|
||||||
throw DowngradeError();
|
throw DowngradeError();
|
||||||
}
|
}
|
||||||
if (needsBGWorkaround) {
|
if (needsBGWorkaround &&
|
||||||
|
settingsProvider.installMethod == InstallMethodSettings.normal) {
|
||||||
// The below 'await' will never return if we are in a background process
|
// The below 'await' will never return if we are in a background process
|
||||||
// To work around this, we should assume the install will be successful
|
// To work around this, we should assume the install will be successful
|
||||||
// So we update the app's installed version first as we will never get to the later code
|
// So we update the app's installed version first as we will never get to the later code
|
||||||
@ -515,8 +517,21 @@ class AppsProvider with ChangeNotifier {
|
|||||||
await saveApps([apps[file.appId]!.app],
|
await saveApps([apps[file.appId]!.app],
|
||||||
attemptToCorrectInstallStatus: false);
|
attemptToCorrectInstallStatus: false);
|
||||||
}
|
}
|
||||||
int? code =
|
int? code;
|
||||||
await AndroidPackageInstaller.installApk(apkFilePath: file.file.path);
|
switch (settingsProvider.installMethod) {
|
||||||
|
case InstallMethodSettings.normal:
|
||||||
|
code = await AndroidPackageInstaller.installApk(
|
||||||
|
apkFilePath: file.file.path);
|
||||||
|
case InstallMethodSettings.shizuku:
|
||||||
|
code = (await Installers.installWithShizuku(
|
||||||
|
apkFileUri: file.file.uri.toString()))
|
||||||
|
? 0
|
||||||
|
: 1;
|
||||||
|
case InstallMethodSettings.root:
|
||||||
|
code = (await Installers.installWithRoot(apkFilePath: file.file.path))
|
||||||
|
? 0
|
||||||
|
: 1;
|
||||||
|
}
|
||||||
bool installed = false;
|
bool installed = false;
|
||||||
if (code != null && code != 0 && code != 3) {
|
if (code != null && code != 0 && code != 3) {
|
||||||
throw InstallError(code);
|
throw InstallError(code);
|
||||||
@ -657,7 +672,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
appsToInstall =
|
appsToInstall =
|
||||||
moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
|
moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId);
|
||||||
|
|
||||||
for (var id in appsToInstall) {
|
Future<void> updateFn(String id, {bool skipInstalls = false}) async {
|
||||||
try {
|
try {
|
||||||
var downloadedArtifact =
|
var downloadedArtifact =
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
@ -672,9 +687,24 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
var appId = downloadedFile?.appId ?? downloadedDir!.appId;
|
var appId = downloadedFile?.appId ?? downloadedDir!.appId;
|
||||||
bool willBeSilent = await canInstallSilently(apps[appId]!.app);
|
bool willBeSilent = await canInstallSilently(apps[appId]!.app);
|
||||||
if (!(await settingsProvider.getInstallPermission(enforce: false))) {
|
switch (settingsProvider.installMethod) {
|
||||||
|
case InstallMethodSettings.normal:
|
||||||
|
if (!(await settingsProvider.getInstallPermission(
|
||||||
|
enforce: false))) {
|
||||||
throw ObtainiumError(tr('cancelled'));
|
throw ObtainiumError(tr('cancelled'));
|
||||||
}
|
}
|
||||||
|
case InstallMethodSettings.shizuku:
|
||||||
|
int code = await Installers.checkPermissionShizuku();
|
||||||
|
if (code == -1) {
|
||||||
|
throw ObtainiumError(tr('shizukuBinderNotFound'));
|
||||||
|
} else if (code == 0) {
|
||||||
|
throw ObtainiumError(tr('cancelled'));
|
||||||
|
}
|
||||||
|
case InstallMethodSettings.root:
|
||||||
|
if (!(await Installers.checkPermissionRoot())) {
|
||||||
|
throw ObtainiumError(tr('cancelled'));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!willBeSilent && context != null) {
|
if (!willBeSilent && context != null) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
await waitForUserToReturnToForeground(context);
|
await waitForUserToReturnToForeground(context);
|
||||||
@ -682,6 +712,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
apps[id]?.downloadProgress = -1;
|
apps[id]?.downloadProgress = -1;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
try {
|
try {
|
||||||
|
if (!skipInstalls) {
|
||||||
if (downloadedFile != null) {
|
if (downloadedFile != null) {
|
||||||
if (willBeSilent && context == null) {
|
if (willBeSilent && context == null) {
|
||||||
installApk(downloadedFile, needsBGWorkaround: true);
|
installApk(downloadedFile, needsBGWorkaround: true);
|
||||||
@ -700,6 +731,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
[apps[appId]!.app],
|
[apps[appId]!.app],
|
||||||
id: appId.hashCode));
|
id: appId.hashCode));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
apps[id]?.downloadProgress = null;
|
apps[id]?.downloadProgress = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -710,6 +742,20 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!settingsProvider.parallelDownloads) {
|
||||||
|
for (var id in appsToInstall) {
|
||||||
|
await updateFn(id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await Future.wait(
|
||||||
|
appsToInstall.map((id) => updateFn(id, skipInstalls: true)));
|
||||||
|
for (var id in appsToInstall) {
|
||||||
|
if (!errors.appIdNames.containsKey(id)) {
|
||||||
|
await updateFn(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (errors.idsByErrorString.isNotEmpty) {
|
if (errors.idsByErrorString.isNotEmpty) {
|
||||||
throw errors;
|
throw errors;
|
||||||
}
|
}
|
||||||
@ -726,14 +772,17 @@ class AppsProvider with ChangeNotifier {
|
|||||||
return appsDir;
|
return appsDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PackageInfo?> getInstalledInfo(String? packageName) async {
|
Future<PackageInfo?> getInstalledInfo(String? packageName,
|
||||||
|
{bool printErr = true}) async {
|
||||||
if (packageName != null) {
|
if (packageName != null) {
|
||||||
try {
|
try {
|
||||||
return await pm.getPackageInfo(packageName: packageName);
|
return await pm.getPackageInfo(packageName: packageName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (printErr) {
|
||||||
print(e); // OK
|
print(e); // OK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1230,7 +1279,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<MapEntry<int, bool>> import(String appsJSON) async {
|
Future<MapEntry<int, bool>> import(String appsJSON) async {
|
||||||
var decodedJSON = jsonDecode(appsJSON);
|
var decodedJSON = jsonDecode(appsJSON);
|
||||||
var newFormat = !(decodedJSON is List);
|
var newFormat = decodedJSON is! List;
|
||||||
List<App> importedApps =
|
List<App> importedApps =
|
||||||
((newFormat ? decodedJSON['apps'] : decodedJSON) as List<dynamic>)
|
((newFormat ? decodedJSON['apps'] : decodedJSON) as List<dynamic>)
|
||||||
.map((e) => App.fromJson(e))
|
.map((e) => App.fromJson(e))
|
||||||
@ -1239,9 +1288,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
await Future.delayed(const Duration(microseconds: 1));
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
}
|
}
|
||||||
for (App a in importedApps) {
|
for (App a in importedApps) {
|
||||||
if (apps[a.id]?.app.installedVersion != null) {
|
a.installedVersion =
|
||||||
a.installedVersion = apps[a.id]?.app.installedVersion;
|
(await getInstalledInfo(a.id, printErr: false))?.versionName;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
await saveApps(importedApps, onlyIfExists: false);
|
await saveApps(importedApps, onlyIfExists: false);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
56
lib/providers/installers_provider.dart
Normal file
56
lib/providers/installers_provider.dart
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class Installers {
|
||||||
|
static const MethodChannel _channel = MethodChannel('installers');
|
||||||
|
static bool _callbacksApplied = false;
|
||||||
|
static int _resPermShizuku = -2; // not set
|
||||||
|
|
||||||
|
static Future waitWhile(bool Function() test,
|
||||||
|
[Duration pollInterval = const Duration(milliseconds: 250)]) {
|
||||||
|
var completer = Completer();
|
||||||
|
check() {
|
||||||
|
if (test()) {
|
||||||
|
Timer(pollInterval, check);
|
||||||
|
} else {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check();
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future handleCalls(MethodCall call) async {
|
||||||
|
if (call.method == 'resPermShizuku') {
|
||||||
|
_resPermShizuku = call.arguments['res'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<int> checkPermissionShizuku() async {
|
||||||
|
if (!_callbacksApplied) {
|
||||||
|
_channel.setMethodCallHandler(handleCalls);
|
||||||
|
_callbacksApplied = true;
|
||||||
|
}
|
||||||
|
int res = await _channel.invokeMethod('checkPermissionShizuku');
|
||||||
|
if(res == -2) {
|
||||||
|
await waitWhile(() => _resPermShizuku == -2);
|
||||||
|
res = _resPermShizuku;
|
||||||
|
_resPermShizuku = -2;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> checkPermissionRoot() async {
|
||||||
|
return await _channel.invokeMethod('checkPermissionRoot');
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> installWithShizuku({required String apkFileUri}) async {
|
||||||
|
return await _channel.invokeMethod(
|
||||||
|
'installWithShizuku', {'apkFileUri': apkFileUri});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> installWithRoot({required String apkFilePath}) async {
|
||||||
|
return await _channel.invokeMethod(
|
||||||
|
'installWithRoot', {'apkFilePath': apkFilePath});
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,8 @@ import 'package:shared_storage/shared_storage.dart' as saf;
|
|||||||
String obtainiumTempId = 'imranr98_obtainium_${GitHub().host}';
|
String obtainiumTempId = 'imranr98_obtainium_${GitHub().host}';
|
||||||
String obtainiumId = 'dev.imranr.obtainium';
|
String obtainiumId = 'dev.imranr.obtainium';
|
||||||
|
|
||||||
|
enum InstallMethodSettings { normal, shizuku, root }
|
||||||
|
|
||||||
enum ThemeSettings { system, light, dark }
|
enum ThemeSettings { system, light, dark }
|
||||||
|
|
||||||
enum ColourSettings { basic, materialYou }
|
enum ColourSettings { basic, materialYou }
|
||||||
@ -49,6 +51,16 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InstallMethodSettings get installMethod {
|
||||||
|
return InstallMethodSettings
|
||||||
|
.values[prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
set installMethod(InstallMethodSettings t) {
|
||||||
|
prefs?.setInt('installMethod', t.index);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
ThemeSettings get theme {
|
ThemeSettings get theme {
|
||||||
return ThemeSettings
|
return ThemeSettings
|
||||||
.values[prefs?.getInt('theme') ?? ThemeSettings.system.index];
|
.values[prefs?.getInt('theme') ?? ThemeSettings.system.index];
|
||||||
@ -425,4 +437,13 @@ class SettingsProvider with ChangeNotifier {
|
|||||||
prefs?.setBool('exportSettings', val);
|
prefs?.setBool('exportSettings', val);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get parallelDownloads {
|
||||||
|
return prefs?.getBool('parallelDownloads') ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
set parallelDownloads(bool val) {
|
||||||
|
prefs?.setBool('parallelDownloads', val);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -684,7 +684,8 @@ class SourceProvider {
|
|||||||
name = name.isNotEmpty ? name : apk.names.name;
|
name = name.isNotEmpty ? name : apk.names.name;
|
||||||
App finalApp = App(
|
App finalApp = App(
|
||||||
currentApp?.id ??
|
currentApp?.id ??
|
||||||
((!source.appIdInferIsOptional ||
|
(!trackOnly &&
|
||||||
|
(!source.appIdInferIsOptional ||
|
||||||
(source.appIdInferIsOptional && inferAppIdIfOptional))
|
(source.appIdInferIsOptional && inferAppIdIfOptional))
|
||||||
? await source.tryInferringAppId(standardUrl,
|
? await source.tryInferringAppId(standardUrl,
|
||||||
additionalSettings: additionalSettings)
|
additionalSettings: additionalSettings)
|
||||||
@ -705,8 +706,9 @@ class SourceProvider {
|
|||||||
changeLog: apk.changeLog,
|
changeLog: apk.changeLog,
|
||||||
overrideSource: overrideSource ?? currentApp?.overrideSource,
|
overrideSource: overrideSource ?? currentApp?.overrideSource,
|
||||||
allowIdChange: currentApp?.allowIdChange ??
|
allowIdChange: currentApp?.allowIdChange ??
|
||||||
source.appIdInferIsOptional &&
|
trackOnly ||
|
||||||
inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install
|
(source.appIdInferIsOptional &&
|
||||||
|
inferAppIdIfOptional) // Optional ID inferring may be incorrect - allow correction on first install
|
||||||
);
|
);
|
||||||
return source.endOfGetAppChanges(finalApp);
|
return source.endOfGetAppChanges(finalApp);
|
||||||
}
|
}
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -206,10 +206,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: dynamic_color
|
name: dynamic_color
|
||||||
sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f"
|
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.8"
|
version: "1.6.9"
|
||||||
easy_localization:
|
easy_localization:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -267,10 +267,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_archive
|
name: flutter_archive
|
||||||
sha256: aec85d1da65e5b33a529db00a86df0b8e92bda78088a7cfaeeba5187701d0d85
|
sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "6.0.0"
|
||||||
flutter_fgbg:
|
flutter_fgbg:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -783,10 +783,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: synchronized
|
name: synchronized
|
||||||
sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60"
|
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.0+1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -919,26 +919,26 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_android
|
name: webview_flutter_android
|
||||||
sha256: e313dcdf45d4c95bcb8960351ef2389b7f0687b90bc92483f7f7983ae5758456
|
sha256: b54c89fe14a6d26a2a46e24880da0441cdd2bf1f6d01a5b3e1d39558feb1de0b
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.13.0"
|
version: "3.13.1"
|
||||||
webview_flutter_platform_interface:
|
webview_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_platform_interface
|
name: webview_flutter_platform_interface
|
||||||
sha256: "68e86162aa8fc646ae859e1585995c096c95fc2476881fa0c4a8d10f56013a5a"
|
sha256: dbe745ee459a16b6fec296f7565a8ef430d0d681001d8ae521898b9361854943
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.0"
|
version: "2.9.0"
|
||||||
webview_flutter_wkwebview:
|
webview_flutter_wkwebview:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: webview_flutter_wkwebview
|
name: webview_flutter_wkwebview
|
||||||
sha256: accdaaa49a2aca2dc3c3230907988954cdd23fed0a19525d6c9789d380f4dc76
|
sha256: eebfabfa8a115b535b52031b8b26f7a4b58ceceab378bc9db8762b0fb46f7b5d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.9.4"
|
version: "3.10.0"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.14.37+231 # When changing this, update the tag in main() accordingly
|
version: 0.14.41+235 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
@ -62,7 +62,7 @@ dependencies:
|
|||||||
easy_localization: ^3.0.1
|
easy_localization: ^3.0.1
|
||||||
android_intent_plus: ^4.0.0
|
android_intent_plus: ^4.0.0
|
||||||
flutter_markdown: ^0.6.14
|
flutter_markdown: ^0.6.14
|
||||||
flutter_archive: ^5.0.0
|
flutter_archive: ^6.0.0
|
||||||
hsluv: ^1.1.3
|
hsluv: ^1.1.3
|
||||||
connectivity_plus: ^5.0.0
|
connectivity_plus: ^5.0.0
|
||||||
shared_storage: ^0.8.0
|
shared_storage: ^0.8.0
|
||||||
|
Reference in New Issue
Block a user