Merge branch 'main' into room-entity

This commit is contained in:
LooKeR 2025-06-08 17:23:36 +05:30 committed by GitHub
commit 22a1b09d2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
146 changed files with 2237 additions and 2443 deletions

View File

@ -6,7 +6,6 @@ insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.{kt,kts}] [*.{kt,kts}]
ktlint_code_style = android_studio
indent_size = 4 indent_size = 4
ij_kotlin_allow_trailing_comma=true ij_kotlin_allow_trailing_comma=true
ij_kotlin_allow_trailing_comma_on_call_site=true ij_kotlin_allow_trailing_comma_on_call_site=true

View File

@ -27,51 +27,48 @@ jobs:
packages: write packages: write
steps: steps:
- name: Check out repository - name: Check out repository
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
submodules: true submodules: true
- name: Validate Gradle Wrapper - name: Validate Gradle Wrapper
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
- name: Setup Gradle - name: Setup Gradle
uses: gradle/wrapper-validation-action@v3 uses: gradle/wrapper-validation-action@v3
- name: Set up Java 17 - name: Set up Java 17
uses: actions/setup-java@v4 uses: actions/setup-java@v4
with: with:
java-version: 17 java-version: 17
distribution: 'adopt' distribution: 'adopt'
cache: gradle cache: gradle
- name: Grant execution permission to Gradle Wrapper - name: Grant execution permission to Gradle Wrapper
run: chmod +x gradlew run: chmod +x gradlew
- name: Format Code - name: Build Debug APK
run: ./gradlew ktlintFormat run: ./gradlew assembleDebug
- name: Build Debug APK - name: Sign Apk
run: ./gradlew assembleDebug continue-on-error: true
id: sign_apk
uses: r0adkll/sign-android-release@v1
with:
releaseDir: app/build/outputs/apk/debug
signingKeyBase64: ${{ secrets.KEY_BASE64 }}
alias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEYSTORE_PASS }}
keyPassword: ${{ secrets.KEYSTORE_PASS }}
- name: Sign Apk - name: Remove file that aren't signed
continue-on-error: true continue-on-error: true
id: sign_apk run: |
uses: r0adkll/sign-android-release@v1 ls | grep 'signed\.apk$' && find . -type f -name '*.apk' ! -name '*-signed.apk' -delete
with:
releaseDir: app/build/outputs/apk/debug
signingKeyBase64: ${{ secrets.KEY_BASE64 }}
alias: ${{ secrets.KEY_ALIAS }}
keyStorePassword: ${{ secrets.KEYSTORE_PASS }}
keyPassword: ${{ secrets.KEYSTORE_PASS }}
- name: Remove file that aren't signed - name: Upload the APK
continue-on-error: true uses: actions/upload-artifact@v4
run: | with:
ls | grep 'signed\.apk$' && find . -type f -name '*.apk' ! -name '*-signed.apk' -delete name: debug
path: app/build/outputs/apk/debug/app-debug*.apk
- name: Upload the APK
uses: actions/upload-artifact@v4
with:
name: debug
path: app/build/outputs/apk/debug/app-debug*.apk

View File

@ -56,12 +56,34 @@ jobs:
keyStorePassword: ${{ secrets.KEYSTORE_PASS }} keyStorePassword: ${{ secrets.KEYSTORE_PASS }}
keyPassword: ${{ secrets.KEYSTORE_PASS }} keyPassword: ${{ secrets.KEYSTORE_PASS }}
env: env:
BUILD_TOOLS_VERSION: "34.0.0" BUILD_TOOLS_VERSION: "35.0.0"
- name: Extract Version Code
id: extract_version
run: |
VERSION_CODE=$(grep -oP '(?<=versionCode=)\d+' app/build.gradle.kts) # Adjust path to your build.gradle
echo "::set-output name=version_code::$VERSION_CODE"
echo "Version Code: $VERSION_CODE"
- name: Read Changelog
id: read_changelog
run: |
CHANGELOG_PATH="metadata/en-US/changelogs/${{ steps.extract_version.outputs.version_code }}.txt"
if [[ -f "$CHANGELOG_PATH" ]]; then
CHANGELOG=$(cat "$CHANGELOG_PATH")
echo "::set-output name=changelog::$CHANGELOG"
else
echo "::set-output name=changelog::No changelog found for this version."
echo "No changelog found at: $CHANGELOG_PATH"
fi
- uses: softprops/action-gh-release@v2 - uses: softprops/action-gh-release@v2
name: Create Release name: Create Release
id: publish_release id: publish_release
with: with:
body: ${{ steps.read_changelog.outputs.changelog }}
tag_name: ${{ github.ref }}
name: Release ${{ github.ref }}
files: ${{steps.sign_app.outputs.signedReleaseFile}} files: ${{steps.sign_app.outputs.signedReleaseFile}}
draft: true draft: true
prerelease: false prerelease: false

View File

@ -7,7 +7,7 @@
[![Github Downloads](https://img.shields.io/github/downloads/Iamlooker/Droid-ify/total.svg?color=%23f5ad64&style=for-the-badge)](https://github.com/Iamlooker/Droid-ify/releases/) [![Github Downloads](https://img.shields.io/github/downloads/Iamlooker/Droid-ify/total.svg?color=%23f5ad64&style=for-the-badge)](https://github.com/Iamlooker/Droid-ify/releases/)
[![Github Latest](https://img.shields.io/github/v/release/Iamlooker/Droid-ify?display_name=tag&color=%23f5ad64&style=for-the-badge)](https://github.com/Iamlooker/Droid-ify/releases/latest) [![Github Latest](https://img.shields.io/github/v/release/Iamlooker/Droid-ify?display_name=tag&color=%23f5ad64&style=for-the-badge)](https://github.com/Iamlooker/Droid-ify/releases/latest)
[![FDroid Latest](https://img.shields.io/f-droid/v/com.looker.droidify?color=%23f5ad64&style=for-the-badge)](https://f-droid.org/packages/com.looker.droidify) [![FDroid Latest](https://img.shields.io/f-droid/v/com.looker.droidify?color=%23f5ad64&style=for-the-badge)](https://f-droid.org/packages/com.looker.droidify)
</div>
<div align="left"> <div align="left">
## Features ## Features
@ -22,8 +22,10 @@
<img src="metadata/en-US/images/phoneScreenshots/1.png" width="25%" /><img src="metadata/en-US/images/phoneScreenshots/2.png" width="25%" /><img src="metadata/en-US/images/phoneScreenshots/3.png" width="25%" /><img src="metadata/en-US/images/phoneScreenshots/4.png" width="25%" /> <img src="metadata/en-US/images/phoneScreenshots/1.png" width="25%" /><img src="metadata/en-US/images/phoneScreenshots/2.png" width="25%" /><img src="metadata/en-US/images/phoneScreenshots/3.png" width="25%" /><img src="metadata/en-US/images/phoneScreenshots/4.png" width="25%" />
## Building and Installing ## Building and Installing
1. **Install Android Studio**: 1. **Install Android Studio**:
- Download and install [Android Studio](https://developer.android.com/studio) on your computer if you haven't already. - Download and install [Android Studio](https://developer.android.com/studio) on your computer
if you haven't already.
2. **Clone the Repository**: 2. **Clone the Repository**:
- Open Android Studio and select "Project from Version Control." - Open Android Studio and select "Project from Version Control."
@ -48,6 +50,7 @@
- Your PR will undergo review - Your PR will undergo review
## Translations ## Translations
[![Translation status](https://hosted.weblate.org/widgets/droidify/-/horizontal-auto.svg)](https://hosted.weblate.org/engage/droidify/?utm_source=widget) [![Translation status](https://hosted.weblate.org/widgets/droidify/-/horizontal-auto.svg)](https://hosted.weblate.org/engage/droidify/?utm_source=widget)
## License ## License
@ -67,3 +70,5 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
``` ```
</div>

View File

@ -10,7 +10,7 @@ plugins {
} }
android { android {
val latestVersionName = "0.6.4" val latestVersionName = "0.6.5"
namespace = "com.looker.droidify" namespace = "com.looker.droidify"
buildToolsVersion = "35.0.0" buildToolsVersion = "35.0.0"
compileSdk = 35 compileSdk = 35
@ -18,7 +18,7 @@ android {
minSdk = 23 minSdk = 23
targetSdk = 35 targetSdk = 35
applicationId = "com.looker.droidify" applicationId = "com.looker.droidify"
versionCode = 640 versionCode = 650
versionName = latestVersionName versionName = latestVersionName
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
testInstrumentationRunner = "com.looker.droidify.TestRunner" testInstrumentationRunner = "com.looker.droidify.TestRunner"
@ -32,7 +32,12 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "17" jvmTarget = "17"
freeCompilerArgs = listOf("-Xcontext-receivers") freeCompilerArgs = listOf(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
"-Xcontext-receivers"
)
} }
ksp { ksp {
@ -45,11 +50,11 @@ android {
} }
buildTypes { buildTypes {
getByName("debug") { debug {
applicationIdSuffix = ".debug" applicationIdSuffix = ".debug"
resValue("string", "application_name", "Droid-ify-Debug") resValue("string", "application_name", "Droid-ify-Debug")
} }
getByName("release") { release {
isMinifyEnabled = true isMinifyEnabled = true
isShrinkResources = true isShrinkResources = true
resValue("string", "application_name", "Droid-ify") resValue("string", "application_name", "Droid-ify")
@ -79,7 +84,16 @@ android {
} }
packaging { packaging {
resources { resources {
excludes += listOf("/DebugProbesKt.bin") excludes += listOf(
"/DebugProbesKt.bin",
"/kotlin/**.kotlin_builtins",
"/kotlin/**.kotlin_metadata",
"/META-INF/**.kotlin_module",
"/META-INF/**.pro",
"/META-INF/**.version",
"/META-INF/{AL2.0,LGPL2.1,LICENSE*}",
"/META-INF/versions/9/previous-**.bin",
)
} }
} }
buildFeatures { buildFeatures {
@ -87,6 +101,10 @@ android {
viewBinding = true viewBinding = true
buildConfig = true buildConfig = true
} }
dependenciesInfo {
includeInApk = false
includeInBundle = false
}
} }
dependencies { dependencies {
@ -102,7 +120,7 @@ dependencies {
implementation(libs.sqlite.ktx) implementation(libs.sqlite.ktx)
implementation(libs.image.viewer) implementation(libs.image.viewer)
implementation(libs.coil.kt) implementation(libs.bundles.coil)
implementation(libs.datastore.core) implementation(libs.datastore.core)
implementation(libs.datastore.proto) implementation(libs.datastore.proto)

65
app/proguard.pro vendored
View File

@ -7,68 +7,3 @@
-dontwarn kotlinx.serialization.KSerializer -dontwarn kotlinx.serialization.KSerializer
-dontwarn kotlinx.serialization.Serializable -dontwarn kotlinx.serialization.Serializable
-dontwarn org.slf4j.impl.StaticLoggerBinder -dontwarn org.slf4j.impl.StaticLoggerBinder
#
#-dontwarn com.looker.core.common.BuildConfig
#-dontwarn com.looker.core.common.DeeplinkType$AddRepository
#-dontwarn com.looker.core.common.DeeplinkType$AppDetail
#-dontwarn com.looker.core.common.DeeplinkType
#-dontwarn com.looker.core.common.DeeplinksKt
#-dontwarn com.looker.core.common.Exporter
#-dontwarn com.looker.core.common.NotificationKt
#-dontwarn com.looker.core.common.PermissionsKt
#-dontwarn com.looker.core.common.Scroller
#-dontwarn com.looker.core.common.SdkCheck
#-dontwarn com.looker.core.common.Singleton
#-dontwarn com.looker.core.common.TextKt
#-dontwarn com.looker.core.common.cache.Cache
#-dontwarn com.looker.core.common.cache.Cache
#-dontwarn com.looker.core.common.device.Huawei
#-dontwarn com.looker.core.common.extension.ContextKt
#-dontwarn com.looker.core.common.extension.CursorKt
#-dontwarn com.looker.core.common.extension.DateTimeKt
#-dontwarn com.looker.core.common.extension.FingerprintKt
#-dontwarn com.looker.core.common.extension.FlowKt
#-dontwarn com.looker.core.common.extension.InsetsKt
#-dontwarn com.looker.core.common.extension.IntentKt
#-dontwarn com.looker.core.common.extension.Json
#-dontwarn com.looker.core.common.extension.JsonKt
#-dontwarn com.looker.core.common.extension.KeyToken
#-dontwarn com.looker.core.common.extension.LocaleKt
#-dontwarn com.looker.core.common.extension.NumberKt
#-dontwarn com.looker.core.common.extension.PackageInfoKt
#-dontwarn com.looker.core.common.extension.SQLiteDatabaseKt
#-dontwarn com.looker.core.common.extension.ServiceKt
#-dontwarn com.looker.core.common.extension.ViewKt
#-dontwarn com.looker.core.common.result.Result$Error
#-dontwarn com.looker.core.common.result.Result$Success
#-dontwarn com.looker.core.common.result.Result
#-dontwarn com.looker.core.common.signature.Hash
#-dontwarn com.looker.core.common.signature.HashCheckerKt
#
#-dontwarn com.looker.core.datastore.Settings
#-dontwarn com.looker.core.datastore.SettingsRepository
#-dontwarn com.looker.core.datastore.di.DatastoreModule_ProvidePreferenceDatastoreFactory
#-dontwarn com.looker.core.datastore.di.DatastoreModule_ProvideProtoDatastoreFactory
#-dontwarn com.looker.core.datastore.di.DatastoreModule_ProvideSettingsExporterFactory
#-dontwarn com.looker.core.datastore.di.DatastoreModule_ProvideSettingsRepositoryFactory
#-dontwarn com.looker.core.datastore.extension.PreferencesKt
#-dontwarn com.looker.core.datastore.model.AutoSync
#-dontwarn com.looker.core.datastore.model.InstallerType$Companion
#-dontwarn com.looker.core.datastore.model.InstallerType
#-dontwarn com.looker.core.datastore.model.ProxyPreference
#-dontwarn com.looker.core.datastore.model.ProxyType
#-dontwarn com.looker.core.datastore.model.SortOrder
#-dontwarn com.looker.core.datastore.model.Theme
#
#-dontwarn com.looker.installer.InstallManager
#-dontwarn com.looker.installer.InstallModule_ProvideRootPermissionHandlerFactory
#-dontwarn com.looker.installer.InstallModule_ProvideShizukuPermissionHandlerFactory
#-dontwarn com.looker.installer.InstallModule_ProvidesInstallerFactory
#-dontwarn com.looker.installer.installers.root.RootPermissionHandler
#-dontwarn com.looker.installer.installers.session.SessionInstallerReceiver_GeneratedInjector
#-dontwarn com.looker.installer.installers.shizuku.ShizukuPermissionHandler$State
#-dontwarn com.looker.installer.installers.shizuku.ShizukuPermissionHandler
#-dontwarn com.looker.installer.model.InstallItem
#-dontwarn com.looker.installer.model.InstallItemKt
#-dontwarn com.looker.installer.model.InstallState
#-dontwarn com.looker.installer.notification.InstallNotificationKt

View File

@ -1,7 +1,6 @@
package com.looker.droidify.sync.common package com.looker.droidify.sync.common
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import java.io.File
import java.io.InputStream import java.io.InputStream
fun assets(name: String): InputStream { fun assets(name: String): InputStream {

View File

@ -50,7 +50,6 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.SHOW_APP_INFO" /> <action android:name="android.intent.action.SHOW_APP_INFO" />

View File

@ -13,10 +13,14 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.hilt.work.HiltWorkerFactory import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration import androidx.work.Configuration
import androidx.work.NetworkType import androidx.work.NetworkType
import coil.ImageLoader import coil3.ImageLoader
import coil.ImageLoaderFactory import coil3.PlatformContext
import coil.disk.DiskCache import coil3.SingletonImageLoader
import coil.memory.MemoryCache import coil3.asImage
import coil3.disk.DiskCache
import coil3.disk.directory
import coil3.memory.MemoryCache
import coil3.request.crossfade
import com.looker.droidify.content.ProductPreferences import com.looker.droidify.content.ProductPreferences
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.datastore.SettingsRepository import com.looker.droidify.datastore.SettingsRepository
@ -35,6 +39,7 @@ import com.looker.droidify.sync.toJobNetworkType
import com.looker.droidify.utility.common.Constants import com.looker.droidify.utility.common.Constants
import com.looker.droidify.utility.common.SdkCheck import com.looker.droidify.utility.common.SdkCheck
import com.looker.droidify.utility.common.cache.Cache import com.looker.droidify.utility.common.cache.Cache
import com.looker.droidify.utility.common.extension.getDrawableCompat
import com.looker.droidify.utility.common.extension.getInstalledPackagesCompat import com.looker.droidify.utility.common.extension.getInstalledPackagesCompat
import com.looker.droidify.utility.common.extension.jobScheduler import com.looker.droidify.utility.common.extension.jobScheduler
import com.looker.droidify.utility.common.log import com.looker.droidify.utility.common.log
@ -55,7 +60,7 @@ import kotlin.time.Duration.Companion.INFINITE
import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.hours
@HiltAndroidApp @HiltAndroidApp
class Droidify : Application(), ImageLoaderFactory, Configuration.Provider { class Droidify : Application(), SingletonImageLoader.Factory, Configuration.Provider {
private val parentJob = SupervisorJob() private val parentJob = SupervisorJob()
private val appScope = CoroutineScope(Dispatchers.Default + parentJob) private val appScope = CoroutineScope(Dispatchers.Default + parentJob)
@ -221,9 +226,14 @@ class Droidify : Application(), ImageLoaderFactory, Configuration.Provider {
override fun onReceive(context: Context, intent: Intent) = Unit override fun onReceive(context: Context, intent: Intent) = Unit
} }
override fun newImageLoader(): ImageLoader { override val workManagerConfiguration: Configuration
val memoryCache = MemoryCache.Builder(this) get() = Configuration.Builder()
.maxSizePercent(0.25) .setWorkerFactory(workerFactory)
.build()
override fun newImageLoader(context: PlatformContext): ImageLoader {
val memoryCache = MemoryCache.Builder()
.maxSizePercent(context, 0.25)
.build() .build()
val diskCache = DiskCache.Builder() val diskCache = DiskCache.Builder()
@ -234,15 +244,10 @@ class Droidify : Application(), ImageLoaderFactory, Configuration.Provider {
return ImageLoader.Builder(this) return ImageLoader.Builder(this)
.memoryCache(memoryCache) .memoryCache(memoryCache)
.diskCache(diskCache) .diskCache(diskCache)
.error(R.drawable.ic_cannot_load) .error(getDrawableCompat(R.drawable.ic_cannot_load).asImage())
.crossfade(350) .crossfade(350)
.build() .build()
} }
override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
} }
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)

View File

@ -2,9 +2,9 @@ package com.looker.droidify.content
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import com.looker.core.common.extension.Json import com.looker.droidify.utility.common.extension.Json
import com.looker.core.common.extension.parseDictionary import com.looker.droidify.utility.common.extension.parseDictionary
import com.looker.core.common.extension.writeDictionary import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.model.ProductPreference import com.looker.droidify.model.ProductPreference
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.utility.serialization.productPreference import com.looker.droidify.utility.serialization.productPreference

View File

@ -12,28 +12,29 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
sealed class Request { sealed class Request {
internal abstract val id: Int internal abstract val id: Int
data class ProductsAvailable( class Available(
val searchQuery: String, val searchQuery: String,
val section: ProductItem.Section, val section: ProductItem.Section,
val order: SortOrder val order: SortOrder,
) : Request() { ) : Request() {
override val id: Int override val id: Int
get() = 1 get() = 1
} }
data class ProductsInstalled( class Installed(
val searchQuery: String, val searchQuery: String,
val section: ProductItem.Section, val section: ProductItem.Section,
val order: SortOrder val order: SortOrder,
) : Request() { ) : Request() {
override val id: Int override val id: Int
get() = 2 get() = 2
} }
data class ProductsUpdates( class Updates(
val searchQuery: String, val searchQuery: String,
val section: ProductItem.Section, val section: ProductItem.Section,
val order: SortOrder val order: SortOrder,
val skipSignatureCheck: Boolean,
) : Request() { ) : Request() {
override val id: Int override val id: Int
get() = 3 get() = 3
@ -52,7 +53,7 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
private data class ActiveRequest( private data class ActiveRequest(
val request: Request, val request: Request,
val callback: Callback?, val callback: Callback?,
val cursor: Cursor? val cursor: Cursor?,
) )
init { init {
@ -93,7 +94,7 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
val request = activeRequests[id]!!.request val request = activeRequests[id]!!.request
return QueryLoader(requireContext()) { return QueryLoader(requireContext()) {
when (request) { when (request) {
is Request.ProductsAvailable -> is Request.Available ->
Database.ProductAdapter Database.ProductAdapter
.query( .query(
installed = false, installed = false,
@ -101,10 +102,10 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
searchQuery = request.searchQuery, searchQuery = request.searchQuery,
section = request.section, section = request.section,
order = request.order, order = request.order,
signal = it signal = it,
) )
is Request.ProductsInstalled -> is Request.Installed ->
Database.ProductAdapter Database.ProductAdapter
.query( .query(
installed = true, installed = true,
@ -112,10 +113,10 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
searchQuery = request.searchQuery, searchQuery = request.searchQuery,
section = request.section, section = request.section,
order = request.order, order = request.order,
signal = it signal = it,
) )
is Request.ProductsUpdates -> is Request.Updates ->
Database.ProductAdapter Database.ProductAdapter
.query( .query(
installed = true, installed = true,
@ -123,7 +124,8 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
searchQuery = request.searchQuery, searchQuery = request.searchQuery,
section = request.section, section = request.section,
order = request.order, order = request.order,
signal = it signal = it,
skipSignatureCheck = request.skipSignatureCheck,
) )
is Request.Repositories -> Database.RepositoryAdapter.query(it) is Request.Repositories -> Database.RepositoryAdapter.query(it)

View File

@ -9,18 +9,18 @@ import android.os.CancellationSignal
import androidx.core.database.sqlite.transaction import androidx.core.database.sqlite.transaction
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.looker.core.common.extension.Json import com.looker.droidify.BuildConfig
import com.looker.droidify.utility.common.extension.asSequence
import com.looker.droidify.utility.common.extension.firstOrNull
import com.looker.core.common.extension.parseDictionary
import com.looker.core.common.extension.writeDictionary
import com.looker.droidify.utility.common.log
import com.looker.droidify.datastore.model.SortOrder import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.model.InstalledItem import com.looker.droidify.model.InstalledItem
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.model.ProductItem import com.looker.droidify.model.ProductItem
import com.looker.droidify.model.Repository import com.looker.droidify.model.Repository
import com.looker.droidify.BuildConfig import com.looker.droidify.utility.common.extension.Json
import com.looker.droidify.utility.common.extension.asSequence
import com.looker.droidify.utility.common.extension.firstOrNull
import com.looker.droidify.utility.common.extension.parseDictionary
import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.utility.common.log
import com.looker.droidify.utility.serialization.product import com.looker.droidify.utility.serialization.product
import com.looker.droidify.utility.serialization.productItem import com.looker.droidify.utility.serialization.productItem
import com.looker.droidify.utility.serialization.repository import com.looker.droidify.utility.serialization.repository
@ -71,14 +71,20 @@ object Database {
get() = "$databasePrefix$innerName" get() = "$databasePrefix$innerName"
fun formatCreateTable(name: String): String { fun formatCreateTable(name: String): String {
return "CREATE TABLE $name (${QueryBuilder.trimQuery(createTable)})" return buildString(128) {
append("CREATE TABLE ")
append(name)
append(" (")
trimAndJoin(createTable)
append(")")
}
} }
val createIndexPairFormatted: Pair<String, String>? val createIndexPairFormatted: Pair<String, String>?
get() = createIndex?.let { get() = createIndex?.let {
Pair( Pair(
"CREATE INDEX ${innerName}_index ON $innerName ($it)", "CREATE INDEX ${innerName}_index ON $innerName ($it)",
"CREATE INDEX ${name}_index ON $innerName ($it)" "CREATE INDEX ${name}_index ON $innerName ($it)",
) )
} }
} }
@ -214,7 +220,7 @@ object Database {
Schema.Product, Schema.Product,
Schema.Category, Schema.Category,
Schema.Installed, Schema.Installed,
Schema.Lock Schema.Lock,
) )
dropOldTables(db, Schema.Repository, Schema.Product, Schema.Category) dropOldTables(db, Schema.Repository, Schema.Product, Schema.Category)
this.created = this.created || create this.created = this.created || create
@ -227,7 +233,7 @@ object Database {
val sql = db.query( val sql = db.query(
"${table.databasePrefix}sqlite_master", "${table.databasePrefix}sqlite_master",
columns = arrayOf("sql"), columns = arrayOf("sql"),
selection = Pair("type = ? AND name = ?", arrayOf("table", table.innerName)) selection = Pair("type = ? AND name = ?", arrayOf("table", table.innerName)),
).use { it.firstOrNull()?.getString(0) }.orEmpty() ).use { it.firstOrNull()?.getString(0) }.orEmpty()
table.formatCreateTable(table.innerName) != sql table.formatCreateTable(table.innerName) != sql
} }
@ -261,7 +267,7 @@ object Database {
val sqls = db.query( val sqls = db.query(
"${table.databasePrefix}sqlite_master", "${table.databasePrefix}sqlite_master",
columns = arrayOf("name", "sql"), columns = arrayOf("name", "sql"),
selection = Pair("type = ? AND tbl_name = ?", arrayOf("index", table.innerName)) selection = Pair("type = ? AND tbl_name = ?", arrayOf("index", table.innerName)),
) )
.use { cursor -> .use { cursor ->
cursor.asSequence() cursor.asSequence()
@ -289,7 +295,7 @@ object Database {
val tables = db.query( val tables = db.query(
"sqlite_master", "sqlite_master",
columns = arrayOf("name"), columns = arrayOf("name"),
selection = Pair("type = ?", arrayOf("table")) selection = Pair("type = ?", arrayOf("table")),
) )
.use { cursor -> cursor.asSequence().mapNotNull { it.getString(0) }.toList() } .use { cursor -> cursor.asSequence().mapNotNull { it.getString(0) }.toList() }
.filter { !it.startsWith("sqlite_") && !it.startsWith("android_") } .filter { !it.startsWith("sqlite_") && !it.startsWith("android_") }
@ -345,7 +351,7 @@ object Database {
private fun SQLiteDatabase.insertOrReplace( private fun SQLiteDatabase.insertOrReplace(
replace: Boolean, replace: Boolean,
table: String, table: String,
contentValues: ContentValues contentValues: ContentValues,
): Long { ): Long {
return if (replace) { return if (replace) {
replace(table, null, contentValues) replace(table, null, contentValues)
@ -353,7 +359,7 @@ object Database {
insert( insert(
table, table,
null, null,
contentValues contentValues,
) )
} }
} }
@ -363,7 +369,7 @@ object Database {
columns: Array<String>? = null, columns: Array<String>? = null,
selection: Pair<String, Array<String>>? = null, selection: Pair<String, Array<String>>? = null,
orderBy: String? = null, orderBy: String? = null,
signal: CancellationSignal? = null signal: CancellationSignal? = null,
): Cursor { ): Cursor {
return query( return query(
false, false,
@ -375,7 +381,7 @@ object Database {
null, null,
orderBy, orderBy,
null, null,
signal signal,
) )
} }
@ -397,7 +403,7 @@ object Database {
internal fun putWithoutNotification( internal fun putWithoutNotification(
repository: Repository, repository: Repository,
shouldReplace: Boolean, shouldReplace: Boolean,
database: SQLiteDatabase database: SQLiteDatabase,
): Long { ): Long {
return database.insertOrReplace( return database.insertOrReplace(
shouldReplace, shouldReplace,
@ -409,7 +415,7 @@ object Database {
put(Schema.Repository.ROW_ENABLED, if (repository.enabled) 1 else 0) put(Schema.Repository.ROW_ENABLED, if (repository.enabled) 1 else 0)
put(Schema.Repository.ROW_DELETED, 0) put(Schema.Repository.ROW_DELETED, 0)
put(Schema.Repository.ROW_DATA, jsonGenerate(repository::serialize)) put(Schema.Repository.ROW_DATA, jsonGenerate(repository::serialize))
} },
) )
} }
@ -442,8 +448,8 @@ object Database {
Schema.Repository.name, Schema.Repository.name,
selection = Pair( selection = Pair(
"${Schema.Repository.ROW_ID} = ? AND ${Schema.Repository.ROW_DELETED} == 0", "${Schema.Repository.ROW_ID} = ? AND ${Schema.Repository.ROW_DELETED} == 0",
arrayOf(id.toString()) arrayOf(id.toString()),
) ),
).use { it.firstOrNull()?.let(::transform) } ).use { it.firstOrNull()?.let(::transform) }
} }
@ -463,9 +469,9 @@ object Database {
selection = Pair( selection = Pair(
"${Schema.Repository.ROW_ENABLED} != 0 AND " + "${Schema.Repository.ROW_ENABLED} != 0 AND " +
"${Schema.Repository.ROW_DELETED} == 0", "${Schema.Repository.ROW_DELETED} == 0",
emptyArray() emptyArray(),
), ),
signal = null signal = null,
).use { it.asSequence().map(::transform).toList() } ).use { it.asSequence().map(::transform).toList() }
} }
@ -473,7 +479,7 @@ object Database {
return db.query( return db.query(
Schema.Repository.name, Schema.Repository.name,
selection = Pair("${Schema.Repository.ROW_DELETED} == 0", emptyArray()), selection = Pair("${Schema.Repository.ROW_DELETED} == 0", emptyArray()),
signal = null signal = null,
).use { it.asSequence().map(::transform).toList() } ).use { it.asSequence().map(::transform).toList() }
} }
@ -489,9 +495,9 @@ object Database {
selection = Pair( selection = Pair(
"${Schema.Repository.ROW_ENABLED} == 0 OR " + "${Schema.Repository.ROW_ENABLED} == 0 OR " +
"${Schema.Repository.ROW_DELETED} != 0", "${Schema.Repository.ROW_DELETED} != 0",
emptyArray() emptyArray(),
), ),
signal = null signal = null,
).use { parentCursor -> ).use { parentCursor ->
parentCursor.asSequence().associate { parentCursor.asSequence().associate {
val idIndex = it.getColumnIndexOrThrow(Schema.Repository.ROW_ID) val idIndex = it.getColumnIndexOrThrow(Schema.Repository.ROW_ID)
@ -508,7 +514,7 @@ object Database {
put(Schema.Repository.ROW_DELETED, 1) put(Schema.Repository.ROW_DELETED, 1)
}, },
"${Schema.Repository.ROW_ID} = ?", "${Schema.Repository.ROW_ID} = ?",
arrayOf(id.toString()) arrayOf(id.toString()),
) )
notifyChanged(Subject.Repositories, Subject.Repository(id), Subject.Products) notifyChanged(Subject.Repositories, Subject.Repository(id), Subject.Products)
} }
@ -519,18 +525,18 @@ object Database {
val productsCount = db.delete( val productsCount = db.delete(
Schema.Product.name, Schema.Product.name,
"${Schema.Product.ROW_REPOSITORY_ID} IN ($idsString)", "${Schema.Product.ROW_REPOSITORY_ID} IN ($idsString)",
null null,
) )
val categoriesCount = db.delete( val categoriesCount = db.delete(
Schema.Category.name, Schema.Category.name,
"${Schema.Category.ROW_REPOSITORY_ID} IN ($idsString)", "${Schema.Category.ROW_REPOSITORY_ID} IN ($idsString)",
null null,
) )
if (isDeleted) { if (isDeleted) {
db.delete( db.delete(
Schema.Repository.name, Schema.Repository.name,
"${Schema.Repository.ROW_ID} IN ($id)", "${Schema.Repository.ROW_ID} IN ($id)",
null null,
) )
} }
productsCount != 0 || categoriesCount != 0 productsCount != 0 || categoriesCount != 0
@ -555,7 +561,7 @@ object Database {
Schema.Repository.name, Schema.Repository.name,
selection = Pair("${Schema.Repository.ROW_DELETED} == 0", emptyArray()), selection = Pair("${Schema.Repository.ROW_DELETED} == 0", emptyArray()),
orderBy = "${Schema.Repository.ROW_ENABLED} DESC", orderBy = "${Schema.Repository.ROW_ENABLED} DESC",
signal = signal signal = signal,
).observable(Subject.Repositories) ).observable(Subject.Repositories)
} }
@ -577,26 +583,28 @@ object Database {
.map { get(packageName, null) } .map { get(packageName, null) }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
suspend fun getUpdates(): List<ProductItem> = withContext(Dispatchers.IO) { suspend fun getUpdates(skipSignatureCheck: Boolean): List<ProductItem> =
query( withContext(Dispatchers.IO) {
installed = true, query(
updates = true, installed = true,
searchQuery = "", updates = true,
section = ProductItem.Section.All, searchQuery = "",
order = SortOrder.NAME, skipSignatureCheck = skipSignatureCheck,
signal = null section = ProductItem.Section.All,
).use { order = SortOrder.NAME,
it.asSequence() signal = null,
.map(ProductAdapter::transformItem) ).use {
.toList() it.asSequence()
.map(ProductAdapter::transformItem)
.toList()
}
} }
}
fun getUpdatesStream(): Flow<List<ProductItem>> = flowOf(Unit) fun getUpdatesStream(skipSignatureCheck: Boolean): Flow<List<ProductItem>> = flowOf(Unit)
.onCompletion { if (it == null) emitAll(flowCollection(Subject.Products)) } .onCompletion { if (it == null) emitAll(flowCollection(Subject.Products)) }
// Crashes due to immediate retrieval of data? // Crashes due to immediate retrieval of data?
.onEach { delay(50) } .onEach { delay(50) }
.map { getUpdates() } .map { getUpdates(skipSignatureCheck) }
.flowOn(Dispatchers.IO) .flowOn(Dispatchers.IO)
fun getAll(): List<Product> { fun getAll(): List<Product> {
@ -618,10 +626,10 @@ object Database {
columns = arrayOf( columns = arrayOf(
Schema.Product.ROW_REPOSITORY_ID, Schema.Product.ROW_REPOSITORY_ID,
Schema.Product.ROW_DESCRIPTION, Schema.Product.ROW_DESCRIPTION,
Schema.Product.ROW_DATA Schema.Product.ROW_DATA,
), ),
selection = Pair("${Schema.Product.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)), selection = Pair("${Schema.Product.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)),
signal = signal signal = signal,
).use { it.asSequence().map(::transform).toList() } ).use { it.asSequence().map(::transform).toList() }
} }
@ -636,24 +644,26 @@ object Database {
columns = arrayOf("COUNT (*)"), columns = arrayOf("COUNT (*)"),
selection = Pair( selection = Pair(
"${Schema.Product.ROW_REPOSITORY_ID} = ?", "${Schema.Product.ROW_REPOSITORY_ID} = ?",
arrayOf(repositoryId.toString()) arrayOf(repositoryId.toString()),
) ),
).use { it.firstOrNull()?.getInt(0) ?: 0 } ).use { it.firstOrNull()?.getInt(0) ?: 0 }
} }
fun query( fun query(
installed: Boolean, installed: Boolean,
updates: Boolean, updates: Boolean,
skipSignatureCheck: Boolean = false,
searchQuery: String, searchQuery: String,
section: ProductItem.Section, section: ProductItem.Section,
order: SortOrder, order: SortOrder,
signal: CancellationSignal? signal: CancellationSignal?,
): Cursor { ): Cursor {
val builder = QueryBuilder() val builder = QueryBuilder()
val signatureMatches = """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND val signatureMatches = if (skipSignatureCheck) "1"
product.${Schema.Product.ROW_SIGNATURES} LIKE ('%.' || installed.${Schema.Installed.ROW_SIGNATURE} || '.%') AND else """installed.${Schema.Installed.ROW_SIGNATURE} IS NOT NULL AND
product.${Schema.Product.ROW_SIGNATURES} != ''""" product.${Schema.Product.ROW_SIGNATURES} LIKE ('%.' || installed.${Schema.Installed.ROW_SIGNATURE} || '.%') AND
product.${Schema.Product.ROW_SIGNATURES} != ''"""
builder += """SELECT product.rowid AS _id, product.${Schema.Product.ROW_REPOSITORY_ID}, builder += """SELECT product.rowid AS _id, product.${Schema.Product.ROW_REPOSITORY_ID},
product.${Schema.Product.ROW_PACKAGE_NAME}, product.${Schema.Product.ROW_NAME}, product.${Schema.Product.ROW_PACKAGE_NAME}, product.${Schema.Product.ROW_NAME},
@ -741,6 +751,10 @@ object Database {
} }
} }
fun transformPackageName(cursor: Cursor): String {
return cursor.getString(cursor.getColumnIndexOrThrow(Schema.Product.ROW_PACKAGE_NAME))
}
fun transformItem(cursor: Cursor): ProductItem { fun transformItem(cursor: Cursor): ProductItem {
return cursor.getBlob(cursor.getColumnIndexOrThrow(Schema.Product.ROW_DATA_ITEM)) return cursor.getBlob(cursor.getColumnIndexOrThrow(Schema.Product.ROW_DATA_ITEM))
.jsonParse { .jsonParse {
@ -806,10 +820,10 @@ object Database {
Schema.Installed.ROW_PACKAGE_NAME, Schema.Installed.ROW_PACKAGE_NAME,
Schema.Installed.ROW_VERSION, Schema.Installed.ROW_VERSION,
Schema.Installed.ROW_VERSION_CODE, Schema.Installed.ROW_VERSION_CODE,
Schema.Installed.ROW_SIGNATURE Schema.Installed.ROW_SIGNATURE,
), ),
selection = Pair("${Schema.Installed.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)), selection = Pair("${Schema.Installed.ROW_PACKAGE_NAME} = ?", arrayOf(packageName)),
signal = signal signal = signal,
).use { it.firstOrNull()?.let(::transform) } ).use { it.firstOrNull()?.let(::transform) }
} }
@ -822,7 +836,7 @@ object Database {
put(Schema.Installed.ROW_VERSION, installedItem.version) put(Schema.Installed.ROW_VERSION, installedItem.version)
put(Schema.Installed.ROW_VERSION_CODE, installedItem.versionCode) put(Schema.Installed.ROW_VERSION_CODE, installedItem.versionCode)
put(Schema.Installed.ROW_SIGNATURE, installedItem.signature) put(Schema.Installed.ROW_SIGNATURE, installedItem.signature)
} },
) )
if (notify) { if (notify) {
notifyChanged(Subject.Products) notifyChanged(Subject.Products)
@ -842,7 +856,7 @@ object Database {
val count = db.delete( val count = db.delete(
Schema.Installed.name, Schema.Installed.name,
"${Schema.Installed.ROW_PACKAGE_NAME} = ?", "${Schema.Installed.ROW_PACKAGE_NAME} = ?",
arrayOf(packageName) arrayOf(packageName),
) )
if (count > 0) { if (count > 0) {
notifyChanged(Subject.Products) notifyChanged(Subject.Products)
@ -854,7 +868,7 @@ object Database {
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_PACKAGE_NAME)), cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_PACKAGE_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_VERSION)), cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_VERSION)),
cursor.getLong(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_VERSION_CODE)), cursor.getLong(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_VERSION_CODE)),
cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_SIGNATURE)) cursor.getString(cursor.getColumnIndexOrThrow(Schema.Installed.ROW_SIGNATURE)),
) )
} }
} }
@ -867,7 +881,7 @@ object Database {
ContentValues().apply { ContentValues().apply {
put(Schema.Lock.ROW_PACKAGE_NAME, lock.first) put(Schema.Lock.ROW_PACKAGE_NAME, lock.first)
put(Schema.Lock.ROW_VERSION_CODE, lock.second) put(Schema.Lock.ROW_VERSION_CODE, lock.second)
} },
) )
if (notify) { if (notify) {
notifyChanged(Subject.Products) notifyChanged(Subject.Products)
@ -923,9 +937,9 @@ object Database {
put(Schema.Product.ROW_DATA, jsonGenerate(product::serialize)) put(Schema.Product.ROW_DATA, jsonGenerate(product::serialize))
put( put(
Schema.Product.ROW_DATA_ITEM, Schema.Product.ROW_DATA_ITEM,
jsonGenerate(product.item()::serialize) jsonGenerate(product.item()::serialize),
) )
} },
) )
for (category in product.categories) { for (category in product.categories) {
db.insertOrReplace( db.insertOrReplace(
@ -935,7 +949,7 @@ object Database {
put(Schema.Category.ROW_REPOSITORY_ID, product.repositoryId) put(Schema.Category.ROW_REPOSITORY_ID, product.repositoryId)
put(Schema.Category.ROW_PACKAGE_NAME, product.packageName) put(Schema.Category.ROW_PACKAGE_NAME, product.packageName)
put(Schema.Category.ROW_NAME, category) put(Schema.Category.ROW_NAME, category)
} },
) )
} }
} }
@ -948,20 +962,20 @@ object Database {
db.delete( db.delete(
Schema.Product.name, Schema.Product.name,
"${Schema.Product.ROW_REPOSITORY_ID} = ?", "${Schema.Product.ROW_REPOSITORY_ID} = ?",
arrayOf(repository.id.toString()) arrayOf(repository.id.toString()),
) )
db.delete( db.delete(
Schema.Category.name, Schema.Category.name,
"${Schema.Category.ROW_REPOSITORY_ID} = ?", "${Schema.Category.ROW_REPOSITORY_ID} = ?",
arrayOf(repository.id.toString()) arrayOf(repository.id.toString()),
) )
db.execSQL( db.execSQL(
"INSERT INTO ${Schema.Product.name} SELECT * " + "INSERT INTO ${Schema.Product.name} SELECT * " +
"FROM ${Schema.Product.temporaryName}" "FROM ${Schema.Product.temporaryName}",
) )
db.execSQL( db.execSQL(
"INSERT INTO ${Schema.Category.name} SELECT * " + "INSERT INTO ${Schema.Category.name} SELECT * " +
"FROM ${Schema.Category.temporaryName}" "FROM ${Schema.Category.temporaryName}",
) )
RepositoryAdapter.putWithoutNotification(repository, true, db) RepositoryAdapter.putWithoutNotification(repository, true, db)
db.execSQL("DROP TABLE IF EXISTS ${Schema.Product.temporaryName}") db.execSQL("DROP TABLE IF EXISTS ${Schema.Product.temporaryName}")
@ -970,7 +984,7 @@ object Database {
notifyChanged( notifyChanged(
Subject.Repositories, Subject.Repositories,
Subject.Repository(repository.id), Subject.Repository(repository.id),
Subject.Products Subject.Products,
) )
} else { } else {
db.execSQL("DROP TABLE IF EXISTS ${Schema.Product.temporaryName}") db.execSQL("DROP TABLE IF EXISTS ${Schema.Product.temporaryName}")

View File

@ -3,26 +3,20 @@ package com.looker.droidify.database
import android.database.Cursor import android.database.Cursor
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.os.CancellationSignal import android.os.CancellationSignal
import com.looker.droidify.BuildConfig
import com.looker.droidify.utility.common.extension.asSequence import com.looker.droidify.utility.common.extension.asSequence
import com.looker.droidify.utility.common.log import com.looker.droidify.utility.common.log
import com.looker.droidify.BuildConfig
class QueryBuilder { class QueryBuilder {
companion object {
fun trimQuery(query: String): String {
return query.lines().map { it.trim() }.filter { it.isNotEmpty() }
.joinToString(separator = " ")
}
}
private val builder = StringBuilder() private val builder = StringBuilder(256)
private val arguments = mutableListOf<String>() private val arguments = mutableListOf<String>()
operator fun plusAssign(query: String) { operator fun plusAssign(query: String) {
if (builder.isNotEmpty()) { if (builder.isNotEmpty()) {
builder.append(" ") builder.append(" ")
} }
builder.append(trimQuery(query)) builder.trimAndJoin(query)
} }
operator fun remAssign(argument: String) { operator fun remAssign(argument: String) {
@ -48,3 +42,53 @@ class QueryBuilder {
return db.rawQuery(query, arguments, signal) return db.rawQuery(query, arguments, signal)
} }
} }
fun StringBuilder.trimAndJoin(
input: String,
) {
var isFirstLine = true
var startOfLine = 0
for (i in input.indices) {
val char = input[i]
when {
char == '\n' -> {
trimAndAppendLine(input, startOfLine, i, this, isFirstLine)
isFirstLine = false
startOfLine = i + 1
}
else -> {
if (i == input.lastIndex) {
trimAndAppendLine(input, startOfLine, i + 1, this, isFirstLine)
}
}
}
}
}
private fun trimAndAppendLine(
input: String,
start: Int,
end: Int,
builder: StringBuilder,
isFirstLine: Boolean,
) {
var lineStart = start
var lineEnd = end - 1
while (lineStart <= lineEnd && input[lineStart].isWhitespace()) {
lineStart++
}
while (lineEnd >= lineStart && input[lineEnd].isWhitespace()) {
lineEnd--
}
if (lineStart <= lineEnd) {
if (!isFirstLine) {
builder.append(' ')
}
builder.append(input, lineStart, lineEnd + 1)
}
}

View File

@ -4,12 +4,12 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.core.JsonToken
import com.looker.droidify.utility.common.Exporter import com.looker.droidify.utility.common.Exporter
import com.looker.core.common.extension.Json import com.looker.droidify.utility.common.extension.Json
import com.looker.core.common.extension.forEach import com.looker.droidify.utility.common.extension.forEach
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.core.common.extension.parseDictionary import com.looker.droidify.utility.common.extension.parseDictionary
import com.looker.core.common.extension.writeArray import com.looker.droidify.utility.common.extension.writeArray
import com.looker.core.common.extension.writeDictionary import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.di.ApplicationScope import com.looker.droidify.di.ApplicationScope
import com.looker.droidify.di.IoDispatcher import com.looker.droidify.di.IoDispatcher
import com.looker.droidify.model.Repository import com.looker.droidify.model.Repository

View File

@ -14,6 +14,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey import androidx.datastore.preferences.core.stringSetPreferencesKey
import com.looker.droidify.datastore.model.AutoSync import com.looker.droidify.datastore.model.AutoSync
import com.looker.droidify.datastore.model.InstallerType import com.looker.droidify.datastore.model.InstallerType
import com.looker.droidify.datastore.model.LegacyInstallerComponent
import com.looker.droidify.datastore.model.ProxyPreference import com.looker.droidify.datastore.model.ProxyPreference
import com.looker.droidify.datastore.model.ProxyType import com.looker.droidify.datastore.model.ProxyType
import com.looker.droidify.datastore.model.SortOrder import com.looker.droidify.datastore.model.SortOrder
@ -85,6 +86,31 @@ class PreferenceSettingsRepository(
override suspend fun setInstallerType(installerType: InstallerType) = override suspend fun setInstallerType(installerType: InstallerType) =
INSTALLER_TYPE.update(installerType.name) INSTALLER_TYPE.update(installerType.name)
override suspend fun setLegacyInstallerComponent(component: LegacyInstallerComponent?) {
when (component) {
null -> {
LEGACY_INSTALLER_COMPONENT_TYPE.update("")
LEGACY_INSTALLER_COMPONENT_CLASS.update("")
LEGACY_INSTALLER_COMPONENT_ACTIVITY.update("")
}
is LegacyInstallerComponent.Component -> {
LEGACY_INSTALLER_COMPONENT_TYPE.update("component")
LEGACY_INSTALLER_COMPONENT_CLASS.update(component.clazz)
LEGACY_INSTALLER_COMPONENT_ACTIVITY.update(component.activity)
}
LegacyInstallerComponent.Unspecified -> {
LEGACY_INSTALLER_COMPONENT_TYPE.update("unspecified")
LEGACY_INSTALLER_COMPONENT_CLASS.update("")
LEGACY_INSTALLER_COMPONENT_ACTIVITY.update("")
}
LegacyInstallerComponent.AlwaysChoose -> {
LEGACY_INSTALLER_COMPONENT_TYPE.update("always_choose")
LEGACY_INSTALLER_COMPONENT_CLASS.update("")
LEGACY_INSTALLER_COMPONENT_ACTIVITY.update("")
}
}
}
override suspend fun setAutoUpdate(allow: Boolean) = override suspend fun setAutoUpdate(allow: Boolean) =
AUTO_UPDATE.update(allow) AUTO_UPDATE.update(allow)
@ -125,6 +151,18 @@ class PreferenceSettingsRepository(
private fun mapSettings(preferences: Preferences): Settings { private fun mapSettings(preferences: Preferences): Settings {
val installerType = val installerType =
InstallerType.valueOf(preferences[INSTALLER_TYPE] ?: InstallerType.Default.name) InstallerType.valueOf(preferences[INSTALLER_TYPE] ?: InstallerType.Default.name)
val legacyInstallerComponent = when (preferences[LEGACY_INSTALLER_COMPONENT_TYPE]) {
"component" -> {
preferences[LEGACY_INSTALLER_COMPONENT_CLASS]?.takeIf { it.isNotBlank() }?.let { cls ->
preferences[LEGACY_INSTALLER_COMPONENT_ACTIVITY]?.takeIf { it.isNotBlank() }?.let { act ->
LegacyInstallerComponent.Component(cls, act)
}
}
}
"unspecified" -> LegacyInstallerComponent.Unspecified
"always_choose" -> LegacyInstallerComponent.AlwaysChoose
else -> null
}
val language = preferences[LANGUAGE] ?: "system" val language = preferences[LANGUAGE] ?: "system"
val incompatibleVersions = preferences[INCOMPATIBLE_VERSIONS] ?: false val incompatibleVersions = preferences[INCOMPATIBLE_VERSIONS] ?: false
@ -154,6 +192,7 @@ class PreferenceSettingsRepository(
theme = theme, theme = theme,
dynamicTheme = dynamicTheme, dynamicTheme = dynamicTheme,
installerType = installerType, installerType = installerType,
legacyInstallerComponent = legacyInstallerComponent,
autoUpdate = autoUpdate, autoUpdate = autoUpdate,
autoSync = autoSync, autoSync = autoSync,
sortOrder = sortOrder, sortOrder = sortOrder,
@ -185,6 +224,9 @@ class PreferenceSettingsRepository(
val LAST_CLEAN_UP = longPreferencesKey("key_last_clean_up_time") val LAST_CLEAN_UP = longPreferencesKey("key_last_clean_up_time")
val FAVOURITE_APPS = stringSetPreferencesKey("key_favourite_apps") val FAVOURITE_APPS = stringSetPreferencesKey("key_favourite_apps")
val HOME_SCREEN_SWIPING = booleanPreferencesKey("key_home_swiping") val HOME_SCREEN_SWIPING = booleanPreferencesKey("key_home_swiping")
val LEGACY_INSTALLER_COMPONENT_CLASS = stringPreferencesKey("key_legacy_installer_component_class")
val LEGACY_INSTALLER_COMPONENT_ACTIVITY = stringPreferencesKey("key_legacy_installer_component_activity")
val LEGACY_INSTALLER_COMPONENT_TYPE = stringPreferencesKey("key_legacy_installer_component_type")
// Enums // Enums
val THEME = stringPreferencesKey("key_theme") val THEME = stringPreferencesKey("key_theme")
@ -200,6 +242,28 @@ class PreferenceSettingsRepository(
set(UNSTABLE_UPDATES, settings.unstableUpdate) set(UNSTABLE_UPDATES, settings.unstableUpdate)
set(THEME, settings.theme.name) set(THEME, settings.theme.name)
set(DYNAMIC_THEME, settings.dynamicTheme) set(DYNAMIC_THEME, settings.dynamicTheme)
when (settings.legacyInstallerComponent) {
is LegacyInstallerComponent.Component -> {
set(LEGACY_INSTALLER_COMPONENT_TYPE, "component")
set(LEGACY_INSTALLER_COMPONENT_CLASS, settings.legacyInstallerComponent.clazz)
set(LEGACY_INSTALLER_COMPONENT_ACTIVITY, settings.legacyInstallerComponent.activity)
}
LegacyInstallerComponent.Unspecified -> {
set(LEGACY_INSTALLER_COMPONENT_TYPE, "unspecified")
set(LEGACY_INSTALLER_COMPONENT_CLASS, "")
set(LEGACY_INSTALLER_COMPONENT_ACTIVITY, "")
}
LegacyInstallerComponent.AlwaysChoose -> {
set(LEGACY_INSTALLER_COMPONENT_TYPE, "always_choose")
set(LEGACY_INSTALLER_COMPONENT_CLASS, "")
set(LEGACY_INSTALLER_COMPONENT_ACTIVITY, "")
}
null -> {
set(LEGACY_INSTALLER_COMPONENT_TYPE, "")
set(LEGACY_INSTALLER_COMPONENT_CLASS, "")
set(LEGACY_INSTALLER_COMPONENT_ACTIVITY, "")
}
}
set(INSTALLER_TYPE, settings.installerType.name) set(INSTALLER_TYPE, settings.installerType.name)
set(AUTO_UPDATE, settings.autoUpdate) set(AUTO_UPDATE, settings.autoUpdate)
set(AUTO_SYNC, settings.autoSync.name) set(AUTO_SYNC, settings.autoSync.name)

View File

@ -3,6 +3,7 @@ package com.looker.droidify.datastore
import androidx.datastore.core.Serializer import androidx.datastore.core.Serializer
import com.looker.droidify.datastore.model.AutoSync import com.looker.droidify.datastore.model.AutoSync
import com.looker.droidify.datastore.model.InstallerType import com.looker.droidify.datastore.model.InstallerType
import com.looker.droidify.datastore.model.LegacyInstallerComponent
import com.looker.droidify.datastore.model.ProxyPreference import com.looker.droidify.datastore.model.ProxyPreference
import com.looker.droidify.datastore.model.SortOrder import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.datastore.model.Theme import com.looker.droidify.datastore.model.Theme
@ -29,6 +30,7 @@ data class Settings(
val theme: Theme = Theme.SYSTEM, val theme: Theme = Theme.SYSTEM,
val dynamicTheme: Boolean = false, val dynamicTheme: Boolean = false,
val installerType: InstallerType = InstallerType.Default, val installerType: InstallerType = InstallerType.Default,
val legacyInstallerComponent: LegacyInstallerComponent? = null,
val autoUpdate: Boolean = false, val autoUpdate: Boolean = false,
val autoSync: AutoSync = AutoSync.WIFI_ONLY, val autoSync: AutoSync = AutoSync.WIFI_ONLY,
val sortOrder: SortOrder = SortOrder.UPDATED, val sortOrder: SortOrder = SortOrder.UPDATED,

View File

@ -3,6 +3,7 @@ package com.looker.droidify.datastore
import android.net.Uri import android.net.Uri
import com.looker.droidify.datastore.model.AutoSync import com.looker.droidify.datastore.model.AutoSync
import com.looker.droidify.datastore.model.InstallerType import com.looker.droidify.datastore.model.InstallerType
import com.looker.droidify.datastore.model.LegacyInstallerComponent
import com.looker.droidify.datastore.model.ProxyType import com.looker.droidify.datastore.model.ProxyType
import com.looker.droidify.datastore.model.SortOrder import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.datastore.model.Theme import com.looker.droidify.datastore.model.Theme
@ -37,6 +38,8 @@ interface SettingsRepository {
suspend fun setInstallerType(installerType: InstallerType) suspend fun setInstallerType(installerType: InstallerType)
suspend fun setLegacyInstallerComponent(component: LegacyInstallerComponent?)
suspend fun setAutoUpdate(allow: Boolean) suspend fun setAutoUpdate(allow: Boolean)
suspend fun setAutoSync(autoSync: AutoSync) suspend fun setAutoSync(autoSync: AutoSync)

View File

@ -0,0 +1,26 @@
package com.looker.droidify.datastore.model
import kotlinx.serialization.Serializable
@Serializable
sealed class LegacyInstallerComponent {
@Serializable
object Unspecified : LegacyInstallerComponent()
@Serializable
object AlwaysChoose : LegacyInstallerComponent()
@Serializable
data class Component(
val clazz: String,
val activity: String,
) : LegacyInstallerComponent() {
fun update(
newClazz: String? = null,
newActivity: String? = null,
): Component = copy(
clazz = newClazz ?: clazz,
activity = newActivity ?: activity
)
}
}

View File

@ -1,5 +1,3 @@
@file:Suppress("unused")
package com.looker.droidify.di package com.looker.droidify.di
import dagger.Module import dagger.Module

View File

@ -22,9 +22,6 @@ data class Repo(
} }
} }
val String.isOnion: Boolean
get() = endsWith(".onion")
data class AntiFeature( data class AntiFeature(
val id: Long, val id: Long,
val name: String, val name: String,

View File

@ -51,7 +51,6 @@ open class DrawableWrapper(val drawable: Drawable) : Drawable() {
drawable.colorFilter = colorFilter drawable.colorFilter = colorFilter
} }
@Deprecated("Deprecated in Java")
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
override fun getOpacity(): Int = drawable.opacity override fun getOpacity(): Int = drawable.opacity
} }

View File

@ -3,10 +3,10 @@ package com.looker.droidify.index
import android.content.ContentValues import android.content.ContentValues
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.core.JsonToken
import com.looker.core.common.extension.Json import com.looker.droidify.utility.common.extension.Json
import com.looker.droidify.utility.common.extension.asSequence import com.looker.droidify.utility.common.extension.asSequence
import com.looker.core.common.extension.collectNotNull import com.looker.droidify.utility.common.extension.collectNotNull
import com.looker.core.common.extension.writeDictionary import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.model.Release import com.looker.droidify.model.Release
import com.looker.droidify.utility.serialization.product import com.looker.droidify.utility.serialization.product
@ -82,9 +82,9 @@ class IndexMerger(file: File) : Closeable {
closeTransaction() closeTransaction()
db.rawQuery( db.rawQuery(
"""SELECT product.description, product.data AS pd, releases.data AS rd FROM product """SELECT product.description, product.data AS pd, releases.data AS rd FROM product
LEFT JOIN releases ON product.package_name = releases.package_name""", LEFT JOIN releases ON product.package_name = releases.package_name""",
null null
)?.use { cursor -> ).use { cursor ->
cursor.asSequence().map { currentCursor -> cursor.asSequence().map { currentCursor ->
val description = currentCursor.getString(0) val description = currentCursor.getString(0)
val product = Json.factory.createParser(currentCursor.getBlob(1)).use { val product = Json.factory.createParser(currentCursor.getBlob(1)).use {

View File

@ -5,16 +5,27 @@ import androidx.core.os.ConfigurationCompat.getLocales
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.core.JsonToken
import com.looker.droidify.utility.common.SdkCheck import com.looker.droidify.utility.common.extension.Json
import com.looker.core.common.extension.Json import com.looker.droidify.utility.common.extension.collectDistinctNotEmptyStrings
import com.looker.core.common.extension.collectDistinctNotEmptyStrings import com.looker.droidify.utility.common.extension.collectNotNull
import com.looker.core.common.extension.collectNotNull import com.looker.droidify.utility.common.extension.forEach
import com.looker.core.common.extension.forEach import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.illegal
import com.looker.core.common.extension.illegal
import com.looker.droidify.utility.common.nullIfEmpty
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.model.Product.Donate.Bitcoin
import com.looker.droidify.model.Product.Donate.Liberapay
import com.looker.droidify.model.Product.Donate.Litecoin
import com.looker.droidify.model.Product.Donate.OpenCollective
import com.looker.droidify.model.Product.Donate.Regular
import com.looker.droidify.model.Product.Screenshot.Type.LARGE_TABLET
import com.looker.droidify.model.Product.Screenshot.Type.PHONE
import com.looker.droidify.model.Product.Screenshot.Type.SMALL_TABLET
import com.looker.droidify.model.Product.Screenshot.Type.TV
import com.looker.droidify.model.Product.Screenshot.Type.VIDEO
import com.looker.droidify.model.Product.Screenshot.Type.WEAR
import com.looker.droidify.model.Release import com.looker.droidify.model.Release
import com.looker.droidify.utility.common.SdkCheck
import com.looker.droidify.utility.common.nullIfEmpty
import java.io.InputStream import java.io.InputStream
object IndexV1Parser { object IndexV1Parser {
@ -32,9 +43,12 @@ object IndexV1Parser {
} }
private class Screenshots( private class Screenshots(
val video: List<String>,
val phone: List<String>, val phone: List<String>,
val smallTablet: List<String>, val smallTablet: List<String>,
val largeTablet: List<String> val largeTablet: List<String>,
val wear: List<String>,
val tv: List<String>,
) )
private class Localized( private class Localized(
@ -90,10 +104,9 @@ object IndexV1Parser {
} }
private fun <T> Map<String, Localized>.find(callback: (String, Localized) -> T?): T? { private fun <T> Map<String, Localized>.find(callback: (String, Localized) -> T?): T? {
return getAndCall("en-US", callback) ?: getAndCall("en_US", callback) ?: getAndCall( return getAndCall("en-US", callback)
"en", ?: getAndCall("en_US", callback)
callback ?: getAndCall("en", callback)
)
} }
private fun <T> Map<String, Localized>.findLocalized(callback: (Localized) -> T?): T? { private fun <T> Map<String, Localized>.findLocalized(callback: (Localized) -> T?): T? {
@ -122,12 +135,11 @@ object IndexV1Parser {
internal object DonateComparator : Comparator<Product.Donate> { internal object DonateComparator : Comparator<Product.Donate> {
private val classes = listOf( private val classes = listOf(
Product.Donate.Regular::class, Regular::class,
Product.Donate.Bitcoin::class, Bitcoin::class,
Product.Donate.Litecoin::class, Litecoin::class,
Product.Donate.Flattr::class, Liberapay::class,
Product.Donate.Liberapay::class, OpenCollective::class
Product.Donate.OpenCollective::class
) )
override fun compare(donate1: Product.Donate, donate2: Product.Donate): Int { override fun compare(donate1: Product.Donate, donate2: Product.Donate): Int {
@ -236,14 +248,17 @@ object IndexV1Parser {
private const val KEY_PRODUCT_LICENSE = "license" private const val KEY_PRODUCT_LICENSE = "license"
private const val KEY_PRODUCT_DONATE = "donate" private const val KEY_PRODUCT_DONATE = "donate"
private const val KEY_PRODUCT_BITCOIN = "bitcoin" private const val KEY_PRODUCT_BITCOIN = "bitcoin"
private const val KEY_PRODUCT_FLATTRID = "flattrID" private const val KEY_PRODUCT_LIBERAPAYID = "liberapay"
private const val KEY_PRODUCT_LIBERAPAYID = "liberapayID" private const val KEY_PRODUCT_LITECOIN = "litecoin"
private const val KEY_PRODUCT_OPENCOLLECTIVE = "openCollective" private const val KEY_PRODUCT_OPENCOLLECTIVE = "openCollective"
private const val KEY_PRODUCT_LOCALIZED = "localized" private const val KEY_PRODUCT_LOCALIZED = "localized"
private const val KEY_PRODUCT_WHATSNEW = "whatsNew" private const val KEY_PRODUCT_WHATSNEW = "whatsNew"
private const val KEY_PRODUCT_PHONESCREENSHOTS = "phoneScreenshots" private const val KEY_PRODUCT_PHONE_SCREENSHOTS = "phoneScreenshots"
private const val KEY_PRODUCT_SEVENINCHSCREENSHOTS = "sevenInchScreenshots" private const val KEY_PRODUCT_SEVEN_INCH_SCREENSHOTS = "sevenInchScreenshots"
private const val KEY_PRODUCT_TENINCHSCREENSHOTS = "tenInchScreenshots" private const val KEY_PRODUCT_TEN_INCH_SCREENSHOTS = "tenInchScreenshots"
private const val KEY_PRODUCT_WEAR_SCREENSHOTS = "wearScreenshots"
private const val KEY_PRODUCT_TV_SCREENSHOTS = "tvScreenshots"
private const val KEY_PRODUCT_VIDEO = "video"
private fun JsonParser.parseProduct(repositoryId: Long): Product { private fun JsonParser.parseProduct(repositoryId: Long): Product {
var packageName = "" var packageName = ""
@ -293,16 +308,11 @@ object IndexV1Parser {
key.string(KEY_PRODUCT_LICENSE) -> licenses += valueAsString.split(',') key.string(KEY_PRODUCT_LICENSE) -> licenses += valueAsString.split(',')
.filter { it.isNotEmpty() } .filter { it.isNotEmpty() }
key.string(KEY_PRODUCT_DONATE) -> donates += Product.Donate.Regular(valueAsString) key.string(KEY_PRODUCT_DONATE) -> donates += Regular(valueAsString)
key.string(KEY_PRODUCT_BITCOIN) -> donates += Product.Donate.Bitcoin(valueAsString) key.string(KEY_PRODUCT_BITCOIN) -> donates += Bitcoin(valueAsString)
key.string(KEY_PRODUCT_FLATTRID) -> donates += Product.Donate.Flattr(valueAsString) key.string(KEY_PRODUCT_LIBERAPAYID) -> donates += Liberapay(valueAsString)
key.string(KEY_PRODUCT_LIBERAPAYID) -> donates += Product.Donate.Liberapay( key.string(KEY_PRODUCT_LITECOIN) -> donates += Litecoin(valueAsString)
valueAsString key.string(KEY_PRODUCT_OPENCOLLECTIVE) -> donates += OpenCollective(valueAsString)
)
key.string(KEY_PRODUCT_OPENCOLLECTIVE) -> donates += Product.Donate.OpenCollective(
valueAsString
)
key.dictionary(KEY_PRODUCT_LOCALIZED) -> forEachKey { localizedKey -> key.dictionary(KEY_PRODUCT_LOCALIZED) -> forEachKey { localizedKey ->
if (localizedKey.token == JsonToken.START_OBJECT) { if (localizedKey.token == JsonToken.START_OBJECT) {
@ -315,6 +325,9 @@ object IndexV1Parser {
var phone = emptyList<String>() var phone = emptyList<String>()
var smallTablet = emptyList<String>() var smallTablet = emptyList<String>()
var largeTablet = emptyList<String>() var largeTablet = emptyList<String>()
var wear = emptyList<String>()
var tv = emptyList<String>()
var video = emptyList<String>()
forEachKey { forEachKey {
when { when {
it.string(KEY_PRODUCT_NAME) -> name = valueAsString it.string(KEY_PRODUCT_NAME) -> name = valueAsString
@ -322,39 +335,42 @@ object IndexV1Parser {
it.string(KEY_PRODUCT_DESCRIPTION) -> description = valueAsString it.string(KEY_PRODUCT_DESCRIPTION) -> description = valueAsString
it.string(KEY_PRODUCT_WHATSNEW) -> whatsNew = valueAsString it.string(KEY_PRODUCT_WHATSNEW) -> whatsNew = valueAsString
it.string(KEY_PRODUCT_ICON) -> metadataIcon = valueAsString it.string(KEY_PRODUCT_ICON) -> metadataIcon = valueAsString
it.array(KEY_PRODUCT_PHONESCREENSHOTS) -> it.string(KEY_PRODUCT_VIDEO) -> video = listOf(valueAsString)
phone = it.array(KEY_PRODUCT_PHONE_SCREENSHOTS) ->
collectDistinctNotEmptyStrings() phone = collectDistinctNotEmptyStrings()
it.array(KEY_PRODUCT_SEVENINCHSCREENSHOTS) -> it.array(KEY_PRODUCT_SEVEN_INCH_SCREENSHOTS) ->
smallTablet = smallTablet = collectDistinctNotEmptyStrings()
collectDistinctNotEmptyStrings()
it.array(KEY_PRODUCT_TENINCHSCREENSHOTS) -> it.array(KEY_PRODUCT_TEN_INCH_SCREENSHOTS) ->
largeTablet = largeTablet = collectDistinctNotEmptyStrings()
collectDistinctNotEmptyStrings()
it.array(KEY_PRODUCT_WEAR_SCREENSHOTS) ->
wear = collectDistinctNotEmptyStrings()
it.array(KEY_PRODUCT_TV_SCREENSHOTS) ->
tv = collectDistinctNotEmptyStrings()
else -> skipChildren() else -> skipChildren()
} }
} }
val isScreenshotEmpty =
arrayOf(video, phone, smallTablet, largeTablet, wear, tv)
.any { it.isNotEmpty() }
val screenshots = val screenshots =
if (sequenceOf( if (isScreenshotEmpty) {
phone, Screenshots(video, phone, smallTablet, largeTablet, wear, tv)
smallTablet,
largeTablet
).any { it.isNotEmpty() }
) {
Screenshots(phone, smallTablet, largeTablet)
} else { } else {
null null
} }
localizedMap[locale] = Localized( localizedMap[locale] = Localized(
name, name = name,
summary, summary = summary,
description, description = description,
whatsNew, whatsNew = whatsNew,
metadataIcon.nullIfEmpty()?.let { "$locale/$it" }.orEmpty(), metadataIcon = metadataIcon.nullIfEmpty()?.let { "$locale/$it" }
screenshots .orEmpty(),
screenshots = screenshots,
) )
} else { } else {
skipChildren() skipChildren()
@ -377,28 +393,14 @@ object IndexV1Parser {
} }
val screenshotPairs = val screenshotPairs =
localizedMap.find { key, localized -> localized.screenshots?.let { Pair(key, it) } } localizedMap.find { key, localized -> localized.screenshots?.let { Pair(key, it) } }
val screenshots = screenshotPairs val screenshots = screenshotPairs?.let { (key, screenshots) ->
?.let { (key, screenshots) -> screenshots.video.map { Product.Screenshot(key, VIDEO, it) } +
screenshots.phone.asSequence() screenshots.phone.map { Product.Screenshot(key, PHONE, it) } +
.map { Product.Screenshot(key, Product.Screenshot.Type.PHONE, it) } + screenshots.smallTablet.map { Product.Screenshot(key, SMALL_TABLET, it) } +
screenshots.smallTablet.asSequence() screenshots.largeTablet.map { Product.Screenshot(key, LARGE_TABLET, it) } +
.map { screenshots.wear.map { Product.Screenshot(key, WEAR, it) } +
Product.Screenshot( screenshots.tv.map { Product.Screenshot(key, TV, it) }
key, }.orEmpty()
Product.Screenshot.Type.SMALL_TABLET,
it
)
} +
screenshots.largeTablet.asSequence()
.map {
Product.Screenshot(
key,
Product.Screenshot.Type.LARGE_TABLET,
it
)
}
}
.orEmpty().toList()
return Product( return Product(
repositoryId = repositoryId, repositoryId = repositoryId,
packageName = packageName, packageName = packageName,

View File

@ -16,6 +16,8 @@ import com.looker.droidify.installer.installers.session.SessionInstaller
import com.looker.droidify.installer.installers.shizuku.ShizukuInstaller import com.looker.droidify.installer.installers.shizuku.ShizukuInstaller
import com.looker.droidify.installer.model.InstallItem import com.looker.droidify.installer.model.InstallItem
import com.looker.droidify.installer.model.InstallState import com.looker.droidify.installer.model.InstallState
import com.looker.droidify.installer.notification.createInstallNotification
import com.looker.droidify.installer.notification.installNotification
import com.looker.droidify.installer.notification.removeInstallNotification import com.looker.droidify.installer.notification.removeInstallNotification
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
@ -30,7 +32,7 @@ import kotlinx.coroutines.sync.withLock
class InstallManager( class InstallManager(
private val context: Context, private val context: Context,
settingsRepository: SettingsRepository private val settingsRepository: SettingsRepository
) { ) {
private val installItems = Channel<InstallItem>() private val installItems = Channel<InstallItem>()
@ -87,6 +89,13 @@ class InstallManager(
}.consumeEach { item -> }.consumeEach { item ->
if (state.value.containsKey(item.packageName)) { if (state.value.containsKey(item.packageName)) {
updateState { put(item.packageName, InstallState.Installing) } updateState { put(item.packageName, InstallState.Installing) }
context.notificationManager?.installNotification(
packageName = item.packageName.name,
notification = context.createInstallNotification(
appName = item.packageName.name,
state = InstallState.Installing,
)
)
val success = installer.use { val success = installer.use {
it.install(item) it.install(item)
} }
@ -106,7 +115,7 @@ class InstallManager(
private suspend fun setInstaller(installerType: InstallerType) { private suspend fun setInstaller(installerType: InstallerType) {
lock.withLock { lock.withLock {
_installer = when (installerType) { _installer = when (installerType) {
InstallerType.LEGACY -> LegacyInstaller(context) InstallerType.LEGACY -> LegacyInstaller(context, settingsRepository)
InstallerType.SESSION -> SessionInstaller(context) InstallerType.SESSION -> SessionInstaller(context)
InstallerType.SHIZUKU -> ShizukuInstaller(context) InstallerType.SHIZUKU -> ShizukuInstaller(context)
InstallerType.ROOT -> RootInstaller(context) InstallerType.ROOT -> RootInstaller(context)

View File

@ -1,49 +1,80 @@
package com.looker.droidify.installer.installers package com.looker.droidify.installer.installers
import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.AndroidRuntimeException import android.util.AndroidRuntimeException
import androidx.core.net.toUri import androidx.core.net.toUri
import com.looker.droidify.utility.common.SdkCheck import com.looker.droidify.R
import com.looker.droidify.utility.common.cache.Cache import com.looker.droidify.datastore.SettingsRepository
import com.looker.droidify.datastore.get
import com.looker.droidify.datastore.model.LegacyInstallerComponent
import com.looker.droidify.domain.model.PackageName import com.looker.droidify.domain.model.PackageName
import com.looker.droidify.installer.model.InstallItem import com.looker.droidify.installer.model.InstallItem
import com.looker.droidify.installer.model.InstallState import com.looker.droidify.installer.model.InstallState
import kotlin.coroutines.resume import com.looker.droidify.utility.common.SdkCheck
import com.looker.droidify.utility.common.cache.Cache
import com.looker.droidify.utility.common.extension.intent
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
internal class LegacyInstaller(private val context: Context) : Installer { class LegacyInstaller(
private val context: Context,
private val settingsRepository: SettingsRepository
) : Installer {
companion object { companion object {
private const val APK_MIME = "application/vnd.android.package-archive" private const val APK_MIME = "application/vnd.android.package-archive"
} }
override suspend fun install( override suspend fun install(
installItem: InstallItem installItem: InstallItem,
): InstallState = suspendCancellableCoroutine { cont -> ): InstallState {
val (uri, flags) = if (SdkCheck.isNougat) { val installFlag = if (SdkCheck.isNougat) Intent.FLAG_GRANT_READ_URI_PERMISSION else 0
Cache.getReleaseUri( val fileUri = if (SdkCheck.isNougat) {
context, Cache.getReleaseUri(context, installItem.installFileName)
installItem.installFileName
) to Intent.FLAG_GRANT_READ_URI_PERMISSION
} else { } else {
val file = Cache.getReleaseFile(context, installItem.installFileName) Cache.getReleaseFile(context, installItem.installFileName).toUri()
file.toUri() to 0
} }
try {
context.startActivity( val comp = settingsRepository.get { legacyInstallerComponent }.firstOrNull()
Intent(Intent.ACTION_INSTALL_PACKAGE).setDataAndType(uri, APK_MIME).setFlags(flags)
) return suspendCancellableCoroutine { cont ->
cont.resume(InstallState.Installed) val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
} catch (e: AndroidRuntimeException) { setDataAndType(fileUri, APK_MIME)
context.startActivity( flags = installFlag
Intent(Intent.ACTION_INSTALL_PACKAGE).setDataAndType(uri, APK_MIME) when (comp) {
.setFlags(flags or Intent.FLAG_ACTIVITY_NEW_TASK) is LegacyInstallerComponent.Component -> {
) component = ComponentName(comp.clazz, comp.activity)
cont.resume(InstallState.Installed) }
} catch (e: Exception) { else -> {
cont.resume(InstallState.Failed) // For Unspecified and AlwaysChoose, don't set component
}
}
}
val installIntent = when (comp) {
LegacyInstallerComponent.AlwaysChoose -> Intent.createChooser(intent, context.getString(
R.string.select_installer))
else -> intent
}
try {
context.startActivity(installIntent)
cont.resume(InstallState.Installed)
} catch (e: AndroidRuntimeException) {
installIntent.flags = installFlag or Intent.FLAG_ACTIVITY_NEW_TASK
try {
context.startActivity(installIntent)
cont.resume(InstallState.Installed)
} catch (e: Exception) {
cont.resume(InstallState.Failed)
}
} catch (e: Exception) {
cont.resume(InstallState.Failed)
}
} }
} }
@ -53,14 +84,14 @@ internal class LegacyInstaller(private val context: Context) : Installer {
override fun close() {} override fun close() {}
} }
internal suspend fun Context.uninstallPackage(packageName: PackageName) = suspend fun Context.uninstallPackage(packageName: PackageName) =
suspendCancellableCoroutine { cont -> suspendCancellableCoroutine { cont ->
try { try {
startActivity( startActivity(
Intent( intent(Intent.ACTION_UNINSTALL_PACKAGE) {
Intent.ACTION_UNINSTALL_PACKAGE, data = "package:${packageName.name}".toUri()
"package:${packageName.name}".toUri() flags = Intent.FLAG_ACTIVITY_NEW_TASK
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }
) )
cont.resume(Unit) cont.resume(Unit)
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -1,70 +1,34 @@
package com.looker.droidify.installer.installers.root package com.looker.droidify.installer.installers.root
import android.content.Context import android.content.Context
import com.looker.droidify.utility.common.SdkCheck
import com.looker.droidify.utility.common.cache.Cache
import com.looker.droidify.domain.model.PackageName import com.looker.droidify.domain.model.PackageName
import com.looker.droidify.installer.installers.Installer import com.looker.droidify.installer.installers.Installer
import com.looker.droidify.installer.installers.uninstallPackage import com.looker.droidify.installer.installers.uninstallPackage
import com.looker.droidify.installer.model.InstallItem import com.looker.droidify.installer.model.InstallItem
import com.looker.droidify.installer.model.InstallState import com.looker.droidify.installer.model.InstallState
import com.looker.droidify.utility.common.SdkCheck
import com.looker.droidify.utility.common.cache.Cache
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.File
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal class RootInstaller(private val context: Context) : Installer { class RootInstaller(private val context: Context) : Installer {
private companion object {
const val ROOT_INSTALL_PACKAGE = "cat %s | pm install --user %s -t -r -S %s"
const val DELETE_PACKAGE = "%s rm %s"
val getCurrentUserState: String
get() = if (SdkCheck.isOreo) {
com.topjohnwu.superuser.Shell.cmd("am get-current-user").exec().out[0]
} else {
com.topjohnwu.superuser.Shell.cmd("dumpsys activity | grep -E \"mUserLru\"")
.exec().out[0].trim()
.removePrefix("mUserLru: [").removeSuffix("]")
}
val String.quote
get() = "\"${this.replace(Regex("""[\\$"`]""")) { c -> "\\${c.value}" }}\""
val getUtilBoxPath: String
get() {
listOf("toybox", "busybox").forEach {
val shellResult = com.topjohnwu.superuser.Shell.cmd("which $it").exec()
if (shellResult.out.isNotEmpty()) {
val utilBoxPath = shellResult.out.joinToString("")
if (utilBoxPath.isNotEmpty()) return utilBoxPath.quote
}
}
return ""
}
fun installCmd(file: File): String = String.format(
ROOT_INSTALL_PACKAGE,
file.absolutePath,
getCurrentUserState,
file.length()
)
fun deleteCmd(file: File): String = String.format(
DELETE_PACKAGE,
getUtilBoxPath,
file.absolutePath.quote
)
}
override suspend fun install( override suspend fun install(
installItem: InstallItem installItem: InstallItem,
): InstallState = suspendCancellableCoroutine { cont -> ): InstallState = suspendCancellableCoroutine { cont ->
val releaseFile = Cache.getReleaseFile(context, installItem.installFileName) val releaseFile = Cache.getReleaseFile(context, installItem.installFileName)
com.topjohnwu.superuser.Shell.cmd(installCmd(releaseFile)).submit { shellResult -> val installCommand = INSTALL_COMMAND.format(
releaseFile.absolutePath,
currentUser(),
releaseFile.length(),
)
Shell.cmd(installCommand).submit { shellResult ->
val result = if (shellResult.isSuccess) InstallState.Installed val result = if (shellResult.isSuccess) InstallState.Installed
else InstallState.Failed else InstallState.Failed
cont.resume(result) cont.resume(result)
com.topjohnwu.superuser.Shell.cmd(deleteCmd(releaseFile)).submit() val deleteCommand = DELETE_COMMAND.format(utilBox(), releaseFile.absolutePath)
Shell.cmd(deleteCommand).submit()
} }
} }
@ -72,4 +36,34 @@ internal class RootInstaller(private val context: Context) : Installer {
context.uninstallPackage(packageName) context.uninstallPackage(packageName)
override fun close() {} override fun close() {}
}
private const val INSTALL_COMMAND = "cat %s | pm install --user %s -t -r -S %s"
private const val DELETE_COMMAND = "%s rm %s"
/** Returns the path of either toybox or busybox, or empty string if not found. */
private fun utilBox(): String {
listOf("toybox", "busybox").forEach {
// Returns the path of the requested [command], or empty string if not found
val out = Shell.cmd("which $it").exec().out
if (out.isEmpty()) return ""
if (out.first().contains("not found")) return ""
return out.first()
}
return ""
}
/** Returns the current user of the device. */
private fun currentUser() = if (SdkCheck.isOreo) {
Shell.cmd("am get-current-user")
.exec()
.out[0]
} else {
Shell.cmd("dumpsys activity | grep -E \"mUserLru\"")
.exec()
.out[0]
.trim()
.removePrefix("mUserLru: [")
.removeSuffix("]")
} }

View File

@ -20,7 +20,7 @@ import com.looker.droidify.installer.model.InstallState
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal class SessionInstaller(private val context: Context) : Installer { class SessionInstaller(private val context: Context) : Installer {
private val installer = context.packageManager.packageInstaller private val installer = context.packageManager.packageInstaller
private val intent = Intent(context, SessionInstallerReceiver::class.java) private val intent = Intent(context, SessionInstallerReceiver::class.java)

View File

@ -14,7 +14,7 @@ import java.io.BufferedReader
import java.io.InputStream import java.io.InputStream
import kotlin.coroutines.resume import kotlin.coroutines.resume
internal class ShizukuInstaller(private val context: Context) : Installer { class ShizukuInstaller(private val context: Context) : Installer {
companion object { companion object {
private val SESSION_ID_REGEX = Regex("(?<=\\[).+?(?=])") private val SESSION_ID_REGEX = Regex("(?<=\\[).+?(?=])")

View File

@ -3,7 +3,7 @@ package com.looker.droidify.installer.model
import com.looker.droidify.domain.model.PackageName import com.looker.droidify.domain.model.PackageName
import com.looker.droidify.domain.model.toPackageName import com.looker.droidify.domain.model.toPackageName
data class InstallItem( class InstallItem(
val packageName: PackageName, val packageName: PackageName,
val installFileName: String val installFileName: String
) )

View File

@ -1,7 +1,9 @@
package com.looker.droidify.model package com.looker.droidify.model
import com.looker.droidify.domain.model.Donation import android.content.Context
import com.looker.droidify.domain.model.Screenshots import com.looker.droidify.utility.common.extension.getColorFromAttr
import com.looker.droidify.utility.common.extension.videoPlaceHolder
import com.google.android.material.R as MaterialR
data class Product( data class Product(
var repositoryId: Long, var repositoryId: Long,
@ -33,29 +35,38 @@ data class Product(
data class Regular(val url: String) : Donate() data class Regular(val url: String) : Donate()
data class Bitcoin(val address: String) : Donate() data class Bitcoin(val address: String) : Donate()
data class Litecoin(val address: String) : Donate() data class Litecoin(val address: String) : Donate()
data class Flattr(val id: String) : Donate()
data class Liberapay(val id: String) : Donate() data class Liberapay(val id: String) : Donate()
data class OpenCollective(val id: String) : Donate() data class OpenCollective(val id: String) : Donate()
} }
class Screenshot(val locale: String, val type: Type, val path: String) { class Screenshot(val locale: String, val type: Type, val path: String) {
enum class Type(val jsonName: String) { enum class Type(val jsonName: String) {
VIDEO("video"),
PHONE("phone"), PHONE("phone"),
SMALL_TABLET("smallTablet"), SMALL_TABLET("smallTablet"),
LARGE_TABLET("largeTablet") LARGE_TABLET("largeTablet"),
WEAR("wear"),
TV("tv")
} }
val identifier: String val identifier: String
get() = "$locale.${type.name}.$path" get() = "$locale.${type.name}.$path"
fun url( fun url(
context: Context,
repository: Repository, repository: Repository,
packageName: String packageName: String
): String { ): Any {
if (type == Type.VIDEO) return context.videoPlaceHolder.apply {
setTintList(context.getColorFromAttr(MaterialR.attr.colorOnSurfaceInverse))
}
val phoneType = when (type) { val phoneType = when (type) {
Type.PHONE -> "phoneScreenshots" Type.PHONE -> "phoneScreenshots"
Type.SMALL_TABLET -> "sevenInchScreenshots" Type.SMALL_TABLET -> "sevenInchScreenshots"
Type.LARGE_TABLET -> "tenInchScreenshots" Type.LARGE_TABLET -> "tenInchScreenshots"
Type.WEAR -> "wearScreenshots"
Type.TV -> "tvScreenshots"
else -> error("Should not be here, video url already returned")
} }
return "${repository.address}/$packageName/$locale/$phoneType/$path" return "${repository.address}/$packageName/$locale/$phoneType/$path"
} }
@ -115,48 +126,3 @@ fun List<Pair<Product, Repository>>.findSuggested(
} }
) )
) )
//
//fun App.toProduct() = Product(
// packageName = metadata.packageName.name,
// name = metadata.name,
// summary = metadata.summary,
// description = metadata.description,
// whatsNew = metadata.whatsNew,
// icon = metadata.icon,
// metadataIcon = "",
// author = Product.Author(
// name = author.name,
// email = author.email,
// web = author.web,
// ),
// source = links.sourceCode,
// changelog = links.changelog,
// web = links.webSite,
// tracker = links.issueTracker,
// added = metadata.added,
// updated = metadata.lastUpdated,
// suggestedVersionCode = metadata.suggestedVersionCode,
// categories = categories,
// antiFeatures = metadata.antiFeatures,
// licenses = listOf(metadata.license),
// donates = donation.toLegacy(),
// screenshots = screenshots.toLegacy(),
// releases = packages
//)
fun Donation.toLegacy() = buildList {
regularUrl?.let { it.forEach { add(Product.Donate.Regular(it)) } }
bitcoinAddress?.let { add(Product.Donate.Bitcoin(it)) }
flattrId?.let { add(Product.Donate.Flattr(it)) }
litecoinAddress?.let { add(Product.Donate.Litecoin(it)) }
openCollectiveId?.let { add(Product.Donate.OpenCollective(it)) }
liberapayId?.let { add(Product.Donate.Liberapay(it)) }
}
fun Screenshots.toLegacy() = buildList {
phone.forEach { add(Product.Screenshot("en-US", Product.Screenshot.Type.PHONE, it)) }
sevenInch.forEach { add(Product.Screenshot("en-US", Product.Screenshot.Type.SMALL_TABLET, it)) }
tenInch.forEach { add(Product.Screenshot("en-US", Product.Screenshot.Type.LARGE_TABLET, it)) }
tv.forEach { add(Product.Screenshot("en-US", Product.Screenshot.Type.PHONE, it)) }
wear.forEach { add(Product.Screenshot("en-US", Product.Screenshot.Type.PHONE, it)) }
}

View File

@ -18,16 +18,16 @@ data class ProductItem(
var canUpdate: Boolean, var canUpdate: Boolean,
var matchRank: Int var matchRank: Int
) { ) {
sealed class Section : Parcelable { sealed interface Section : Parcelable {
@Parcelize @Parcelize
data object All : Section() object All : Section
@Parcelize @Parcelize
data class Category(val name: String) : Section() class Category(val name: String) : Section
@Parcelize @Parcelize
data class Repository(val id: Long, val name: String) : Section() class Repository(val id: Long, val name: String) : Section
} }
private val supportedDpi = intArrayOf(120, 160, 240, 320, 480, 640) private val supportedDpi = intArrayOf(120, 160, 240, 320, 480, 640)

View File

@ -31,7 +31,7 @@ data class Release(
object MinSdk : Incompatibility() object MinSdk : Incompatibility()
object MaxSdk : Incompatibility() object MaxSdk : Incompatibility()
object Platform : Incompatibility() object Platform : Incompatibility()
data class Feature(val feature: String) : Incompatibility() class Feature(val feature: String) : Incompatibility()
} }
val identifier: String val identifier: String

View File

@ -15,7 +15,7 @@ data class Repository(
val entityTag: String, val entityTag: String,
val updated: Long, val updated: Long,
val timestamp: Long, val timestamp: Long,
val authentication: String val authentication: String,
) { ) {
fun edit(address: String, fingerprint: String, authentication: String): Repository { fun edit(address: String, fingerprint: String, authentication: String): Repository {
@ -38,7 +38,7 @@ data class Repository(
version: Int, version: Int,
lastModified: String, lastModified: String,
entityTag: String, entityTag: String,
timestamp: Long timestamp: Long,
): Repository { ): Repository {
return copy( return copy(
mirrors = mirrors, mirrors = mirrors,
@ -62,7 +62,7 @@ data class Repository(
fun newRepository( fun newRepository(
address: String, address: String,
fingerprint: String, fingerprint: String,
authentication: String authentication: String,
): Repository { ): Repository {
val name = try { val name = try {
URL(address).let { "${it.host}${it.path}" } URL(address).let { "${it.host}${it.path}" }
@ -79,7 +79,7 @@ data class Repository(
version: Int = 21, version: Int = 21,
enabled: Boolean = false, enabled: Boolean = false,
fingerprint: String, fingerprint: String,
authentication: String = "" authentication: String = "",
): Repository { ): Repository {
return Repository( return Repository(
-1, address, emptyList(), name, description, version, enabled, -1, address, emptyList(), name, description, version, enabled,
@ -150,14 +150,6 @@ data class Repository(
" by Netsyms Technologies.", " by Netsyms Technologies.",
fingerprint = "2581BA7B32D3AB443180C4087CAB6A7E8FB258D3A6E98870ECB3C675E4D64489" fingerprint = "2581BA7B32D3AB443180C4087CAB6A7E8FB258D3A6E98870ECB3C675E4D64489"
), ),
defaultRepository(
address = "https://fdroid.bromite.org/fdroid/repo",
name = "Bromite",
description = "The official repository for Bromite. " +
"Bromite is a Chromium with ad blocking and enhanced p" +
"rivacy.",
fingerprint = "E1EE5CD076D7B0DC84CB2B45FB78B86DF2EB39A3B6C56BA3DC292A5E0C3B9504"
),
defaultRepository( defaultRepository(
address = "https://molly.im/fdroid/foss/fdroid/repo", address = "https://molly.im/fdroid/foss/fdroid/repo",
name = "Molly", name = "Molly",
@ -217,7 +209,7 @@ data class Repository(
name = "Kali Nethunter", name = "Kali Nethunter",
description = "Kali Nethunter's official selection of original b" + description = "Kali Nethunter's official selection of original b" +
"inaries.", "inaries.",
fingerprint = "7E418D34C3AD4F3C37D7E6B0FACE13332364459C862134EB099A3BDA2CCF4494" fingerprint = "FE7A23DFC003A1CF2D2ADD2469B9C0C49B206BA5DC9EDD6563B3B7EB6A8F5FAB"
), ),
defaultRepository( defaultRepository(
address = "https://secfirst.org/fdroid/repo", address = "https://secfirst.org/fdroid/repo",
@ -265,14 +257,14 @@ data class Repository(
name = "Threema Libre", name = "Threema Libre",
description = "The official repository for Threema Libre. R" + description = "The official repository for Threema Libre. R" +
"equires Threema Shop license. Threema Libre is an open" + "equires Threema Shop license. Threema Libre is an open" +
"-source messanger focused on security and privacy.", "-source messenger focused on security and privacy.",
fingerprint = "5734E753899B25775D90FE85362A49866E05AC4F83C05BEF5A92880D2910639E" fingerprint = "5734E753899B25775D90FE85362A49866E05AC4F83C05BEF5A92880D2910639E"
), ),
defaultRepository( defaultRepository(
address = "https://fdroid.getsession.org/fdroid/repo", address = "https://fdroid.getsession.org/fdroid/repo",
name = "Session", name = "Session",
description = "The official repository for Session. Session" + description = "The official repository for Session. Session" +
" is an open-source messanger focused on security and privacy.", " is an open-source messenger focused on security and privacy.",
fingerprint = "DB0E5297EB65CC22D6BD93C869943BDCFCB6A07DC69A48A0DD8C7BA698EC04E6" fingerprint = "DB0E5297EB65CC22D6BD93C869943BDCFCB6A07DC69A48A0DD8C7BA698EC04E6"
), ),
defaultRepository( defaultRepository(
@ -398,7 +390,7 @@ data class Repository(
), ),
) )
val newlyAdded = listOf<Repository>( val newlyAdded: List<Repository> = listOf(
defaultRepository( defaultRepository(
address = "https://fdroid.ironfoxoss.org/fdroid/repo", address = "https://fdroid.ironfoxoss.org/fdroid/repo",
name = "IronFox", name = "IronFox",
@ -412,6 +404,14 @@ data class Repository(
description = "The official repository for Total Commander", description = "The official repository for Total Commander",
fingerprint = "3576596CECDD70488D61CFD90799A49B7FFD26A81A8FEF1BADEC88D069FA72C1" fingerprint = "3576596CECDD70488D61CFD90799A49B7FFD26A81A8FEF1BADEC88D069FA72C1"
), ),
defaultRepository(
address = "https://www.cromite.org/fdroid/repo",
name = "Cromite",
description = "The official repository for Cromite. " +
"Cromite is a Chromium fork based on Bromite with " +
"built-in support for ad blocking and an eye for privacy.",
fingerprint = "49F37E74DEE483DCA2B991334FB5A0200787430D0B5F9A783DD5F13695E9517B"
)
) )
} }
} }

View File

@ -136,7 +136,7 @@ internal class KtorDownloader(
private const val CONNECTION_TIMEOUT = 30_000L private const val CONNECTION_TIMEOUT = 30_000L
private const val SOCKET_TIMEOUT = 15_000L private const val SOCKET_TIMEOUT = 15_000L
private const val USER_AGENT = "Droid-ify, ${BuildConfig.VERSION_NAME}" private const val USER_AGENT = "Droid-ify/${BuildConfig.VERSION_NAME}-${BuildConfig.BUILD_TYPE}"
private fun HttpClientConfig<*>.userAgentConfig() = install(UserAgent) { private fun HttpClientConfig<*>.userAgentConfig() = install(UserAgent) {
agent = USER_AGENT agent = USER_AGENT

View File

@ -22,7 +22,6 @@ import com.looker.droidify.utility.common.extension.getColorFromAttr
import com.looker.droidify.utility.common.extension.notificationManager import com.looker.droidify.utility.common.extension.notificationManager
import com.looker.droidify.utility.common.extension.startServiceCompat import com.looker.droidify.utility.common.extension.startServiceCompat
import com.looker.droidify.utility.common.extension.stopForegroundCompat import com.looker.droidify.utility.common.extension.stopForegroundCompat
import com.looker.droidify.utility.common.log
import com.looker.droidify.utility.common.result.Result import com.looker.droidify.utility.common.result.Result
import com.looker.droidify.utility.common.sdkAbove import com.looker.droidify.utility.common.sdkAbove
import com.looker.droidify.datastore.SettingsRepository import com.looker.droidify.datastore.SettingsRepository
@ -53,6 +52,8 @@ import kotlinx.coroutines.withContext
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import javax.inject.Inject import javax.inject.Inject
import com.looker.droidify.R import com.looker.droidify.R
import kotlinx.coroutines.FlowPreview
import kotlin.math.roundToInt
import android.R as AndroidR import android.R as AndroidR
import com.looker.droidify.R.string as stringRes import com.looker.droidify.R.string as stringRes
import com.looker.droidify.R.style as styleRes import com.looker.droidify.R.style as styleRes
@ -69,15 +70,16 @@ class SyncService : ConnectionService<SyncService.Binder>() {
private const val MAX_UPDATE_NOTIFICATION = 5 private const val MAX_UPDATE_NOTIFICATION = 5
private const val ACTION_CANCEL = "${BuildConfig.APPLICATION_ID}.intent.action.CANCEL" private const val ACTION_CANCEL = "${BuildConfig.APPLICATION_ID}.intent.action.CANCEL"
private val syncState = MutableSharedFlow<State>() val syncState = MutableSharedFlow<State>()
} }
@Inject @Inject
lateinit var settingsRepository: SettingsRepository lateinit var settingsRepository: SettingsRepository
sealed class State(val name: String) { sealed class State(val name: String) {
data class Connecting(val appName: String) : State(appName) class Connecting(appName: String) : State(appName)
data class Syncing(
class Syncing(
val appName: String, val appName: String,
val stage: RepositoryUpdater.Stage, val stage: RepositoryUpdater.Stage,
val read: DataSize, val read: DataSize,
@ -85,6 +87,18 @@ class SyncService : ConnectionService<SyncService.Binder>() {
) : State(appName) ) : State(appName)
data object Finish : State("") data object Finish : State("")
val progress: Int
get() = when (this) {
is Connecting -> Int.MIN_VALUE
Finish -> Int.MAX_VALUE
is Syncing -> when(stage) {
RepositoryUpdater.Stage.DOWNLOAD -> ((read percentBy total) * 0.4F).roundToInt()
RepositoryUpdater.Stage.PROCESS -> 50
RepositoryUpdater.Stage.MERGE -> 75
RepositoryUpdater.Stage.COMMIT -> 90
}
}
} }
private class Task(val repositoryId: Long, val manual: Boolean) private class Task(val repositoryId: Long, val manual: Boolean)
@ -145,7 +159,8 @@ class SyncService : ConnectionService<SyncService.Binder>() {
} }
suspend fun updateAllApps() { suspend fun updateAllApps() {
updateAllAppsInternal() val skipSignature = settingsRepository.getInitial().ignoreSignature
updateAllAppsInternal(skipSignature)
} }
fun setUpdateNotificationBlocker(fragment: Fragment?) { fun setUpdateNotificationBlocker(fragment: Fragment?) {
@ -198,6 +213,7 @@ class SyncService : ConnectionService<SyncService.Binder>() {
private val binder = Binder() private val binder = Binder()
override fun onBind(intent: Intent): Binder = binder override fun onBind(intent: Intent): Binder = binder
@OptIn(FlowPreview::class)
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -389,7 +405,8 @@ class SyncService : ConnectionService<SyncService.Binder>() {
handleUpdates( handleUpdates(
hasUpdates = hasUpdates, hasUpdates = hasUpdates,
notifyUpdates = setting.notifyUpdate, notifyUpdates = setting.notifyUpdate,
autoUpdate = setting.autoUpdate autoUpdate = setting.autoUpdate,
skipSignature = setting.ignoreSignature,
) )
} }
} }
@ -470,7 +487,8 @@ class SyncService : ConnectionService<SyncService.Binder>() {
private suspend fun handleUpdates( private suspend fun handleUpdates(
hasUpdates: Boolean, hasUpdates: Boolean,
notifyUpdates: Boolean, notifyUpdates: Boolean,
autoUpdate: Boolean autoUpdate: Boolean,
skipSignature: Boolean,
) { ) {
try { try {
if (!hasUpdates) { if (!hasUpdates) {
@ -481,15 +499,16 @@ class SyncService : ConnectionService<SyncService.Binder>() {
return return
} }
val blocked = updateNotificationBlockerFragment?.get()?.isAdded == true val blocked = updateNotificationBlockerFragment?.get()?.isAdded == true
val updates = Database.ProductAdapter.getUpdates() val updates = Database.ProductAdapter.getUpdates(skipSignature)
if (!blocked && updates.isNotEmpty()) { if (!blocked && updates.isNotEmpty()) {
if (notifyUpdates) displayUpdatesNotification(updates) if (notifyUpdates) displayUpdatesNotification(updates)
if (autoUpdate) updateAllAppsInternal() if (autoUpdate) updateAllAppsInternal(skipSignature)
} }
handleUpdates( handleUpdates(
hasUpdates = false, hasUpdates = false,
notifyUpdates = notifyUpdates, notifyUpdates = notifyUpdates,
autoUpdate = autoUpdate autoUpdate = autoUpdate,
skipSignature = skipSignature,
) )
} finally { } finally {
withContext(NonCancellable) { withContext(NonCancellable) {
@ -499,10 +518,9 @@ class SyncService : ConnectionService<SyncService.Binder>() {
} }
} }
private suspend fun updateAllAppsInternal() { private suspend fun updateAllAppsInternal(skipSignature: Boolean) {
log("Check Running", "Syncing")
Database.ProductAdapter Database.ProductAdapter
.getUpdates() .getUpdates(skipSignature)
// Update Droid-ify the last // Update Droid-ify the last
.sortedBy { if (it.packageName == packageName) 1 else -1 } .sortedBy { if (it.packageName == packageName) 1 else -1 }
.map { .map {

View File

@ -40,7 +40,7 @@ import androidx.core.text.util.LinkifyCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil3.load
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
@ -56,6 +56,7 @@ import com.looker.droidify.model.Release
import com.looker.droidify.model.Repository import com.looker.droidify.model.Repository
import com.looker.droidify.model.findSuggested import com.looker.droidify.model.findSuggested
import com.looker.droidify.network.DataSize import com.looker.droidify.network.DataSize
import com.looker.droidify.network.percentBy
import com.looker.droidify.utility.PackageItemResolver import com.looker.droidify.utility.PackageItemResolver
import com.looker.droidify.utility.common.extension.authentication import com.looker.droidify.utility.common.extension.authentication
import com.looker.droidify.utility.common.extension.copyToClipboard import com.looker.droidify.utility.common.extension.copyToClipboard
@ -69,6 +70,7 @@ import com.looker.droidify.utility.common.extension.inflate
import com.looker.droidify.utility.common.extension.open import com.looker.droidify.utility.common.extension.open
import com.looker.droidify.utility.common.extension.setTextSizeScaled import com.looker.droidify.utility.common.extension.setTextSizeScaled
import com.looker.droidify.utility.common.nullIfEmpty import com.looker.droidify.utility.common.nullIfEmpty
import com.looker.droidify.utility.common.sdkName
import com.looker.droidify.utility.extension.android.Android import com.looker.droidify.utility.extension.android.Android
import com.looker.droidify.utility.extension.resources.TypefaceExtra import com.looker.droidify.utility.extension.resources.TypefaceExtra
import com.looker.droidify.utility.extension.resources.sizeScaled import com.looker.droidify.utility.extension.resources.sizeScaled
@ -101,7 +103,7 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
fun onFavouriteClicked() fun onFavouriteClicked()
fun onPreferenceChanged(preference: ProductPreference) fun onPreferenceChanged(preference: ProductPreference)
fun onPermissionsClick(group: String?, permissions: List<String>) fun onPermissionsClick(group: String?, permissions: List<String>)
fun onScreenshotClick(screenshot: Product.Screenshot, parentView: ImageView) fun onScreenshotClick(position: Int)
fun onReleaseClick(release: Release) fun onReleaseClick(release: Release)
fun onRequestAddRepository(address: String) fun onRequestAddRepository(address: String)
fun onUriClick(uri: Uri, shouldConfirm: Boolean): Boolean fun onUriClick(uri: Uri, shouldConfirm: Boolean): Boolean
@ -313,7 +315,6 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
is Product.Donate.Regular -> drawableRes.ic_donate is Product.Donate.Regular -> drawableRes.ic_donate
is Product.Donate.Bitcoin -> drawableRes.ic_donate_bitcoin is Product.Donate.Bitcoin -> drawableRes.ic_donate_bitcoin
is Product.Donate.Litecoin -> drawableRes.ic_donate_litecoin is Product.Donate.Litecoin -> drawableRes.ic_donate_litecoin
is Product.Donate.Flattr -> drawableRes.ic_donate_flattr
is Product.Donate.Liberapay -> drawableRes.ic_donate_liberapay is Product.Donate.Liberapay -> drawableRes.ic_donate_liberapay
is Product.Donate.OpenCollective -> drawableRes.ic_donate_opencollective is Product.Donate.OpenCollective -> drawableRes.ic_donate_opencollective
} }
@ -322,7 +323,6 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
is Product.Donate.Regular -> context.getString(stringRes.website) is Product.Donate.Regular -> context.getString(stringRes.website)
is Product.Donate.Bitcoin -> "Bitcoin" is Product.Donate.Bitcoin -> "Bitcoin"
is Product.Donate.Litecoin -> "Litecoin" is Product.Donate.Litecoin -> "Litecoin"
is Product.Donate.Flattr -> "Flattr"
is Product.Donate.Liberapay -> "Liberapay" is Product.Donate.Liberapay -> "Liberapay"
is Product.Donate.OpenCollective -> "Open Collective" is Product.Donate.OpenCollective -> "Open Collective"
} }
@ -331,12 +331,8 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
is Product.Donate.Regular -> Uri.parse(donate.url) is Product.Donate.Regular -> Uri.parse(donate.url)
is Product.Donate.Bitcoin -> Uri.parse("bitcoin:${donate.address}") is Product.Donate.Bitcoin -> Uri.parse("bitcoin:${donate.address}")
is Product.Donate.Litecoin -> Uri.parse("litecoin:${donate.address}") is Product.Donate.Litecoin -> Uri.parse("litecoin:${donate.address}")
is Product.Donate.Flattr -> Uri.parse(
"https://flattr.com/thing/${donate.id}"
)
is Product.Donate.Liberapay -> Uri.parse( is Product.Donate.Liberapay -> Uri.parse(
"https://liberapay.com/~${donate.id}" "https://liberapay.com/${donate.id}"
) )
is Product.Donate.OpenCollective -> Uri.parse( is Product.Donate.OpenCollective -> Uri.parse(
@ -561,6 +557,7 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
val size = itemView.findViewById<TextView>(R.id.size)!! val size = itemView.findViewById<TextView>(R.id.size)!!
val signature = itemView.findViewById<TextView>(R.id.signature)!! val signature = itemView.findViewById<TextView>(R.id.signature)!!
val compatibility = itemView.findViewById<TextView>(R.id.compatibility)!! val compatibility = itemView.findViewById<TextView>(R.id.compatibility)!!
val sdkVer = itemView.findViewById<TextView>(R.id.sdk_ver)!!
val statefulViews: Sequence<View> val statefulViews: Sequence<View>
get() = sequenceOf( get() = sequenceOf(
@ -571,7 +568,8 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
added, added,
size, size,
signature, signature,
compatibility compatibility,
sdkVer,
) )
} }
@ -1365,12 +1363,10 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
) )
holder.progress.isIndeterminate = status.total == null holder.progress.isIndeterminate = status.total == null
if (status.total != null) { if (status.total != null) {
holder.progress.progress = holder.progress.setProgressCompat(
( status.read.value percentBy status.total.value,
holder.progress.max.toFloat() * true
status.read.value / )
status.total.value
).roundToInt()
} }
} }
@ -1430,17 +1426,15 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
holder as ScreenShotViewHolder holder as ScreenShotViewHolder
item as Item.ScreenshotItem item as Item.ScreenshotItem
holder.screenshotsRecycler.run { holder.screenshotsRecycler.run {
setHasFixedSize(true)
isNestedScrollingEnabled = false isNestedScrollingEnabled = false
clipToPadding = false clipToPadding = false
setPadding(8.dp, 8.dp, 8.dp, 8.dp) setPadding(8.dp, 8.dp, 8.dp, 8.dp)
layoutManager = layoutManager =
LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
adapter = adapter = ScreenshotsAdapter(callbacks::onScreenshotClick).apply {
ScreenshotsAdapter { screenshot, view -> setScreenshots(item.repository, item.packageName, item.screenshots)
callbacks.onScreenshotClick(screenshot, view) }
}.apply {
setScreenshots(item.repository, item.packageName, item.screenshots)
}
} }
} }
@ -1626,7 +1620,7 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
holder.version.text = holder.version.text =
context.getString(stringRes.version_FORMAT, item.release.version) context.getString(stringRes.version_FORMAT, item.release.version)
holder.status.apply { with(holder.status) {
isVisible = installed || suggested isVisible = installed || suggested
setText( setText(
when { when {
@ -1637,14 +1631,15 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
) )
background = context.corneredBackground background = context.corneredBackground
setPadding(15, 15, 15, 15) setPadding(15, 15, 15, 15)
val (background, foreground) = if (installed) { if (installed) {
MaterialR.attr.colorSecondaryContainer to MaterialR.attr.colorOnSecondaryContainer backgroundTintList =
context.getColorFromAttr(MaterialR.attr.colorSecondaryContainer)
setTextColor(context.getColorFromAttr(MaterialR.attr.colorOnSecondaryContainer))
} else { } else {
MaterialR.attr.colorPrimaryContainer to MaterialR.attr.colorOnPrimaryContainer backgroundTintList =
context.getColorFromAttr(MaterialR.attr.colorPrimaryContainer)
setTextColor(context.getColorFromAttr(MaterialR.attr.colorOnPrimaryContainer))
} }
backgroundTintList =
context.getColorFromAttr(background)
setTextColor(context.getColorFromAttr(foreground))
} }
holder.source.text = holder.source.text =
context.getString(stringRes.provided_by_FORMAT, item.repository.name) context.getString(stringRes.provided_by_FORMAT, item.repository.name)
@ -1688,35 +1683,51 @@ class AppDetailAdapter(private val callbacks: Callbacks) :
} }
holder.signature.text = builder holder.signature.text = builder
} }
holder.compatibility.isVisible = incompatibility != null || singlePlatform != null with(holder.compatibility) {
if (incompatibility != null) { isVisible = incompatibility != null || singlePlatform != null
holder.compatibility.setTextColor( if (incompatibility != null) {
context.getColorFromAttr(MaterialR.attr.colorError) setTextColor(context.getColorFromAttr(MaterialR.attr.colorError))
) text = when (incompatibility) {
holder.compatibility.text = when (incompatibility) { is Release.Incompatibility.MinSdk,
is Release.Incompatibility.MinSdk, is Release.Incompatibility.MaxSdk -> context.getString(
is Release.Incompatibility.MaxSdk stringRes.incompatible_with_FORMAT,
-> context.getString( Android.name
stringRes.incompatible_with_FORMAT, )
Android.name
)
is Release.Incompatibility.Platform -> context.getString( is Release.Incompatibility.Platform -> context.getString(
stringRes.incompatible_with_FORMAT, stringRes.incompatible_with_FORMAT,
Android.primaryPlatform ?: context.getString(stringRes.unknown) Android.primaryPlatform ?: context.getString(stringRes.unknown)
) )
is Release.Incompatibility.Feature -> context.getString( is Release.Incompatibility.Feature -> context.getString(
stringRes.requires_FORMAT, stringRes.requires_FORMAT,
incompatibility.feature incompatibility.feature
)
}
} else if (singlePlatform != null) {
setTextColor(context.getColorFromAttr(android.R.attr.textColorSecondary))
text = context.getString(
stringRes.only_compatible_with_FORMAT,
singlePlatform,
) )
} }
} else if (singlePlatform != null) { }
holder.compatibility.setTextColor( with(holder.sdkVer) {
context.getColorFromAttr(android.R.attr.textColorSecondary) val targetSdkVersion = sdkName.getOrDefault(
item.release.targetSdkVersion,
context.getString(
stringRes.label_unknown_sdk,
item.release.targetSdkVersion,
),
) )
holder.compatibility.text = val minSdkVersion = sdkName.getOrDefault(
context.getString(stringRes.only_compatible_with_FORMAT, singlePlatform) item.release.minSdkVersion,
context.getString(
stringRes.label_unknown_sdk,
item.release.minSdkVersion,
),
)
text = context.getString(stringRes.label_sdk_version, targetSdkVersion, minSdkVersion)
} }
val enabled = status == Status.Idle val enabled = status == Status.Idle
holder.statefulViews.forEach { it.isEnabled = enabled } holder.statefulViews.forEach { it.isEnabled = enabled }

View File

@ -1,5 +1,6 @@
package com.looker.droidify.ui.appDetail package com.looker.droidify.ui.appDetail
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
@ -8,7 +9,6 @@ import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
@ -20,16 +20,13 @@ import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import coil.load import coil3.load
import coil3.request.allowHardware
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.looker.droidify.utility.common.cache.Cache
import com.looker.droidify.utility.common.extension.getLauncherActivities
import com.looker.droidify.utility.common.extension.getMutatedIcon
import com.looker.droidify.utility.common.extension.isFirstItemVisible
import com.looker.droidify.utility.common.extension.isSystemApplication
import com.looker.droidify.utility.common.extension.systemBarsPadding
import com.looker.droidify.utility.common.extension.updateAsMutable
import com.looker.droidify.content.ProductPreferences import com.looker.droidify.content.ProductPreferences
import com.looker.droidify.installer.installers.launchShizuku
import com.looker.droidify.installer.model.InstallState
import com.looker.droidify.installer.model.isCancellable
import com.looker.droidify.model.InstalledItem import com.looker.droidify.model.InstalledItem
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.model.ProductPreference import com.looker.droidify.model.ProductPreference
@ -43,11 +40,15 @@ import com.looker.droidify.ui.MessageDialog
import com.looker.droidify.ui.ScreenFragment import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.ui.appDetail.AppDetailViewModel.Companion.ARG_PACKAGE_NAME import com.looker.droidify.ui.appDetail.AppDetailViewModel.Companion.ARG_PACKAGE_NAME
import com.looker.droidify.ui.appDetail.AppDetailViewModel.Companion.ARG_REPO_ADDRESS import com.looker.droidify.ui.appDetail.AppDetailViewModel.Companion.ARG_REPO_ADDRESS
import com.looker.droidify.utility.extension.screenActivity import com.looker.droidify.utility.common.cache.Cache
import com.looker.droidify.utility.common.extension.getLauncherActivities
import com.looker.droidify.utility.common.extension.getMutatedIcon
import com.looker.droidify.utility.common.extension.isFirstItemVisible
import com.looker.droidify.utility.common.extension.isSystemApplication
import com.looker.droidify.utility.common.extension.systemBarsPadding
import com.looker.droidify.utility.common.extension.updateAsMutable
import com.looker.droidify.utility.extension.mainActivity
import com.looker.droidify.utility.extension.startUpdate import com.looker.droidify.utility.extension.startUpdate
import com.looker.droidify.installer.installers.launchShizuku
import com.looker.droidify.installer.model.InstallState
import com.looker.droidify.installer.model.isCancellable
import com.stfalcon.imageviewer.StfalconImageViewer import com.stfalcon.imageviewer.StfalconImageViewer
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -89,6 +90,7 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
private val viewModel: AppDetailViewModel by viewModels() private val viewModel: AppDetailViewModel by viewModels()
@SuppressLint("RestrictedApi")
private var layoutManagerState: LinearLayoutManager.SavedState? = null private var layoutManagerState: LinearLayoutManager.SavedState? = null
private var actions = Pair(emptySet<Action>(), null as Action?) private var actions = Pair(emptySet<Action>(), null as Action?)
@ -99,6 +101,7 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
private var recyclerView: RecyclerView? = null private var recyclerView: RecyclerView? = null
private var detailAdapter: AppDetailAdapter? = null private var detailAdapter: AppDetailAdapter? = null
private var imageViewer: StfalconImageViewer.Builder<Product.Screenshot>? = null
private val downloadConnection = Connection( private val downloadConnection = Connection(
serviceClass = DownloadService::class.java, serviceClass = DownloadService::class.java,
@ -109,11 +112,12 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
} }
) )
@SuppressLint("RestrictedApi")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
detailAdapter = AppDetailAdapter(this@AppDetailFragment) detailAdapter = AppDetailAdapter(this@AppDetailFragment)
screenActivity.onToolbarCreated(toolbar) mainActivity.onToolbarCreated(toolbar)
toolbar.menu.apply { toolbar.menu.apply {
Action.entries.forEach { action -> Action.entries.forEach { action ->
add(0, action.id, 0, action.adapterAction.titleResId) add(0, action.id, 0, action.adapterAction.titleResId)
@ -205,10 +209,12 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
super.onDestroyView() super.onDestroyView()
recyclerView = null recyclerView = null
detailAdapter = null detailAdapter = null
imageViewer = null
downloadConnection.unbind(requireContext()) downloadConnection.unbind(requireContext())
} }
@SuppressLint("RestrictedApi")
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
@ -446,20 +452,27 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
.show(childFragmentManager) .show(childFragmentManager)
} }
override fun onScreenshotClick(screenshot: Product.Screenshot, parentView: ImageView) { override fun onScreenshotClick(position: Int) {
val product = products if (imageViewer == null) {
.firstOrNull { (product, _) -> val productRepository = products.findSuggested(installed?.installedItem) ?: return
product.screenshots.find { it === screenshot }?.identifier != null val screenshots = productRepository.first.screenshots.mapNotNull {
if (it.type == Product.Screenshot.Type.VIDEO) null
else it
} }
?: return imageViewer = StfalconImageViewer
val screenshots = product.first.screenshots .Builder(context, screenshots) { view, current ->
val position = screenshots.indexOfFirst { screenshot.identifier == it.identifier } val screenshotUrl = current.url(
StfalconImageViewer context = requireContext(),
.Builder(context, screenshots) { view, current -> repository = productRepository.second,
view.load(current.url(product.second, viewModel.packageName)) packageName = viewModel.packageName
} )
.withStartPosition(position) view.load(screenshotUrl) {
.show() allowHardware(false)
}
}
}
imageViewer?.withStartPosition(position)
imageViewer?.show()
} }
override fun onReleaseClick(release: Release) { override fun onReleaseClick(release: Release) {
@ -517,7 +530,7 @@ class AppDetailFragment() : ScreenFragment(), AppDetailAdapter.Callbacks {
} }
override fun onRequestAddRepository(address: String) { override fun onRequestAddRepository(address: String) {
screenActivity.navigateAddRepository(address) mainActivity.navigateAddRepository(address)
} }
override fun onUriClick(uri: Uri, shouldConfirm: Boolean): Boolean { override fun onUriClick(uri: Uri, shouldConfirm: Boolean): Boolean {

View File

@ -5,55 +5,71 @@ import android.graphics.drawable.Drawable
import android.view.Gravity import android.view.Gravity
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.dispose import coil3.asImage
import coil.load import coil3.dispose
import coil.size.Dimension import coil3.load
import coil.size.Scale import coil3.request.placeholder
import coil3.size.Scale
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
import com.looker.droidify.databinding.VideoButtonBinding
import com.looker.droidify.graphics.PaddingDrawable
import com.looker.droidify.model.Product
import com.looker.droidify.model.Repository
import com.looker.droidify.utility.common.extension.aspectRatio import com.looker.droidify.utility.common.extension.aspectRatio
import com.looker.droidify.utility.common.extension.authentication import com.looker.droidify.utility.common.extension.authentication
import com.looker.droidify.utility.common.extension.camera import com.looker.droidify.utility.common.extension.camera
import com.looker.droidify.utility.common.extension.dp import com.looker.droidify.utility.common.extension.dp
import com.looker.droidify.utility.common.extension.dpToPx
import com.looker.droidify.utility.common.extension.getColorFromAttr import com.looker.droidify.utility.common.extension.getColorFromAttr
import com.looker.droidify.utility.common.extension.layoutInflater
import com.looker.droidify.utility.common.extension.openLink
import com.looker.droidify.utility.common.extension.selectableBackground import com.looker.droidify.utility.common.extension.selectableBackground
import com.looker.droidify.graphics.PaddingDrawable
import com.looker.droidify.model.Product
import com.looker.droidify.model.Repository
import com.looker.droidify.widget.StableRecyclerAdapter import com.looker.droidify.widget.StableRecyclerAdapter
import com.google.android.material.R as MaterialR import com.google.android.material.R as MaterialR
import com.looker.droidify.R.dimen as dimenRes import com.looker.droidify.R.dimen as dimenRes
class ScreenshotsAdapter(private val onClick: (Product.Screenshot, ImageView) -> Unit) : class ScreenshotsAdapter(private val onClick: (position: Int) -> Unit) :
StableRecyclerAdapter<ScreenshotsAdapter.ViewType, RecyclerView.ViewHolder>() { StableRecyclerAdapter<ScreenshotsAdapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { SCREENSHOT } enum class ViewType { SCREENSHOT, VIDEO }
private val items = mutableListOf<Item.ScreenshotItem>() private val items = mutableListOf<Item>()
private class ViewHolder(context: Context) : private inner class VideoViewHolder(
RecyclerView.ViewHolder(FrameLayout(context)) { binding: VideoButtonBinding,
val image: ShapeableImageView = object : ShapeableImageView(context) { ) : RecyclerView.ViewHolder(binding.root) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val button = binding.videoButton
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(measuredWidth, measuredHeight) init {
with(button) {
layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.WRAP_CONTENT,
150.dp,
)
setOnClickListener {
val item = items[absoluteAdapterPosition] as Item.VideoItem
it.context?.openLink(item.videoUrl)
}
} }
} }
}
private inner class ScreenshotViewHolder(
context: Context,
) : RecyclerView.ViewHolder(FrameLayout(context)) {
val image = ShapeableImageView(context)
val placeholderColor = context.getColorFromAttr(MaterialR.attr.colorPrimaryContainer) val placeholderColor = context.getColorFromAttr(MaterialR.attr.colorPrimaryContainer)
val radius = context.resources.getDimension(dimenRes.shape_small_corner) val radius = context.resources.getDimension(dimenRes.shape_small_corner)
val imageShapeModel = image.shapeAppearanceModel.toBuilder() val imageShapeModel = image.shapeAppearanceModel.toBuilder()
.setAllCornerSizes(radius) .setAllCornerSizes(radius)
.build() .build()
val cameraIcon = context.camera val cameraIcon = context.camera.apply { setTintList(placeholderColor) }
.apply { setTintList(placeholderColor) }
val placeholder: Drawable = PaddingDrawable(cameraIcon, 3f, context.aspectRatio) val placeholder: Drawable = PaddingDrawable(cameraIcon, 3f, context.aspectRatio)
init { init {
with(image) { with(image) {
layout(0, 0, 0, 0) layout(0, 0, 0, 0)
adjustViewBounds = true
shapeAppearanceModel = imageShapeModel shapeAppearanceModel = imageShapeModel
background = context.selectableBackground background = context.selectableBackground
isFocusable = true isFocusable = true
@ -68,6 +84,14 @@ class ScreenshotsAdapter(private val onClick: (Product.Screenshot, ImageView) ->
marginEnd = radius.toInt() marginEnd = radius.toInt()
} }
foregroundGravity = Gravity.CENTER foregroundGravity = Gravity.CENTER
setOnClickListener {
val position = if (items.any { it.viewType == ViewType.VIDEO }) {
absoluteAdapterPosition - 1
} else {
absoluteAdapterPosition
}
onClick(position)
}
} }
} }
} }
@ -75,67 +99,73 @@ class ScreenshotsAdapter(private val onClick: (Product.Screenshot, ImageView) ->
fun setScreenshots( fun setScreenshots(
repository: Repository, repository: Repository,
packageName: String, packageName: String,
screenshots: List<Product.Screenshot> screenshots: List<Product.Screenshot>,
) { ) {
items.clear() items.clear()
items += screenshots.map { Item.ScreenshotItem(repository, packageName, it) } items += screenshots.map {
if (it.type == Product.Screenshot.Type.VIDEO) Item.VideoItem(it.path)
else Item.ScreenshotItem(repository, packageName, it)
}
notifyItemRangeInserted(0, screenshots.size) notifyItemRangeInserted(0, screenshots.size)
} }
override val viewTypeClass: Class<ViewType> override val viewTypeClass: Class<ViewType> get() = ViewType::class.java
get() = ViewType::class.java override fun getItemCount(): Int = items.size
override fun getItemEnumViewType(position: Int) = items[position].viewType
override fun getItemEnumViewType(position: Int): ViewType { override fun getItemDescriptor(position: Int): String = items[position].descriptor
return ViewType.SCREENSHOT
}
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: ViewType viewType: ViewType,
): RecyclerView.ViewHolder { ): RecyclerView.ViewHolder {
return ViewHolder(parent.context).apply { return when (viewType) {
image.setOnClickListener { ViewType.VIDEO -> VideoViewHolder(VideoButtonBinding.inflate(parent.context.layoutInflater))
onClick( ViewType.SCREENSHOT -> ScreenshotViewHolder(parent.context)
items[absoluteAdapterPosition].screenshot,
it as ImageView
)
}
} }
} }
override fun getItemDescriptor(position: Int): String = items[position].descriptor
override fun getItemCount(): Int = items.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
holder as ViewHolder when (getItemEnumViewType(position)) {
val item = items[position] ViewType.SCREENSHOT -> {
with(holder.image) { holder as ScreenshotViewHolder
load(item.screenshot.url(item.repository, item.packageName)) { val item = items[position] as Item.ScreenshotItem
size(Dimension.Undefined, Dimension(150.dp.dpToPx.toInt())) with(holder.image) {
scale(Scale.FIT) load(item.screenshot.url(context, item.repository, item.packageName)) {
placeholder(holder.placeholder) authentication(item.repository.authentication)
error(holder.placeholder) scale(Scale.FILL)
authentication(item.repository.authentication) placeholder(holder.placeholder)
error(holder.placeholder.asImage())
}
}
} }
ViewType.VIDEO -> {}
} }
} }
override fun onViewRecycled(holder: RecyclerView.ViewHolder) { override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
super.onViewRecycled(holder) super.onViewRecycled(holder)
holder as ViewHolder if (holder is ScreenshotViewHolder) holder.image.dispose()
holder.image.dispose()
} }
private sealed class Item { private sealed interface Item {
abstract val descriptor: String
val descriptor: String
val viewType: ViewType
class ScreenshotItem( class ScreenshotItem(
val repository: Repository, val repository: Repository,
val packageName: String, val packageName: String,
val screenshot: Product.Screenshot val screenshot: Product.Screenshot,
) : Item() { ) : Item {
override val viewType: ViewType get() = ViewType.SCREENSHOT
override val descriptor: String override val descriptor: String
get() = "screenshot.${repository.id}.${screenshot.identifier}" get() = "screenshot.${repository.id}.${screenshot.identifier}"
} }
class VideoItem(val videoUrl: String) : Item {
override val viewType: ViewType get() = ViewType.VIDEO
override val descriptor: String get() = "video"
}
} }
} }

View File

@ -2,6 +2,7 @@ package com.looker.droidify.ui.appList
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.ColorStateList
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,7 +10,7 @@ import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil3.load
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.progressindicator.CircularProgressIndicator import com.google.android.material.progressindicator.CircularProgressIndicator
import com.looker.droidify.R import com.looker.droidify.R
@ -22,23 +23,31 @@ import com.looker.droidify.utility.common.extension.dp
import com.looker.droidify.utility.common.extension.getColorFromAttr import com.looker.droidify.utility.common.extension.getColorFromAttr
import com.looker.droidify.utility.common.extension.inflate import com.looker.droidify.utility.common.extension.inflate
import com.looker.droidify.utility.common.extension.setTextSizeScaled import com.looker.droidify.utility.common.extension.setTextSizeScaled
import com.looker.droidify.utility.common.log
import com.looker.droidify.utility.common.nullIfEmpty import com.looker.droidify.utility.common.nullIfEmpty
import com.looker.droidify.utility.extension.resources.TypefaceExtra import com.looker.droidify.utility.extension.resources.TypefaceExtra
import com.looker.droidify.widget.CursorRecyclerAdapter import com.looker.droidify.widget.CursorRecyclerAdapter
import kotlin.system.measureTimeMillis
import com.google.android.material.R as MaterialR import com.google.android.material.R as MaterialR
class AppListAdapter( class AppListAdapter(
private val source: AppListFragment.Source, private val source: AppListFragment.Source,
private val onClick: (ProductItem) -> Unit private val onClick: (packageName: String) -> Unit,
) : CursorRecyclerAdapter<AppListAdapter.ViewType, RecyclerView.ViewHolder>() { ) : CursorRecyclerAdapter<AppListAdapter.ViewType, RecyclerView.ViewHolder>() {
enum class ViewType { PRODUCT, LOADING, EMPTY } enum class ViewType { PRODUCT, LOADING, EMPTY }
private class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private inner class ProductViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val name = itemView.findViewById<TextView>(R.id.name)!! val name = itemView.findViewById<TextView>(R.id.name)!!
val status = itemView.findViewById<TextView>(R.id.status)!! val status = itemView.findViewById<TextView>(R.id.status)!!
val summary = itemView.findViewById<TextView>(R.id.summary)!! val summary = itemView.findViewById<TextView>(R.id.summary)!!
val icon = itemView.findViewById<ShapeableImageView>(R.id.icon)!! val icon = itemView.findViewById<ShapeableImageView>(R.id.icon)!!
init {
itemView.setOnClickListener {
log(measureTimeMillis { onClick(getPackageName(absoluteAdapterPosition)) }, "Bench")
}
}
} }
private class LoadingViewHolder(context: Context) : private class LoadingViewHolder(context: Context) :
@ -82,12 +91,14 @@ class AppListAdapter(
} }
} }
var repositories: Map<Long, Repository> = emptyMap() private val repositories: HashMap<Long, Repository> = HashMap()
@SuppressLint("NotifyDataSetChanged")
set(value) { fun updateRepos(repos: List<Repository>) {
field = value repos.forEach {
notifyDataSetChanged() repositories[it.id] = it
} }
notifyDataSetChanged()
}
var emptyText: String = "" var emptyText: String = ""
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
@ -117,24 +128,31 @@ class AppListAdapter(
} }
} }
private fun getPackageName(position: Int): String {
return Database.ProductAdapter.transformPackageName(moveTo(position.coerceAtLeast(0)))
}
private fun getProductItem(position: Int): ProductItem { private fun getProductItem(position: Int): ProductItem {
return Database.ProductAdapter.transformItem(moveTo(position.coerceAtLeast(0))) return Database.ProductAdapter.transformItem(moveTo(position.coerceAtLeast(0)))
} }
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: ViewType viewType: ViewType,
): RecyclerView.ViewHolder { ): RecyclerView.ViewHolder {
return when (viewType) { return when (viewType) {
ViewType.PRODUCT -> ProductViewHolder(parent.inflate(R.layout.product_item)).apply { ViewType.PRODUCT -> ProductViewHolder(parent.inflate(R.layout.product_item))
itemView.setOnClickListener { onClick(getProductItem(absoluteAdapterPosition)) }
}
ViewType.LOADING -> LoadingViewHolder(parent.context) ViewType.LOADING -> LoadingViewHolder(parent.context)
ViewType.EMPTY -> EmptyViewHolder(parent.context) ViewType.EMPTY -> EmptyViewHolder(parent.context)
} }
} }
private var updateBackground: ColorStateList? = null
private var updateForeground: ColorStateList? = null
private var installedBackground: ColorStateList? = null
private var installedForeground: ColorStateList? = null
private var defaultForeground: ColorStateList? = null
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemEnumViewType(position)) { when (getItemEnumViewType(position)) {
ViewType.PRODUCT -> { ViewType.PRODUCT -> {
@ -142,9 +160,9 @@ class AppListAdapter(
val productItem = getProductItem(position) val productItem = getProductItem(position)
holder.name.text = productItem.name holder.name.text = productItem.name
holder.summary.text = productItem.summary holder.summary.text = productItem.summary
holder.summary.isVisible = holder.summary.isVisible = productItem.summary.isNotEmpty()
productItem.summary.isNotEmpty() && productItem.name != productItem.summary && productItem.name != productItem.summary
val repository: Repository? = repositories[productItem.repositoryId] val repository = repositories[productItem.repositoryId]
if (repository != null) { if (repository != null) {
val iconUrl = productItem.icon(view = holder.icon, repository = repository) val iconUrl = productItem.icon(view = holder.icon, repository = repository)
holder.icon.load(iconUrl) { holder.icon.load(iconUrl) {
@ -161,28 +179,38 @@ class AppListAdapter(
val isInstalled = productItem.installedVersion.nullIfEmpty() != null val isInstalled = productItem.installedVersion.nullIfEmpty() != null
when { when {
productItem.canUpdate -> { productItem.canUpdate -> {
backgroundTintList = if (updateBackground == null) {
context.getColorFromAttr(MaterialR.attr.colorTertiaryContainer) updateBackground =
setTextColor( context.getColorFromAttr(MaterialR.attr.colorTertiaryContainer)
context.getColorFromAttr(MaterialR.attr.colorOnTertiaryContainer) }
) if (updateForeground == null) {
updateForeground =
context.getColorFromAttr(MaterialR.attr.colorOnTertiaryContainer)
}
backgroundTintList = updateBackground
setTextColor(updateForeground)
} }
isInstalled -> { isInstalled -> {
backgroundTintList = if (installedBackground == null) {
context.getColorFromAttr(MaterialR.attr.colorSecondaryContainer) installedBackground =
setTextColor( context.getColorFromAttr(MaterialR.attr.colorSecondaryContainer)
context.getColorFromAttr(MaterialR.attr.colorOnSecondaryContainer) }
) if (installedForeground == null) {
installedForeground =
context.getColorFromAttr(MaterialR.attr.colorOnSecondaryContainer)
}
backgroundTintList = installedBackground
setTextColor(installedForeground)
} }
else -> { else -> {
setPadding(0, 0, 0, 0) setPadding(0, 0, 0, 0)
setTextColor( if (defaultForeground == null) {
holder.status.context.getColorFromAttr( defaultForeground =
MaterialR.attr.colorOnBackground context.getColorFromAttr(MaterialR.attr.colorOnBackground)
) }
) setTextColor(defaultForeground)
background = null background = null
return@with return@with
} }
@ -191,9 +219,9 @@ class AppListAdapter(
6.dp.let { setPadding(it, it, it, it) } 6.dp.let { setPadding(it, it, it, it) }
} }
val enabled = productItem.compatible || productItem.installedVersion.isNotEmpty() val enabled = productItem.compatible || productItem.installedVersion.isNotEmpty()
sequenceOf(holder.name, holder.status, holder.summary).forEach { holder.name.isEnabled = enabled
it.isEnabled = enabled holder.status.isEnabled = enabled
} holder.summary.isEnabled = enabled
} }
ViewType.LOADING -> { ViewType.LOADING -> {
@ -204,6 +232,6 @@ class AppListAdapter(
holder as EmptyViewHolder holder as EmptyViewHolder
holder.text.text = emptyText holder.text.text = emptyText
} }
}::class }
} }
} }

View File

@ -14,19 +14,19 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.looker.droidify.utility.common.Scroller
import com.looker.droidify.R import com.looker.droidify.R
import com.looker.droidify.R.string as stringRes import com.looker.droidify.database.CursorOwner
import com.looker.droidify.databinding.RecyclerViewWithFabBinding
import com.looker.droidify.model.ProductItem
import com.looker.droidify.utility.common.Scroller
import com.looker.droidify.utility.common.extension.dp import com.looker.droidify.utility.common.extension.dp
import com.looker.droidify.utility.common.extension.isFirstItemVisible import com.looker.droidify.utility.common.extension.isFirstItemVisible
import com.looker.droidify.utility.common.extension.systemBarsMargin import com.looker.droidify.utility.common.extension.systemBarsMargin
import com.looker.droidify.utility.common.extension.systemBarsPadding import com.looker.droidify.utility.common.extension.systemBarsPadding
import com.looker.droidify.model.ProductItem import com.looker.droidify.utility.extension.mainActivity
import com.looker.droidify.database.CursorOwner
import com.looker.droidify.databinding.RecyclerViewWithFabBinding
import com.looker.droidify.utility.extension.screenActivity
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import com.looker.droidify.R.string as stringRes
@AndroidEntryPoint @AndroidEntryPoint
class AppListFragment() : Fragment(), CursorOwner.Callback { class AppListFragment() : Fragment(), CursorOwner.Callback {
@ -46,7 +46,7 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
val titleResId: Int, val titleResId: Int,
val sections: Boolean, val sections: Boolean,
val order: Boolean, val order: Boolean,
val updateAll: Boolean val updateAll: Boolean,
) { ) {
AVAILABLE(stringRes.available, true, true, false), AVAILABLE(stringRes.available, true, true, false),
INSTALLED(stringRes.installed, false, true, false), INSTALLED(stringRes.installed, false, true, false),
@ -63,14 +63,15 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
get() = requireArguments().getString(EXTRA_SOURCE)!!.let(Source::valueOf) get() = requireArguments().getString(EXTRA_SOURCE)!!.let(Source::valueOf)
private lateinit var recyclerView: RecyclerView private lateinit var recyclerView: RecyclerView
private lateinit var recyclerViewAdapter: AppListAdapter private lateinit var appListAdapter: AppListAdapter
private var scroller: Scroller? = null
private var shortAnimationDuration: Int = 0 private var shortAnimationDuration: Int = 0
private var layoutManagerState: Parcelable? = null private var layoutManagerState: Parcelable? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
_binding = RecyclerViewWithFabBinding.inflate(inflater, container, false) _binding = RecyclerViewWithFabBinding.inflate(inflater, container, false)
@ -83,10 +84,8 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
isMotionEventSplittingEnabled = false isMotionEventSplittingEnabled = false
setHasFixedSize(true) setHasFixedSize(true)
recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30) recycledViewPool.setMaxRecycledViews(AppListAdapter.ViewType.PRODUCT.ordinal, 30)
recyclerViewAdapter = AppListAdapter(source) { appListAdapter = AppListAdapter(source, mainActivity::navigateProduct)
screenActivity.navigateProduct(it.packageName) adapter = appListAdapter
}
adapter = recyclerViewAdapter
systemBarsPadding() systemBarsPadding()
} }
val fab = binding.scrollUp val fab = binding.scrollUp
@ -103,11 +102,13 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
} }
systemBarsMargin(16.dp) systemBarsMargin(16.dp)
} else { } else {
text = "" text = null
setIconResource(R.drawable.arrow_up) setIconResource(R.drawable.arrow_up)
setOnClickListener { setOnClickListener {
val scroller = Scroller(requireContext()) if (scroller == null) {
scroller.targetPosition = 0 scroller = Scroller(requireContext())
}
scroller!!.targetPosition = 0
recyclerView.layoutManager?.startSmoothScroll(scroller) recyclerView.layoutManager?.startSmoothScroll(scroller)
} }
alpha = 0f alpha = 0f
@ -138,7 +139,7 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
repeatOnLifecycle(Lifecycle.State.RESUMED) { repeatOnLifecycle(Lifecycle.State.RESUMED) {
launch { launch {
viewModel.reposStream.collect { repos -> viewModel.reposStream.collect { repos ->
recyclerViewAdapter.repositories = repos.associateBy { it.id } appListAdapter.updateRepos(repos)
} }
} }
launch { launch {
@ -160,12 +161,13 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
super.onDestroyView() super.onDestroyView()
viewModel.syncConnection.unbind(requireContext()) viewModel.syncConnection.unbind(requireContext())
_binding = null _binding = null
screenActivity.cursorOwner.detach(this) scroller = null
mainActivity.cursorOwner.detach(this)
} }
override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) { override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) {
recyclerViewAdapter.cursor = cursor appListAdapter.cursor = cursor
recyclerViewAdapter.emptyText = when { appListAdapter.emptyText = when {
cursor == null -> "" cursor == null -> ""
viewModel.searchQuery.value.isNotEmpty() -> { viewModel.searchQuery.value.isNotEmpty() -> {
getString(stringRes.no_matching_applications_found) getString(stringRes.no_matching_applications_found)
@ -197,7 +199,7 @@ class AppListFragment() : Fragment(), CursorOwner.Callback {
private fun updateRequest() { private fun updateRequest() {
if (view != null) { if (view != null) {
screenActivity.cursorOwner.attach(this, viewModel.request(source)) mainActivity.cursorOwner.attach(this, viewModel.request(source))
} }
} }
} }

View File

@ -2,40 +2,52 @@ package com.looker.droidify.ui.appList
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.looker.droidify.utility.common.extension.asStateFlow import com.looker.droidify.database.CursorOwner
import com.looker.droidify.database.CursorOwner.Request.Available
import com.looker.droidify.database.CursorOwner.Request.Installed
import com.looker.droidify.database.CursorOwner.Request.Updates
import com.looker.droidify.database.Database
import com.looker.droidify.datastore.SettingsRepository import com.looker.droidify.datastore.SettingsRepository
import com.looker.droidify.datastore.get import com.looker.droidify.datastore.get
import com.looker.droidify.datastore.model.SortOrder import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.model.ProductItem import com.looker.droidify.model.ProductItem
import com.looker.droidify.model.ProductItem.Section.All import com.looker.droidify.model.ProductItem.Section.All
import com.looker.droidify.database.CursorOwner
import com.looker.droidify.database.Database
import com.looker.droidify.service.Connection import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService import com.looker.droidify.service.SyncService
import com.looker.droidify.utility.common.extension.asStateFlow
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel @HiltViewModel
class AppListViewModel class AppListViewModel
@Inject constructor( @Inject constructor(
settingsRepository: SettingsRepository settingsRepository: SettingsRepository,
) : ViewModel() { ) : ViewModel() {
private val skipSignatureStream = settingsRepository
.get { ignoreSignature }
.asStateFlow(false)
val sortOrderFlow = settingsRepository
.get { sortOrder }
.asStateFlow(SortOrder.UPDATED)
val reposStream = Database.RepositoryAdapter val reposStream = Database.RepositoryAdapter
.getAllStream() .getAllStream()
.asStateFlow(emptyList()) .asStateFlow(emptyList())
val showUpdateAllButton = Database.ProductAdapter @OptIn(ExperimentalCoroutinesApi::class)
.getUpdatesStream() val showUpdateAllButton = skipSignatureStream.flatMapConcat { skip ->
.map { it.isNotEmpty() } Database.ProductAdapter
.asStateFlow(false) .getUpdatesStream(skip)
.map { it.isNotEmpty() }
val sortOrderFlow = settingsRepository.get { sortOrder } }.asStateFlow(false)
.asStateFlow(SortOrder.UPDATED)
private val sections = MutableStateFlow<ProductItem.Section>(All) private val sections = MutableStateFlow<ProductItem.Section>(All)
@ -51,22 +63,23 @@ class AppListViewModel
fun request(source: AppListFragment.Source): CursorOwner.Request { fun request(source: AppListFragment.Source): CursorOwner.Request {
return when (source) { return when (source) {
AppListFragment.Source.AVAILABLE -> CursorOwner.Request.ProductsAvailable( AppListFragment.Source.AVAILABLE -> Available(
searchQuery.value, searchQuery = searchQuery.value,
sections.value, section = sections.value,
sortOrderFlow.value order = sortOrderFlow.value,
) )
AppListFragment.Source.INSTALLED -> CursorOwner.Request.ProductsInstalled( AppListFragment.Source.INSTALLED -> Installed(
searchQuery.value, searchQuery = searchQuery.value,
sections.value, section = sections.value,
sortOrderFlow.value order = sortOrderFlow.value,
) )
AppListFragment.Source.UPDATES -> CursorOwner.Request.ProductsUpdates( AppListFragment.Source.UPDATES -> Updates(
searchQuery.value, searchQuery = searchQuery.value,
sections.value, section = sections.value,
sortOrderFlow.value order = sortOrderFlow.value,
skipSignatureCheck = skipSignatureStream.value,
) )
} }
} }

View File

@ -4,7 +4,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.load import coil3.load
import com.looker.droidify.databinding.ProductItemBinding import com.looker.droidify.databinding.ProductItemBinding
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.model.Repository import com.looker.droidify.model.Repository

View File

@ -15,7 +15,7 @@ import com.looker.droidify.R
import com.looker.droidify.utility.common.extension.systemBarsPadding import com.looker.droidify.utility.common.extension.systemBarsPadding
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.ui.ScreenFragment import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.utility.extension.screenActivity import com.looker.droidify.utility.extension.mainActivity
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -43,7 +43,7 @@ class FavouritesFragment : ScreenFragment() {
isVerticalScrollBarEnabled = false isVerticalScrollBarEnabled = false
setHasFixedSize(true) setHasFixedSize(true)
recyclerViewAdapter = recyclerViewAdapter =
FavouriteFragmentAdapter { screenActivity.navigateProduct(it) } FavouriteFragmentAdapter { mainActivity.navigateProduct(it) }
this.adapter = recyclerViewAdapter this.adapter = recyclerViewAdapter
systemBarsPadding(includeFab = false) systemBarsPadding(includeFab = false)
recyclerView = this recyclerView = this
@ -74,6 +74,6 @@ class FavouritesFragment : ScreenFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
screenActivity.onToolbarCreated(toolbar) mainActivity.onToolbarCreated(toolbar)
} }
} }

View File

@ -1,21 +1,19 @@
package com.looker.droidify.ui.favourites package com.looker.droidify.ui.favourites
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import com.looker.droidify.database.Database
import com.looker.droidify.utility.common.extension.asStateFlow
import com.looker.droidify.datastore.SettingsRepository import com.looker.droidify.datastore.SettingsRepository
import com.looker.droidify.datastore.get import com.looker.droidify.datastore.get
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.database.Database import com.looker.droidify.utility.common.extension.asStateFlow
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import javax.inject.Inject
@HiltViewModel @HiltViewModel
class FavouritesViewModel @Inject constructor( class FavouritesViewModel @Inject constructor(
private val settingsRepository: SettingsRepository settingsRepository: SettingsRepository,
) : ViewModel() { ) : ViewModel() {
val favouriteApps: StateFlow<List<List<Product>>> = val favouriteApps: StateFlow<List<List<Product>>> =
@ -27,9 +25,4 @@ class FavouritesViewModel @Inject constructor(
} }
}.asStateFlow(emptyList()) }.asStateFlow(emptyList())
fun updateFavourites(packageName: String) {
viewModelScope.launch {
settingsRepository.toggleFavourites(packageName)
}
}
} }

View File

@ -15,26 +15,25 @@ import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.looker.droidify.utility.common.extension.clipboardManager import com.looker.droidify.R
import com.looker.droidify.utility.common.extension.get
import com.looker.droidify.utility.common.extension.getMutatedIcon
import com.looker.droidify.utility.common.nullIfEmpty
import com.looker.droidify.model.Repository
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.databinding.EditRepositoryBinding import com.looker.droidify.databinding.EditRepositoryBinding
import com.looker.droidify.model.Repository
import com.looker.droidify.network.Downloader
import com.looker.droidify.network.NetworkResponse
import com.looker.droidify.service.Connection import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService import com.looker.droidify.service.SyncService
import com.looker.droidify.ui.Message import com.looker.droidify.ui.Message
import com.looker.droidify.ui.MessageDialog import com.looker.droidify.ui.MessageDialog
import com.looker.droidify.ui.ScreenFragment import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.utility.extension.screenActivity import com.looker.droidify.utility.common.extension.clipboardManager
import com.looker.droidify.network.Downloader import com.looker.droidify.utility.common.extension.get
import com.looker.droidify.network.NetworkResponse import com.looker.droidify.utility.common.extension.getMutatedIcon
import com.looker.droidify.utility.common.nullIfEmpty
import com.looker.droidify.utility.extension.mainActivity
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.net.URI import java.net.URI
@ -44,7 +43,6 @@ import java.nio.charset.Charset
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.min import kotlin.math.min
import com.looker.droidify.R
import com.looker.droidify.R.string as stringRes import com.looker.droidify.R.string as stringRes
@AndroidEntryPoint @AndroidEntryPoint
@ -82,11 +80,9 @@ class EditRepositoryFragment() : ScreenFragment() {
syncConnection.bind(requireContext()) syncConnection.bind(requireContext())
screenActivity.onToolbarCreated(toolbar) mainActivity.onToolbarCreated(toolbar)
toolbar.title = toolbar.title =
getString( getString(if (repoId != null) stringRes.edit_repository else stringRes.add_repository)
if (repoId != null) stringRes.edit_repository else stringRes.add_repository
)
saveMenuItem = toolbar.menu.add(stringRes.save) saveMenuItem = toolbar.menu.add(stringRes.save)
.setIcon(toolbar.context.getMutatedIcon(R.drawable.ic_save)) .setIcon(toolbar.context.getMutatedIcon(R.drawable.ic_save))
@ -240,6 +236,8 @@ class EditRepositoryFragment() : ScreenFragment() {
saveMenuItem = null saveMenuItem = null
syncConnection.unbind(requireContext()) syncConnection.unbind(requireContext())
checkJob?.cancel()
checkJob = null
_binding = null _binding = null
} }
@ -394,22 +392,22 @@ class EditRepositoryFragment() : ScreenFragment() {
} }
private suspend fun checkAddress( private suspend fun checkAddress(
address: String, rawAddress: String,
authentication: String authentication: String
): String? = coroutineScope { ): String? = coroutineScope {
checkInProgress = true checkInProgress = true
invalidateState() invalidateState()
val allAddresses = addressSuffixes.map { "$address/$it" } + address val allAddresses = addressSuffixes.map { "$rawAddress/$it" } + rawAddress
val pathCheck = allAddresses.map { allAddresses
async { .sortedBy { it.length }
downloader.headCall( .forEach { address ->
url = "$it/index-v1.jar", val response = downloader.headCall(
url = "$address/index-v1.jar",
headers = { authentication(authentication) } headers = { authentication(authentication) }
) is NetworkResponse.Success )
if (response is NetworkResponse.Success) return@coroutineScope address
} }
} null
val indexOfValidAddress = pathCheck.awaitAll().indexOf(true)
allAddresses[indexOfValidAddress].nullIfEmpty()
} }
private fun onSaveRepositoryProceedInvalidate( private fun onSaveRepositoryProceedInvalidate(
@ -431,7 +429,7 @@ class EditRepositoryFragment() : ScreenFragment() {
if (repositoryId == null && changedRepository.enabled) { if (repositoryId == null && changedRepository.enabled) {
binder.sync(changedRepository) binder.sync(changedRepository)
} }
screenActivity.onBackPressedDispatcher.onBackPressed() mainActivity.onBackPressedDispatcher.onBackPressed()
} }
} else { } else {
invalidateState() invalidateState()
@ -470,6 +468,6 @@ class EditRepositoryFragment() : ScreenFragment() {
const val EXTRA_REPOSITORY_ID = "repositoryId" const val EXTRA_REPOSITORY_ID = "repositoryId"
const val EXTRA_REPOSITORY_ADDRESS = "repositoryAddress" const val EXTRA_REPOSITORY_ADDRESS = "repositoryAddress"
val addressSuffixes = listOf("fdroid/repo", "repo") val addressSuffixes = arrayOf("fdroid/repo", "repo")
} }
} }

View File

@ -15,7 +15,7 @@ import com.looker.droidify.databinding.RecyclerViewWithFabBinding
import com.looker.droidify.service.Connection import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService import com.looker.droidify.service.SyncService
import com.looker.droidify.ui.ScreenFragment import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.utility.extension.screenActivity import com.looker.droidify.utility.extension.mainActivity
import com.looker.droidify.widget.addDivider import com.looker.droidify.widget.addDivider
class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback { class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback {
@ -36,7 +36,7 @@ class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback {
binding.scrollUp.apply { binding.scrollUp.apply {
setIconResource(R.drawable.ic_add) setIconResource(R.drawable.ic_add)
setText(R.string.add_repository) setText(R.string.add_repository)
setOnClickListener { screenActivity.navigateAddRepository() } setOnClickListener { mainActivity.navigateAddRepository() }
systemBarsMargin(16.dp) systemBarsMargin(16.dp)
} }
binding.recyclerView.apply { binding.recyclerView.apply {
@ -44,7 +44,7 @@ class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback {
isMotionEventSplittingEnabled = false isMotionEventSplittingEnabled = false
setHasFixedSize(true) setHasFixedSize(true)
adapter = RepositoriesAdapter( adapter = RepositoriesAdapter(
navigate = { screenActivity.navigateRepository(it.id) } navigate = { mainActivity.navigateRepository(it.id) }
) { repository, isEnabled -> ) { repository, isEnabled ->
repository.enabled != isEnabled && repository.enabled != isEnabled &&
syncConnection.binder?.setEnabled(repository, isEnabled) == true syncConnection.binder?.setEnabled(repository, isEnabled) == true
@ -79,8 +79,8 @@ class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
syncConnection.bind(requireContext()) syncConnection.bind(requireContext())
screenActivity.cursorOwner.attach(this, CursorOwner.Request.Repositories) mainActivity.cursorOwner.attach(this, CursorOwner.Request.Repositories)
screenActivity.onToolbarCreated(toolbar) mainActivity.onToolbarCreated(toolbar)
toolbar.title = getString(R.string.repositories) toolbar.title = getString(R.string.repositories)
} }
@ -89,7 +89,7 @@ class RepositoriesFragment : ScreenFragment(), CursorOwner.Callback {
_binding = null _binding = null
syncConnection.unbind(requireContext()) syncConnection.unbind(requireContext())
screenActivity.cursorOwner.detach(this) mainActivity.cursorOwner.detach(this)
} }
override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) { override fun onCursorData(request: CursorOwner.Request, cursor: Cursor?) {

View File

@ -22,7 +22,7 @@ import com.looker.droidify.databinding.RepositoryPageBinding
import com.looker.droidify.ui.Message import com.looker.droidify.ui.Message
import com.looker.droidify.ui.MessageDialog import com.looker.droidify.ui.MessageDialog
import com.looker.droidify.ui.ScreenFragment import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.utility.extension.screenActivity import com.looker.droidify.utility.extension.mainActivity
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -53,7 +53,7 @@ class RepositoryFragment() : ScreenFragment() {
super.onCreateView(inflater, container, savedInstanceState) super.onCreateView(inflater, container, savedInstanceState)
_binding = RepositoryPageBinding.inflate(inflater, container, false) _binding = RepositoryPageBinding.inflate(inflater, container, false)
viewModel.bindService(requireContext()) viewModel.bindService(requireContext())
screenActivity.onToolbarCreated(toolbar) mainActivity.onToolbarCreated(toolbar)
toolbar.title = getString(stringRes.repository) toolbar.title = getString(stringRes.repository)
val scroll = NestedScrollView(binding.root.context) val scroll = NestedScrollView(binding.root.context)
scroll.addView(binding.root) scroll.addView(binding.root)
@ -149,7 +149,7 @@ class RepositoryFragment() : ScreenFragment() {
} }
editRepoButton.setOnClickListener { editRepoButton.setOnClickListener {
screenActivity.navigateEditRepository(viewModel.id) mainActivity.navigateEditRepository(viewModel.id)
} }
deleteRepoButton.setOnClickListener { deleteRepoButton.setOnClickListener {

View File

@ -2,6 +2,7 @@ package com.looker.droidify.ui.settings
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -23,13 +24,11 @@ import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import com.looker.droidify.utility.common.SdkCheck import com.looker.droidify.BuildConfig
import com.looker.droidify.utility.common.extension.getColorFromAttr import com.looker.droidify.R
import com.looker.droidify.utility.common.extension.homeAsUp import com.looker.droidify.databinding.EnumTypeBinding
import com.looker.droidify.utility.common.extension.systemBarsPadding import com.looker.droidify.databinding.SettingsPageBinding
import com.looker.droidify.utility.common.extension.updateAsMutable import com.looker.droidify.databinding.SwitchTypeBinding
import com.looker.droidify.utility.common.isIgnoreBatteryEnabled
import com.looker.droidify.utility.common.requestBatteryFreedom
import com.looker.droidify.datastore.Settings import com.looker.droidify.datastore.Settings
import com.looker.droidify.datastore.extension.autoSyncName import com.looker.droidify.datastore.extension.autoSyncName
import com.looker.droidify.datastore.extension.installerName import com.looker.droidify.datastore.extension.installerName
@ -38,22 +37,25 @@ import com.looker.droidify.datastore.extension.themeName
import com.looker.droidify.datastore.extension.toTime import com.looker.droidify.datastore.extension.toTime
import com.looker.droidify.datastore.model.AutoSync import com.looker.droidify.datastore.model.AutoSync
import com.looker.droidify.datastore.model.InstallerType import com.looker.droidify.datastore.model.InstallerType
import com.looker.droidify.datastore.model.LegacyInstallerComponent
import com.looker.droidify.datastore.model.ProxyType import com.looker.droidify.datastore.model.ProxyType
import com.looker.droidify.datastore.model.Theme import com.looker.droidify.datastore.model.Theme
import com.looker.droidify.BuildConfig import com.looker.droidify.utility.common.SdkCheck
import com.looker.droidify.databinding.EnumTypeBinding import com.looker.droidify.utility.common.extension.getColorFromAttr
import com.looker.droidify.databinding.SettingsPageBinding import com.looker.droidify.utility.common.extension.homeAsUp
import com.looker.droidify.databinding.SwitchTypeBinding import com.looker.droidify.utility.common.extension.systemBarsPadding
import com.looker.droidify.utility.common.extension.updateAsMutable
import com.looker.droidify.utility.common.isIgnoreBatteryEnabled
import com.looker.droidify.utility.common.requestBatteryFreedom
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Locale import java.util.Locale
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.Duration.Companion.days import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours import kotlin.time.Duration.Companion.hours
import com.google.android.material.R as MaterialR import com.google.android.material.R as MaterialR
import com.looker.droidify.R import androidx.core.net.toUri
@AndroidEntryPoint @AndroidEntryPoint
class SettingsFragment : Fragment() { class SettingsFragment : Fragment() {
@ -119,9 +121,7 @@ class SettingsFragment : Fragment() {
): View { ): View {
_binding = SettingsPageBinding.inflate(inflater, container, false) _binding = SettingsPageBinding.inflate(inflater, container, false)
binding.nestedScrollView.systemBarsPadding() binding.nestedScrollView.systemBarsPadding()
if (requireContext().isIgnoreBatteryEnabled()) { viewModel.toggleBackgroundAccess(requireContext().isIgnoreBatteryEnabled())
viewModel.allowBackground()
}
val toolbar = binding.toolbar val toolbar = binding.toolbar
toolbar.navigationIcon = toolbar.context.homeAsUp toolbar.navigationIcon = toolbar.context.homeAsUp
toolbar.setNavigationOnClickListener { activity?.onBackPressedDispatcher?.onBackPressed() } toolbar.setNavigationOnClickListener { activity?.onBackPressedDispatcher?.onBackPressed() }
@ -233,6 +233,56 @@ class SettingsFragment : Fragment() {
onClick = { viewModel.setInstaller(requireContext(), it) } onClick = { viewModel.setInstaller(requireContext(), it) }
) )
} }
val pm = requireContext().packageManager
legacyInstallerComponent.connect(
titleText = getString(R.string.legacyInstallerComponent),
setting = viewModel.getSetting { legacyInstallerComponent },
map = {
when (it) {
is LegacyInstallerComponent.Component -> {
val component = it
val appLabel = runCatching {
val info = pm.getApplicationInfo(component.clazz, 0)
pm.getApplicationLabel(info).toString()
}.getOrElse { component.clazz }
"$appLabel (${component.activity})"
}
LegacyInstallerComponent.Unspecified -> getString(R.string.unspecified)
LegacyInstallerComponent.AlwaysChoose -> getString(R.string.always_choose)
null -> getString(R.string.unspecified)
}
},
) { component, valueToString ->
val installerOptions = run {
var contentProtocol = "content://"
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE).apply {
setDataAndType(contentProtocol.toUri(), "application/vnd.android.package-archive")
}
val activities = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
listOf(
LegacyInstallerComponent.Unspecified,
LegacyInstallerComponent.AlwaysChoose
) + activities.map {
LegacyInstallerComponent.Component(
clazz = it.activityInfo.packageName,
activity = it.activityInfo.name,
)
}
}
addSingleCorrectDialog(
initialValue = component ?: LegacyInstallerComponent.Unspecified,
values = installerOptions,
title = R.string.legacyInstallerComponent,
iconRes = R.drawable.ic_apk_install,
valueToString = valueToString,
onClick = { viewModel.setLegacyInstallerComponentComponent(it) },
)
}
incompatibleUpdates.connect(
titleText = getString(R.string.incompatible_versions),
contentText = getString(R.string.incompatible_versions_summary),
setting = viewModel.getInitialSetting { incompatibleVersions },
)
proxyType.connect( proxyType.connect(
titleText = getString(R.string.proxy_type), titleText = getString(R.string.proxy_type),
setting = viewModel.getSetting { proxy.type }, setting = viewModel.getSetting { proxy.type },
@ -283,6 +333,7 @@ class SettingsFragment : Fragment() {
exportRepos.title.text = getString(R.string.export_repos_title) exportRepos.title.text = getString(R.string.export_repos_title)
exportRepos.content.text = getString(R.string.export_repos_DESC) exportRepos.content.text = getString(R.string.export_repos_DESC)
allowBackgroundWork.root.isVisible = false
allowBackgroundWork.title.text = getString(R.string.require_background_access) allowBackgroundWork.title.text = getString(R.string.require_background_access)
allowBackgroundWork.content.text = allowBackgroundWork.content.text =
getString(R.string.require_background_access_DESC) getString(R.string.require_background_access_DESC)
@ -315,8 +366,8 @@ class SettingsFragment : Fragment() {
launch { launch {
viewModel.settingsFlow.collect { setting -> viewModel.settingsFlow.collect { setting ->
updateSettings(setting) updateSettings(setting)
binding.allowBackgroundWork.root.isVisible = !viewModel.backgroundTask.first() binding.allowBackgroundWork.root.isVisible =
&& setting.autoSync != AutoSync.NEVER !viewModel.isBackgroundAllowed && setting.autoSync != AutoSync.NEVER
} }
} }
} }
@ -326,9 +377,7 @@ class SettingsFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (requireContext().isIgnoreBatteryEnabled()) { viewModel.toggleBackgroundAccess(requireContext().isIgnoreBatteryEnabled())
viewModel.allowBackground()
}
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -376,9 +425,7 @@ class SettingsFragment : Fragment() {
} }
allowBackgroundWork.root.setOnClickListener { allowBackgroundWork.root.setOnClickListener {
requireContext().requestBatteryFreedom() requireContext().requestBatteryFreedom()
if (requireContext().isIgnoreBatteryEnabled()) { viewModel.toggleBackgroundAccess(requireContext().isIgnoreBatteryEnabled())
viewModel.allowBackground()
}
} }
creditFoxy.root.setOnClickListener { creditFoxy.root.setOnClickListener {
openLink(FOXY_DROID_URL) openLink(FOXY_DROID_URL)
@ -395,6 +442,9 @@ class SettingsFragment : Fragment() {
proxyHost.root.isVisible = allowProxies proxyHost.root.isVisible = allowProxies
proxyPort.root.isVisible = allowProxies proxyPort.root.isVisible = allowProxies
forceCleanUp.root.isVisible = settings.cleanUpInterval == Duration.INFINITE forceCleanUp.root.isVisible = settings.cleanUpInterval == Duration.INFINITE
val useLegacyInstaller = settings.installerType == InstallerType.LEGACY
legacyInstallerComponent.root.isVisible = useLegacyInstaller
} }
} }

View File

@ -7,35 +7,35 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.looker.droidify.R
import com.looker.droidify.database.Database
import com.looker.droidify.database.RepositoryExporter
import com.looker.droidify.datastore.Settings import com.looker.droidify.datastore.Settings
import com.looker.droidify.datastore.SettingsRepository import com.looker.droidify.datastore.SettingsRepository
import com.looker.droidify.datastore.get import com.looker.droidify.datastore.get
import com.looker.droidify.datastore.model.AutoSync import com.looker.droidify.datastore.model.AutoSync
import com.looker.droidify.datastore.model.InstallerType import com.looker.droidify.datastore.model.InstallerType
import com.looker.droidify.datastore.model.InstallerType.* import com.looker.droidify.datastore.model.InstallerType.ROOT
import com.looker.droidify.datastore.model.InstallerType.SHIZUKU
import com.looker.droidify.datastore.model.LegacyInstallerComponent
import com.looker.droidify.datastore.model.ProxyType import com.looker.droidify.datastore.model.ProxyType
import com.looker.droidify.datastore.model.Theme import com.looker.droidify.datastore.model.Theme
import com.looker.droidify.database.Database
import com.looker.droidify.database.RepositoryExporter
import com.looker.droidify.work.CleanUpWorker
import com.looker.droidify.installer.installers.isMagiskGranted import com.looker.droidify.installer.installers.isMagiskGranted
import com.looker.droidify.installer.installers.isShizukuAlive import com.looker.droidify.installer.installers.isShizukuAlive
import com.looker.droidify.installer.installers.isShizukuGranted import com.looker.droidify.installer.installers.isShizukuGranted
import com.looker.droidify.installer.installers.isShizukuInstalled import com.looker.droidify.installer.installers.isShizukuInstalled
import com.looker.droidify.installer.installers.requestPermissionListener import com.looker.droidify.installer.installers.requestPermissionListener
import com.looker.droidify.work.CleanUpWorker
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration import kotlin.time.Duration
import com.looker.droidify.R
@HiltViewModel @HiltViewModel
class SettingsViewModel class SettingsViewModel
@ -49,8 +49,8 @@ class SettingsViewModel
} }
val settingsFlow get() = settingsRepository.data val settingsFlow get() = settingsRepository.data
private val _backgroundTask = MutableStateFlow(false) var isBackgroundAllowed = true
val backgroundTask = _backgroundTask.asStateFlow() private set
private val _snackbarStringId = MutableSharedFlow<Int>() private val _snackbarStringId = MutableSharedFlow<Int>()
val snackbarStringId = _snackbarStringId.asSharedFlow() val snackbarStringId = _snackbarStringId.asSharedFlow()
@ -59,10 +59,8 @@ class SettingsViewModel
fun <T> getInitialSetting(block: Settings.() -> T): Flow<T> = initialSetting.map { it.block() } fun <T> getInitialSetting(block: Settings.() -> T): Flow<T> = initialSetting.map { it.block() }
fun allowBackground() { fun toggleBackgroundAccess(enable: Boolean) {
viewModelScope.launch { isBackgroundAllowed = enable
_backgroundTask.emit(true)
}
} }
fun setLanguage(language: String) { fun setLanguage(language: String) {
@ -172,7 +170,7 @@ class SettingsViewModel
} else if (isShizukuGranted()) { } else if (isShizukuGranted()) {
settingsRepository.setInstallerType(installerType) settingsRepository.setInstallerType(installerType)
} else if (!isShizukuGranted()) { } else if (!isShizukuGranted()) {
if(requestPermissionListener()) { if (requestPermissionListener()) {
settingsRepository.setInstallerType(installerType) settingsRepository.setInstallerType(installerType)
} }
} }
@ -194,6 +192,12 @@ class SettingsViewModel
} }
} }
fun setLegacyInstallerComponentComponent(component: LegacyInstallerComponent?) {
viewModelScope.launch {
settingsRepository.setLegacyInstallerComponent(component)
}
}
fun exportSettings(file: Uri) { fun exportSettings(file: Uri) {
viewModelScope.launch { viewModelScope.launch {
settingsRepository.export(file) settingsRepository.export(file)

View File

@ -14,6 +14,7 @@ import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
@ -28,24 +29,24 @@ import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel import com.google.android.material.shape.ShapeAppearanceModel
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.looker.droidify.R
import com.looker.droidify.databinding.TabsToolbarBinding
import com.looker.droidify.datastore.model.supportedSortOrders
import com.looker.droidify.datastore.extension.sortOrderName
import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.model.ProductItem
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.ui.appList.AppListFragment
import com.looker.droidify.utility.common.device.Huawei import com.looker.droidify.utility.common.device.Huawei
import com.looker.droidify.utility.common.extension.dp import com.looker.droidify.utility.common.extension.dp
import com.looker.droidify.utility.common.extension.getMutatedIcon import com.looker.droidify.utility.common.extension.getMutatedIcon
import com.looker.droidify.utility.common.extension.selectableBackground import com.looker.droidify.utility.common.extension.selectableBackground
import com.looker.droidify.utility.common.extension.systemBarsPadding import com.looker.droidify.utility.common.extension.systemBarsPadding
import com.looker.droidify.utility.common.sdkAbove import com.looker.droidify.utility.common.sdkAbove
import com.looker.droidify.datastore.extension.sortOrderName
import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.R
import com.looker.droidify.databinding.TabsToolbarBinding
import com.looker.droidify.datastore.model.supportedSortOrders
import com.looker.droidify.model.ProductItem
import com.looker.droidify.service.Connection
import com.looker.droidify.service.SyncService
import com.looker.droidify.ui.ScreenFragment
import com.looker.droidify.ui.appList.AppListFragment
import com.looker.droidify.utility.extension.resources.sizeScaled import com.looker.droidify.utility.extension.resources.sizeScaled
import com.looker.droidify.utility.extension.screenActivity import com.looker.droidify.utility.extension.mainActivity
import com.looker.droidify.widget.DividerConfiguration import com.looker.droidify.widget.DividerConfiguration
import com.looker.droidify.widget.FocusSearchView import com.looker.droidify.widget.FocusSearchView
import com.looker.droidify.widget.StableRecyclerAdapter import com.looker.droidify.widget.StableRecyclerAdapter
@ -152,7 +153,7 @@ class TabsFragment : ScreenFragment() {
} }
} }
screenActivity.onToolbarCreated(toolbar) mainActivity.onToolbarCreated(toolbar)
toolbar.title = getString(R.string.application_name) toolbar.title = getString(R.string.application_name)
// Move focus from SearchView to Toolbar // Move focus from SearchView to Toolbar
toolbar.isFocusable = true toolbar.isFocusable = true
@ -204,7 +205,7 @@ class TabsFragment : ScreenFragment() {
syncRepositoriesMenuItem = add(0, 0, 0, stringRes.sync_repositories) syncRepositoriesMenuItem = add(0, 0, 0, stringRes.sync_repositories)
.setIcon(toolbar.context.getMutatedIcon(R.drawable.ic_sync)) .setIcon(toolbar.context.getMutatedIcon(R.drawable.ic_sync))
.setOnMenuItemClickListener { .setOnMenuItemClickListener {
viewModel.sync() syncConnection.binder?.sync(SyncService.SyncRequest.MANUAL)
true true
} }
@ -226,19 +227,19 @@ class TabsFragment : ScreenFragment() {
favouritesItem = add(1, 0, 0, stringRes.favourites) favouritesItem = add(1, 0, 0, stringRes.favourites)
.setIcon(toolbar.context.getMutatedIcon(R.drawable.ic_favourite_checked)) .setIcon(toolbar.context.getMutatedIcon(R.drawable.ic_favourite_checked))
.setOnMenuItemClickListener { .setOnMenuItemClickListener {
view.post { screenActivity.navigateFavourites() } view.post { mainActivity.navigateFavourites() }
true true
} }
add(1, 0, 0, stringRes.repositories) add(1, 0, 0, stringRes.repositories)
.setOnMenuItemClickListener { .setOnMenuItemClickListener {
view.post { screenActivity.navigateRepositories() } view.post { mainActivity.navigateRepositories() }
true true
} }
add(1, 0, 0, stringRes.settings) add(1, 0, 0, stringRes.settings)
.setOnMenuItemClickListener { .setOnMenuItemClickListener {
view.post { screenActivity.navigatePreferences() } view.post { mainActivity.navigatePreferences() }
true true
} }
} }
@ -295,6 +296,25 @@ class TabsFragment : ScreenFragment() {
onBackPressedCallback?.isEnabled = it != BackAction.None onBackPressedCallback?.isEnabled = it != BackAction.None
} }
} }
launch {
SyncService.syncState.collect {
when (it) {
is SyncService.State.Connecting -> {
tabsBinding.syncState.isVisible = true
tabsBinding.syncState.isIndeterminate = true
}
SyncService.State.Finish -> {
tabsBinding.syncState.isGone = true
}
is SyncService.State.Syncing -> {
tabsBinding.syncState.isVisible = true
tabsBinding.syncState.setProgressCompat(it.progress, true)
}
}
}
}
} }
} }

View File

@ -3,17 +3,12 @@ package com.looker.droidify.ui.tabsFragment
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.looker.droidify.data.local.dao.IndexDao
import com.looker.droidify.data.local.model.RepoEntity
import com.looker.droidify.data.local.model.toRepo
import com.looker.droidify.database.Database import com.looker.droidify.database.Database
import com.looker.droidify.datastore.SettingsRepository import com.looker.droidify.datastore.SettingsRepository
import com.looker.droidify.datastore.get import com.looker.droidify.datastore.get
import com.looker.droidify.datastore.model.SortOrder import com.looker.droidify.datastore.model.SortOrder
import com.looker.droidify.domain.model.Fingerprint import com.looker.droidify.domain.model.Fingerprint
import com.looker.droidify.model.ProductItem import com.looker.droidify.model.ProductItem
import com.looker.droidify.sync.Syncable
import com.looker.droidify.sync.v2.model.IndexV2
import com.looker.droidify.ui.tabsFragment.TabsFragment.BackAction import com.looker.droidify.ui.tabsFragment.TabsFragment.BackAction
import com.looker.droidify.utility.common.extension.asStateFlow import com.looker.droidify.utility.common.extension.asStateFlow
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@ -68,9 +63,15 @@ class TabsViewModel @Inject constructor(
val backAction = combine( val backAction = combine(
currentSection, currentSection,
isSearchActionItemExpanded, isSearchActionItemExpanded,
showSections, showSections
::calcBackAction, ) { currentSection, isSearchActionItemExpanded, showSections ->
).asStateFlow(BackAction.None) when {
currentSection != ProductItem.Section.All -> BackAction.ProductAll
isSearchActionItemExpanded -> BackAction.CollapseSearchView
showSections -> BackAction.HideSections
else -> BackAction.None
}
}.asStateFlow(BackAction.None)
fun setSection(section: ProductItem.Section) { fun setSection(section: ProductItem.Section) {
savedStateHandle[STATE_SECTION] = section savedStateHandle[STATE_SECTION] = section
@ -82,33 +83,6 @@ class TabsViewModel @Inject constructor(
} }
} }
fun sync() {
viewModelScope.launch {
val repo = RepoEntity(
id = 1,
address = "https://apt.izzysoft.de/fdroid/repo",
name = mapOf("en-US" to "IzzyOnDroid F-Droid Repo"),
description = emptyMap(),
fingerprint = Fingerprint("3BF0D6ABFEAE2F401707B6D966BE743BF0EEE49C2561B9BA39073711F628937A"),
timestamp = 0L,
icon = emptyMap(),
)
val (_, index) = syncable.sync(
repo.toRepo(
locale = "en-US",
mirrors = emptyList(),
enabled = true,
),
)
requireNotNull(index)
indexDao.insertIndex(
fingerprint = repo.fingerprint,
index = index,
expectedRepoId = repo.id,
)
}
}
private fun calcBackAction( private fun calcBackAction(
currentSection: ProductItem.Section, currentSection: ProductItem.Section,
isSearchActionItemExpanded: Boolean, isSearchActionItemExpanded: Boolean,

View File

@ -12,7 +12,7 @@ private val supportedExternalHosts = arrayOf(
"f-droid.org", "f-droid.org",
"www.f-droid.org", "www.f-droid.org",
"staging.f-droid.org", "staging.f-droid.org",
"apt.izzysoft.de" "apt.izzysoft.de",
) )
val Intent.deeplinkType: DeeplinkType? val Intent.deeplinkType: DeeplinkType?
@ -77,7 +77,7 @@ private inline fun invalidDeeplink(message: String): Nothing = throw InvalidDeep
sealed interface DeeplinkType { sealed interface DeeplinkType {
data class AddRepository(val address: String) : DeeplinkType class AddRepository(val address: String) : DeeplinkType
data class AppDetail(val packageName: String, val repoAddress: String? = null) : DeeplinkType class AppDetail(val packageName: String, val repoAddress: String? = null) : DeeplinkType
} }

View File

@ -33,3 +33,28 @@ object SdkCheck {
@get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N)
val isNougat: Boolean get() = sdk >= Build.VERSION_CODES.N val isNougat: Boolean get() = sdk >= Build.VERSION_CODES.N
} }
val sdkName by lazy {
mapOf(
16 to "4.1",
17 to "4.2",
18 to "4.3",
19 to "4.4",
21 to "5.0",
22 to "5.1",
23 to "6",
24 to "7.0",
25 to "7.1",
26 to "8.0",
27 to "8.1",
28 to "9",
29 to "10",
30 to "11",
31 to "12",
32 to "12L",
33 to "13",
34 to "14",
35 to "15",
36 to "16",
)
}

View File

@ -46,13 +46,6 @@ object Cache {
} }
} }
private fun subPath(dir: File, file: File): String {
val dirPath = "${dir.path}/"
val filePath = file.path
filePath.startsWith(dirPath) || throw RuntimeException()
return filePath.substring(dirPath.length)
}
fun getEmptySpace(context: Context): Long { fun getEmptySpace(context: Context): Long {
val dir = context.cacheDir val dir = context.cacheDir
return min(dir.usableSpace, dir.freeSpace) return min(dir.usableSpace, dir.freeSpace)
@ -187,7 +180,7 @@ object Cache {
projection: Array<String>?, projection: Array<String>?,
selection: String?, selection: String?,
selectionArgs: Array<out String>?, selectionArgs: Array<out String>?,
sortOrder: String? sortOrder: String?,
): Cursor { ): Cursor {
val file = getFileAndTypeForUri(uri).first val file = getFileAndTypeForUri(uri).first
val columns = (projection ?: defaultColumns).mapNotNull { val columns = (projection ?: defaultColumns).mapNotNull {
@ -217,7 +210,7 @@ object Cache {
uri: Uri, uri: Uri,
contentValues: ContentValues?, contentValues: ContentValues?,
selection: String?, selection: String?,
selectionArgs: Array<out String>? selectionArgs: Array<out String>?,
): Int = unsupported ): Int = unsupported
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {

View File

@ -5,9 +5,9 @@ import android.app.job.JobScheduler
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.ConnectivityManager
import android.os.PowerManager import android.os.PowerManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
@ -15,14 +15,12 @@ import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.net.toUri
import com.looker.droidify.R import com.looker.droidify.R
inline val Context.clipboardManager: ClipboardManager? inline val Context.clipboardManager: ClipboardManager?
get() = getSystemService() get() = getSystemService()
inline val Context.connectivityManager: ConnectivityManager?
get() = getSystemService()
inline val Context.inputManager: InputMethodManager? inline val Context.inputManager: InputMethodManager?
get() = getSystemService() get() = getSystemService()
@ -39,6 +37,13 @@ fun Context.copyToClipboard(clip: String) {
clipboardManager?.setPrimaryClip(ClipData.newPlainText(null, clip)) clipboardManager?.setPrimaryClip(ClipData.newPlainText(null, clip))
} }
fun Context.openLink(url: String) {
val intent = intent(Intent.ACTION_VIEW) {
setData(url.toUri())
}
startActivity(intent)
}
val Context.corneredBackground: Drawable val Context.corneredBackground: Drawable
get() = getDrawableCompat(R.drawable.background_border) get() = getDrawableCompat(R.drawable.background_border)
@ -57,6 +62,9 @@ val Context.selectableBackground: Drawable
val Context.camera: Drawable val Context.camera: Drawable
get() = getDrawableCompat(R.drawable.ic_image) get() = getDrawableCompat(R.drawable.ic_image)
val Context.videoPlaceHolder: Drawable
get() = getDrawableCompat(R.drawable.ic_video)
val Context.aspectRatio: Float val Context.aspectRatio: Float
get() = with(resources.displayMetrics) { get() = with(resources.displayMetrics) {
(heightPixels / widthPixels).toFloat() (heightPixels / widthPixels).toFloat()
@ -75,14 +83,21 @@ private fun Context.getDrawableFromAttr(attrResId: Int): Drawable {
} }
fun Context.getDrawableCompat(@DrawableRes resId: Int = R.drawable.background_border): Drawable = fun Context.getDrawableCompat(@DrawableRes resId: Int = R.drawable.background_border): Drawable =
requireNotNull(AppCompatResources.getDrawable(this, resId)) { "Cannot find drawable, ID: $resId" } requireNotNull(
AppCompatResources.getDrawable(
this,
resId
)
) { "Cannot find drawable, ID: $resId" }
fun Context.getColorFromAttr(@AttrRes attrResId: Int): ColorStateList { fun Context.getColorFromAttr(@AttrRes attrResId: Int): ColorStateList {
val typedArray = obtainStyledAttributes(intArrayOf(attrResId)) val typedArray = obtainStyledAttributes(intArrayOf(attrResId))
val (colorStateList, resId) = try { return try {
Pair(typedArray.getColorStateList(0), typedArray.getResourceId(0, 0)) typedArray.getColorStateList(0) ?: run {
val resourceId = typedArray.getResourceId(0, 0)
ContextCompat.getColorStateList(this, resourceId)!!
}
} finally { } finally {
typedArray.recycle() typedArray.recycle()
} }
return colorStateList ?: ContextCompat.getColorStateList(this, resId)!!
} }

View File

@ -1,4 +1,4 @@
package com.looker.core.common.extension package com.looker.droidify.utility.common.extension
import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonFactory
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator

View File

@ -74,7 +74,6 @@ fun PackageManager.getApplicationInfoCompat(
PackageManager.ApplicationInfoFlags.of(0L) PackageManager.ApplicationInfoFlags.of(0L)
) )
} else { } else {
@Suppress("DEPRECATION")
getApplicationInfo(filePath, 0) getApplicationInfo(filePath, 0)
} }
@ -98,7 +97,6 @@ fun PackageManager.getPackageInfoCompat(
PackageManager.PackageInfoFlags.of(signatureFlag.toLong()) PackageManager.PackageInfoFlags.of(signatureFlag.toLong())
) )
} else { } else {
@Suppress("DEPRECATION")
getPackageInfo(packageName, signatureFlag) getPackageInfo(packageName, signatureFlag)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -131,7 +129,6 @@ fun PackageManager.getPackageArchiveInfoCompat(
PackageManager.PackageInfoFlags.of(signatureFlag.toLong()) PackageManager.PackageInfoFlags.of(signatureFlag.toLong())
) )
} else { } else {
@Suppress("DEPRECATION")
getPackageArchiveInfo(filePath, signatureFlag) getPackageArchiveInfo(filePath, signatureFlag)
} }
} catch (e: Exception) { } catch (e: Exception) {
@ -144,7 +141,6 @@ fun PackageManager.getInstalledPackagesCompat(
if (SdkCheck.isTiramisu) { if (SdkCheck.isTiramisu) {
getInstalledPackages(PackageManager.PackageInfoFlags.of(signatureFlag.toLong())) getInstalledPackages(PackageManager.PackageInfoFlags.of(signatureFlag.toLong()))
} else { } else {
@Suppress("DEPRECATION")
getInstalledPackages(signatureFlag) getInstalledPackages(signatureFlag)
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -1,5 +1,6 @@
package com.looker.droidify.utility.common.extension package com.looker.droidify.utility.common.extension
import android.content.Context
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -7,7 +8,9 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.request.ImageRequest import coil3.network.NetworkHeaders
import coil3.network.httpHeaders
import coil3.request.ImageRequest
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
@ -17,8 +20,13 @@ import kotlinx.coroutines.flow.map
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
private val networkHeader by lazy { NetworkHeaders.Builder() }
fun ImageRequest.Builder.authentication(base64: String) { fun ImageRequest.Builder.authentication(base64: String) {
addHeader("Authorization", base64) if (base64.isNotEmpty()) {
networkHeader["Authorization"] = base64
httpHeaders(networkHeader.build())
}
} }
fun TextView.setTextSizeScaled(size: Int) { fun TextView.setTextSizeScaled(size: Int) {
@ -26,6 +34,9 @@ fun TextView.setTextSizeScaled(size: Int) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, realSize.toFloat()) setTextSize(TypedValue.COMPLEX_UNIT_PX, realSize.toFloat())
} }
val Context.layoutInflater: LayoutInflater
get() = LayoutInflater.from(this)
fun ViewGroup.inflate(layoutResId: Int): View { fun ViewGroup.inflate(layoutResId: Int): View {
return LayoutInflater.from(context).inflate(layoutResId, this, false) return LayoutInflater.from(context).inflate(layoutResId, this, false)
} }

View File

@ -34,7 +34,7 @@ suspend fun File.calculateHash(hashType: String): String? {
} }
private suspend fun MessageDigest.readBytesFrom( private suspend fun MessageDigest.readBytesFrom(
file: File file: File,
): ByteArray? = withContext(Dispatchers.IO) { ): ByteArray? = withContext(Dispatchers.IO) {
try { try {
if (file.length() < DIRECT_READ_LIMIT) return@withContext digest(file.readBytes()) if (file.length() < DIRECT_READ_LIMIT) return@withContext digest(file.readBytes())
@ -57,16 +57,9 @@ private suspend fun MessageDigest.readBytesFrom(
// 25 MB // 25 MB
private const val DIRECT_READ_LIMIT = 25 * 1024 * 1024 private const val DIRECT_READ_LIMIT = 25 * 1024 * 1024
@Suppress("FunctionName")
data class Hash( data class Hash(
val type: String, val type: String,
val hash: String val hash: String,
) { ) {
companion object {
fun SHA256(hash: String) = Hash(type = "sha256", hash)
fun MD5(hash: String) = Hash(type = "md5", hash)
}
fun isValid(): Boolean = type.isNotBlank() && hash.isNotBlank() fun isValid(): Boolean = type.isNotBlank() && hash.isNotBlank()
} }

View File

@ -3,5 +3,5 @@ package com.looker.droidify.utility.extension
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.looker.droidify.MainActivity import com.looker.droidify.MainActivity
inline val Fragment.screenActivity: MainActivity inline val Fragment.mainActivity: MainActivity
get() = requireActivity() as MainActivity get() = requireActivity() as MainActivity

View File

@ -2,7 +2,7 @@ package com.looker.droidify.utility.serialization
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.droidify.model.ProductItem import com.looker.droidify.model.ProductItem
fun ProductItem.serialize(generator: JsonGenerator) { fun ProductItem.serialize(generator: JsonGenerator) {

View File

@ -2,7 +2,7 @@ package com.looker.droidify.utility.serialization
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.droidify.model.ProductPreference import com.looker.droidify.model.ProductPreference
fun ProductPreference.serialize(generator: JsonGenerator) { fun ProductPreference.serialize(generator: JsonGenerator) {

View File

@ -3,11 +3,11 @@ package com.looker.droidify.utility.serialization
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.core.JsonToken
import com.looker.core.common.extension.collectNotNull import com.looker.droidify.utility.common.extension.collectNotNull
import com.looker.core.common.extension.collectNotNullStrings import com.looker.droidify.utility.common.extension.collectNotNullStrings
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.core.common.extension.writeArray import com.looker.droidify.utility.common.extension.writeArray
import com.looker.core.common.extension.writeDictionary import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.model.Product import com.looker.droidify.model.Product
import com.looker.droidify.model.Release import com.looker.droidify.model.Release
@ -53,11 +53,6 @@ fun Product.serialize(generator: JsonGenerator) {
writeStringField(ADDRESS, it.address) writeStringField(ADDRESS, it.address)
} }
is Product.Donate.Flattr -> {
writeStringField(TYPE, DONATION_FLATTR)
writeStringField(ID, it.id)
}
is Product.Donate.Liberapay -> { is Product.Donate.Liberapay -> {
writeStringField(TYPE, DONATION_LIBERAPAY) writeStringField(TYPE, DONATION_LIBERAPAY)
writeStringField(ID, it.id) writeStringField(ID, it.id)
@ -149,7 +144,6 @@ fun JsonParser.product(): Product {
DONATION_EMPTY -> Product.Donate.Regular(url) DONATION_EMPTY -> Product.Donate.Regular(url)
DONATION_BITCOIN -> Product.Donate.Bitcoin(address) DONATION_BITCOIN -> Product.Donate.Bitcoin(address)
DONATION_LITECOIN -> Product.Donate.Litecoin(address) DONATION_LITECOIN -> Product.Donate.Litecoin(address)
DONATION_FLATTR -> Product.Donate.Flattr(id)
DONATION_LIBERAPAY -> Product.Donate.Liberapay(id) DONATION_LIBERAPAY -> Product.Donate.Liberapay(id)
DONATION_OPENCOLLECTIVE -> Product.Donate.OpenCollective(id) DONATION_OPENCOLLECTIVE -> Product.Donate.OpenCollective(id)
else -> null else -> null
@ -243,6 +237,5 @@ private const val KEY_EMPTY = ""
private const val DONATION_EMPTY = "" private const val DONATION_EMPTY = ""
private const val DONATION_BITCOIN = "bitcoin" private const val DONATION_BITCOIN = "bitcoin"
private const val DONATION_LITECOIN = "litecoin" private const val DONATION_LITECOIN = "litecoin"
private const val DONATION_FLATTR = "flattr"
private const val DONATION_LIBERAPAY = "liberapay" private const val DONATION_LIBERAPAY = "liberapay"
private const val DONATION_OPENCOLLECTIVE = "openCollective" private const val DONATION_OPENCOLLECTIVE = "openCollective"

View File

@ -3,11 +3,11 @@ package com.looker.droidify.utility.serialization
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonToken import com.fasterxml.jackson.core.JsonToken
import com.looker.core.common.extension.collectNotNull import com.looker.droidify.utility.common.extension.collectNotNull
import com.looker.core.common.extension.collectNotNullStrings import com.looker.droidify.utility.common.extension.collectNotNullStrings
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.core.common.extension.writeArray import com.looker.droidify.utility.common.extension.writeArray
import com.looker.core.common.extension.writeDictionary import com.looker.droidify.utility.common.extension.writeDictionary
import com.looker.droidify.model.Release import com.looker.droidify.model.Release
fun Release.serialize(generator: JsonGenerator) { fun Release.serialize(generator: JsonGenerator) {

View File

@ -2,9 +2,9 @@ package com.looker.droidify.utility.serialization
import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.JsonParser
import com.looker.core.common.extension.collectNotNullStrings import com.looker.droidify.utility.common.extension.collectNotNullStrings
import com.looker.core.common.extension.forEachKey import com.looker.droidify.utility.common.extension.forEachKey
import com.looker.core.common.extension.writeArray import com.looker.droidify.utility.common.extension.writeArray
import com.looker.droidify.model.Repository import com.looker.droidify.model.Repository
fun Repository.serialize(generator: JsonGenerator) { fun Repository.serialize(generator: JsonGenerator) {

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorOnSurfaceInverse" android:state_enabled="false" />
<item android:color="?attr/colorPrimary" android:state_checked="true" />
<item android:color="?attr/colorOutline" />
</selector>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/material_emphasis_disabled_background" android:color="?attr/colorOnSurface" android:state_enabled="false" />
<item android:color="?attr/colorPrimaryContainer" android:state_checked="true" />
<item android:color="?attr/colorSurfaceVariant" />
</selector>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorOnSurface"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M9.461 3C5.183 3 3 5.464 3 10.064v3.213 6.438L7.19 15.52v-4.902c0-1.906 .505-3.118 2.199-3.391
.592-.116 1.824-.075 2.607-.075v2.911c0 .027 .004 .074 .01 .098 .033 .118 .139 .204 .266 .204 .071 0 .138-.037
.207-.105l7.262-7.259-4.875-.001zM21 4.285L16.81 8.48v4.902c0 1.906-.505 3.118-2.199 3.391-.592 .116-1.824 .075
-2.607 .075v-2.911c0-.026-.004-.074-.01-.098-.033-.118-.139-.204-.266-.204-.071 0-.138 .037-.207 .105L4.258 20.999
9.133 21H14.539C18.817 21 21 18.536 21 13.936v-3.214z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="?android:textColor"
android:pathData="M494.07,281.6l-25.18,-78.08a11,11 0,0 0,-0.61 -2.1L417.78,44.48a20.08,20.08 0,0 0,-19.17 -13.82A19.77,19.77 0,0 0,379.66 44.6L331.52,194.15h-152L131.34,44.59a19.76,19.76 0,0 0,-18.86 -13.94h-0.11a20.15,20.15 0,0 0,-19.12 14L42.7,201.73c0,0.14 -0.11,0.26 -0.16,0.4L16.91,281.61a29.15,29.15 0,0 0,10.44 32.46L248.79,476.48a11.25,11.25 0,0 0,13.38 -0.07L483.65,314.07a29.13,29.13 0,0 0,10.42 -32.47m-331,-64.51L224.8,408.85 76.63,217.09m209.64,191.8 l59.19,-183.84 2.55,-8h86.52L300.47,390.44M398.8,59.31l43.37,134.83H355.35M324.16,217l-43,133.58L255.5,430.14 186.94,217M112.27,59.31l43.46,134.83H69M40.68,295.58a6.19,6.19 0,0 1,-2.21 -6.9l19,-59L197.08,410.27M470.34,295.58 L313.92,410.22l0.52,-0.69L453.5,229.64l19,59a6.2,6.2 0,0 1,-2.19 6.92" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:textColor"
android:pathData="M13.8521,0.7714C12.3162,0.2228 11.0572,0.3718 9.89,0.7524L9.9406,16.1462c0.8343,0.7743 3.0648,0.8543 3.8984,0.0768V9.216l5.2569,7.0811c0.9244,0.5188 4.1768,0.2319 4.162,-1.317L17.873,8.196 23.352,1.146C22.8842,0.3544 19.6824,0.1619 18.7887,1.003L13.84,7.22ZM4.834,4.005C4.7874,4.0099 4.744,4.0307 4.711,4.064L3.145,5.63C3.0793,5.696 3.0669,5.7982 3.115,5.878L4.949,8.9C4.6205,9.4525 4.3613,10.0432 4.177,10.659l-3.367,0.7c-0.0944,0.0195 -0.1621,0.1026 -0.162,0.199v2.215c0,0.093 0.064,0.174 0.155,0.196l3.268,0.8c0.1709,0.7107 0.4405,1.394 0.801,2.03L2.98,19.683c-0.0521,0.0804 -0.0408,0.1863 0.027,0.254l1.566,1.567c0.0663,0.0659 0.1689,0.0782 0.249,0.03l2.964,-1.8c0.582,0.336 1.21,0.6 1.874,0.78l0.692,3.325C10.372,23.933 10.454,24 10.55,24h2.215c0.0937,0.0003 0.1752,-0.0639 0.197,-0.155l0.815,-3.332c0.6758,-0.1827 1.3239,-0.4555 1.927,-0.811l2.922,1.915c0.08,0.053 0.186,0.042 0.254,-0.026l1.567,-1.566c0.0661,-0.0658 0.0784,-0.1683 0.03,-0.248L19.41,18.019C18.9297,17.2055 18.038,15.026 17.371,15.8 15.3819,19.6985 10.1369,20.4613 7.1197,17.291 4.1025,14.1206 5.1238,8.9198 9.116,7.126 9.5319,6.8182 7.957,5.999 7.957,5.999L7.956,5.997 4.966,4.037C4.9271,4.0111 4.8805,3.9995 4.834,4.004Z" />
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M380,660L660,480L380,300L380,660ZM160,800Q127,800 103.5,776.5Q80,753 80,720L80,240Q80,207 103.5,183.5Q127,160 160,160L800,160Q833,160 856.5,183.5Q880,207 880,240L880,720Q880,753 856.5,776.5Q833,800 800,800L160,800ZM160,720L800,720Q800,720 800,720Q800,720 800,720L800,240Q800,240 800,240Q800,240 800,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720ZM160,720Q160,720 160,720Q160,720 160,720L160,240Q160,240 160,240Q160,240 160,240L160,240Q160,240 160,240Q160,240 160,240L160,720Q160,720 160,720Q160,720 160,720Z"/>
</vector>

View File

@ -90,7 +90,6 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:id="@+id/divider1"
android:layout_width="2dp" android:layout_width="2dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginVertical="12dp" /> android:layout_marginVertical="12dp" />
@ -120,7 +119,6 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:id="@+id/divider2"
android:layout_width="2dp" android:layout_width="2dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginVertical="12dp" /> android:layout_marginVertical="12dp" />

View File

@ -10,77 +10,68 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<com.google.android.material.card.MaterialCardView <LinearLayout
style="?materialCardViewElevatedStyle"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="12dp" android:orientation="vertical"
android:layout_marginTop="12dp"> android:paddingHorizontal="12dp"
android:paddingTop="4dp"
android:paddingBottom="12dp">
<LinearLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/address_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:hint="@string/address"
android:paddingHorizontal="12dp" android:paddingVertical="12dp">
android:paddingTop="4dp"
android:paddingBottom="12dp">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputEditText
android:id="@+id/address_container" android:id="@+id/address"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:hint="@string/address" android:inputType="textNoSuggestions|textVisiblePassword" />
android:paddingVertical="12dp"> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/address" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="match_parent" android:hint="@string/fingerprint"
android:inputType="textNoSuggestions|textVisiblePassword" /> android:paddingVertical="12dp">
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputEditText
android:id="@+id/fingerprint"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent" />
android:hint="@string/fingerprint" </com.google.android.material.textfield.TextInputLayout>
android:paddingVertical="12dp">
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/fingerprint" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="match_parent" /> android:hint="@string/username"
</com.google.android.material.textfield.TextInputLayout> android:paddingVertical="12dp">
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputEditText
android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:hint="@string/username" android:autofillHints="username" />
android:paddingVertical="12dp"> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputLayout
android:id="@+id/username" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="match_parent" android:hint="@string/password"
android:autofillHints="username" /> android:paddingVertical="12dp">
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:hint="@string/password" android:autofillHints="password"
android:paddingVertical="12dp"> android:inputType="textNoSuggestions|textVisiblePassword" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:autofillHints="password"
android:inputType="textNoSuggestions|textVisiblePassword" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<LinearLayout <LinearLayout
@ -90,6 +81,7 @@
android:background="?android:attr/colorBackground" android:background="?android:attr/colorBackground"
android:clickable="true" android:clickable="true"
android:gravity="center" android:gravity="center"
android:visibility="gone"
android:orientation="vertical" android:orientation="vertical"
tools:ignore="KeyboardInaccessibleWidget"> tools:ignore="KeyboardInaccessibleWidget">

View File

@ -19,4 +19,4 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?textAppearanceBodyMedium" android:textAppearance="?textAppearanceBodyMedium"
android:textColor="?colorOutline" /> android:textColor="?colorOutline" />
</LinearLayout> </LinearLayout>

View File

@ -19,4 +19,4 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -20,7 +20,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:gravity="center_vertical" android:gravity="center_vertical"
@ -96,6 +96,13 @@
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" /> android:textSize="14sp" />
<TextView
android:id="@+id/sdk_ver"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</LinearLayout> </LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View File

@ -2,7 +2,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical"
android:paddingBottom="20dp">
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/repo_switch" android:id="@+id/repo_switch"
@ -65,9 +66,4 @@
android:text="@string/delete" android:text="@string/delete"
android:textColor="?colorOnError" /> android:textColor="?colorOnError" />
</LinearLayout> </LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="20dp"/>
</LinearLayout> </LinearLayout>

View File

@ -145,6 +145,9 @@
<include <include
android:id="@+id/installer" android:id="@+id/installer"
layout="@layout/enum_type" /> layout="@layout/enum_type" />
<include
android:id="@+id/legacy_installer_component"
layout="@layout/enum_type" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -44,8 +44,12 @@
android:layout_marginEnd="20dp" android:layout_marginEnd="20dp"
android:src="@drawable/ic_arrow_down" android:src="@drawable/ic_arrow_down"
tools:ignore="ContentDescription" /> tools:ignore="ContentDescription" />
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</LinearLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/sync_state"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/video_button"
style="?materialIconButtonFilledTonalStyle"
android:text="@string/label_open_video"
android:layout_width="match_parent"
android:layout_height="150dp"
android:paddingHorizontal="24dp"
app:icon="@drawable/ic_video"
app:iconGravity="textTop"
app:iconPadding="6dp"
app:iconSize="24dp" />

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/exploreTab"
android:icon="@drawable/ic_public"
android:title="@string/explore" />
<item
android:id="@+id/latestTab"
android:icon="@drawable/ic_new_releases"
android:title="@string/latest" />
<item
android:id="@+id/installedTab"
android:icon="@drawable/ic_launch"
android:title="@string/installed" />
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -6,17 +6,13 @@
<string name="description">الوصف</string> <string name="description">الوصف</string>
<string name="incompatible_api_min_DESC_FORMAT">الحد الأدنى لإصدار واجهة برمجة التطبيقات هو %d.</string> <string name="incompatible_api_min_DESC_FORMAT">الحد الأدنى لإصدار واجهة برمجة التطبيقات هو %d.</string>
<string name="light">فاتح</string> <string name="light">فاتح</string>
<string name="list_animation">تحريكات القائمة</string>
<string name="ok">حسنًا</string> <string name="ok">حسنًا</string>
<string name="repository_unsigned_DESC">ليس موقعًا. تعذر التحقق من قائمة التطبيقات. كن حذرًا عند تنزيل التطبيقات من مستودعات غير موقعة.</string> <string name="repository_unsigned_DESC">ليس موقعًا. تعذر التحقق من قائمة التطبيقات. كن حذرًا عند تنزيل التطبيقات من مستودعات غير موقعة.</string>
<string name="sync_repositories">زامن المستودعات</string> <string name="sync_repositories">زامن المستودعات</string>
<string name="syncing">جاري المزامنة</string> <string name="syncing">جاري المزامنة</string>
<string name="unknown_FORMAT">غير معروف: %s</string> <string name="unknown_FORMAT">غير معروف: %s</string>
<string name="unsigned">لم يُوقع</string>
<string name="unstable_updates">تحديثات غير مستقرة</string> <string name="unstable_updates">تحديثات غير مستقرة</string>
<string name="show_less">اعرض أقل</string> <string name="show_less">اعرض أقل</string>
<string name="latest">الأحدث</string>
<string name="explore">استكشف</string>
<string name="action_failed">فشل الإجراء</string> <string name="action_failed">فشل الإجراء</string>
<string name="add_repository">أضف مستودعًا</string> <string name="add_repository">أضف مستودعًا</string>
<string name="all_applications">كل التطبيقات</string> <string name="all_applications">كل التطبيقات</string>
@ -90,7 +86,6 @@
<string name="license_FORMAT">رخصة %s</string> <string name="license_FORMAT">رخصة %s</string>
<string name="link_copied_to_clipboard">تم نسخ الرابط</string> <string name="link_copied_to_clipboard">تم نسخ الرابط</string>
<string name="links">الروابط</string> <string name="links">الروابط</string>
<string name="list_animation_description">أظهر تحريكات القائمة على الصفحة الرئيسة</string>
<string name="merging_FORMAT">جاري الدمج %s</string> <string name="merging_FORMAT">جاري الدمج %s</string>
<string name="name">الاسم</string> <string name="name">الاسم</string>
<string name="network_error_DESC">خطأ في الشبكة</string> <string name="network_error_DESC">خطأ في الشبكة</string>
@ -135,12 +130,8 @@
<string name="requires_FORMAT">يتطلب %s</string> <string name="requires_FORMAT">يتطلب %s</string>
<string name="repositories">المستودعات</string> <string name="repositories">المستودعات</string>
<string name="repository">المستودع</string> <string name="repository">المستودع</string>
<string name="repository_not_used_DESC">هذا المستودع لم يُستخدم بعد. مكنه لعرض التطبيقات التي يحتويها.</string>
<string name="root_permission">تثبيت صامت</string>
<string name="root_permission_description">اسمح لصلاحية الجذر لتمكين التثبيت الصامت</string>
<string name="save">احفظ</string> <string name="save">احفظ</string>
<string name="saving_details">جارٍ حفظ التفاصيل…</string> <string name="saving_details">جارٍ حفظ التفاصيل…</string>
<string name="screenshots">لقطات الشاشة</string>
<string name="search">ابحث</string> <string name="search">ابحث</string>
<string name="select_mirror">اختر مرآة</string> <string name="select_mirror">اختر مرآة</string>
<string name="share">شارك</string> <string name="share">شارك</string>
@ -159,12 +150,10 @@
<string name="syncing_FORMAT">جاري مزامنة %s…</string> <string name="syncing_FORMAT">جاري مزامنة %s…</string>
<string name="system">النظام</string> <string name="system">النظام</string>
<string name="tap_to_install_DESC">اضغط للتثبيت.</string> <string name="tap_to_install_DESC">اضغط للتثبيت.</string>
<string name="target">الهدف</string>
<string name="theme">السمة</string> <string name="theme">السمة</string>
<string name="themes">السمات</string> <string name="themes">السمات</string>
<string name="tracks_or_reports_your_activity">يتعقب أو يرفق نشاطك</string> <string name="tracks_or_reports_your_activity">يتعقب أو يرفق نشاطك</string>
<string name="uninstall">إلغاء التثبيت</string> <string name="uninstall">إلغاء التثبيت</string>
<string name="unverified">لم يُتحقق منه</string>
<string name="update">حدث</string> <string name="update">حدث</string>
<string name="updates">التحديثات</string> <string name="updates">التحديثات</string>
<string name="unknown">غير معروف</string> <string name="unknown">غير معروف</string>
@ -183,10 +172,6 @@
<string name="prefs_language_title">اللغة</string> <string name="prefs_language_title">اللغة</string>
<string name="prefs_personalization">التفضيلات</string> <string name="prefs_personalization">التفضيلات</string>
<string name="update_all">حدث الكل</string> <string name="update_all">حدث الكل</string>
<string name="installed_applications">التطبيقات المثبَّتة</string>
<string name="sort_filter">افرز وصفِّ</string>
<string name="new_applications">التطبيقات الجديدة</string>
<string name="cleanup_description">الزمن المار قبل فحص وإزالة الملفات المنزَّلة</string>
<string name="cleanup_title">مدة تنظيف ملفات APK</string> <string name="cleanup_title">مدة تنظيف ملفات APK</string>
<plurals name="days"> <plurals name="days">
<item quantity="zero">أقل من يوم</item> <item quantity="zero">أقل من يوم</item>
@ -206,9 +191,6 @@
</plurals> </plurals>
<string name="only_on_wifi_with_charging">فقط على شبكة واي-فاي وعند الشحن</string> <string name="only_on_wifi_with_charging">فقط على شبكة واي-فاي وعند الشحن</string>
<string name="io_error_DESC">تعذر أداء بعض الإجراءات.</string> <string name="io_error_DESC">تعذر أداء بعض الإجراءات.</string>
<string name="no_internet">ليس لديك اتصال بالإنترنت</string>
<string name="allow_collapsing_toolbar">اسمح بتوسيع شريط التطبيقات العلوي</string>
<string name="allow_collapsing_toolbar_DESC">اسمح لشريط التطبيقات العلوي بالتوسع والطي</string>
<string name="material_you">لون النظام ( Material You )</string> <string name="material_you">لون النظام ( Material You )</string>
<string name="material_you_desc">استخدم سمة ألوان النظام</string> <string name="material_you_desc">استخدم سمة ألوان النظام</string>
<string name="favourites">المفضَّلات</string> <string name="favourites">المفضَّلات</string>
@ -216,7 +198,6 @@
<string name="force_clean_up">افرض التنظيف</string> <string name="force_clean_up">افرض التنظيف</string>
<string name="force_clean_up_DESC">ينظِّف الملفَّات المتكرِّرة</string> <string name="force_clean_up_DESC">ينظِّف الملفَّات المتكرِّرة</string>
<string name="enable_repo">مكِّن المستودع</string> <string name="enable_repo">مكِّن المستودع</string>
<string name="restart_app">أعد تشغيل Droid-ify لرؤية التغييرات</string>
<string name="installing">يثبّت</string> <string name="installing">يثبّت</string>
<string name="waiting_to_start_installation">في انتظار بدء التثبيت…</string> <string name="waiting_to_start_installation">في انتظار بدء التثبيت…</string>
<string name="auto_update">حدِّث التطبيقات تلقائيًّا</string> <string name="auto_update">حدِّث التطبيقات تلقائيًّا</string>
@ -230,7 +211,6 @@
<string name="special_credits">شكر خاص</string> <string name="special_credits">شكر خاص</string>
<string name="home_screen_swiping">ايماءات الشاشة الرئيسة</string> <string name="home_screen_swiping">ايماءات الشاشة الرئيسة</string>
<string name="home_screen_swiping_DESC">اسمح للمستخدم بالتمرير بين الصفحات في الشاشة الرئيسة</string> <string name="home_screen_swiping_DESC">اسمح للمستخدم بالتمرير بين الصفحات في الشاشة الرئيسة</string>
<string name="label_copy">انسخ</string>
<string name="proxy_port_error_not_int">يجب أن يكون منفذ الوكيل رقمًا صحيحًا</string> <string name="proxy_port_error_not_int">يجب أن يكون منفذ الوكيل رقمًا صحيحًا</string>
<string name="repository_not_found">لم يُعثَر على المستودع</string> <string name="repository_not_found">لم يُعثَر على المستودع</string>
<string name="import_settings_title">استيراد الإعدادات</string> <string name="import_settings_title">استيراد الإعدادات</string>
@ -254,4 +234,14 @@
<string name="ignore_signature">تجاهل التوقيع</string> <string name="ignore_signature">تجاهل التوقيع</string>
<string name="insufficient_storage">مساحة غير كافية</string> <string name="insufficient_storage">مساحة غير كافية</string>
<string name="insufficient_storage_DESC">لا توجد مساحة خالية كافية على الجهاز لتثبيت هذا التطبيق. حاول إفراغ بعض المساحة</string> <string name="insufficient_storage_DESC">لا توجد مساحة خالية كافية على الجهاز لتثبيت هذا التطبيق. حاول إفراغ بعض المساحة</string>
</resources> <string name="error_shizuku_not_granted">إذن Shizuku مفقود</string>
<string name="error_shizuku_not_granted_DESC">لم يتم منح إذن خدمة Shizuku. يُرجى التحقق من تطبيق Shizuku</string>
<string name="error_shizuku_not_installed_DESC">لا يبدو أن Shizuku مثبت</string>
<string name="open_shizuku">افتح Shizuku</string>
<string name="label_unknown_sdk">غير معروف (%d)</string>
<string name="error_shizuku_not_installed">Shizuku غير مثبت</string>
<string name="error_shizuku_not_running_DESC">خدمة Shizuku لا تعمل. يُرجى التحقق من تطبيق Shizuku</string>
<string name="label_open_video">فيديو</string>
<string name="error_shizuku_service_unavailable">Shizuku لا يعمل</string>
<string name="switch_to_default_installer">بدّل إلى الافتراضي</string>
</resources>

View File

@ -3,8 +3,6 @@
<string name="add_repository">Yeni qaynaq əlavə et</string> <string name="add_repository">Yeni qaynaq əlavə et</string>
<string name="address">Ünvan</string> <string name="address">Ünvan</string>
<string name="all_applications">Bütün applikasiyalar</string> <string name="all_applications">Bütün applikasiyalar</string>
<string name="allow_collapsing_toolbar">Üst çubuğun genişlədilməsinə izn ver</string>
<string name="allow_collapsing_toolbar_DESC">Üst çubuğun genişləndirilməsinə və sıxılmasına izn ver</string>
<string name="already_exists">Zatən mövcuddur</string> <string name="already_exists">Zatən mövcuddur</string>
<string name="always">Hər zaman</string> <string name="always">Hər zaman</string>
<string name="anti_features">Anti-özəlliklər</string> <string name="anti_features">Anti-özəlliklər</string>
@ -20,7 +18,6 @@
<string name="changelog">Dəyişim günlüyü</string> <string name="changelog">Dəyişim günlüyü</string>
<string name="changes">Dəyişikliklər</string> <string name="changes">Dəyişikliklər</string>
<string name="cleanup_title">APK cleanup interval</string> <string name="cleanup_title">APK cleanup interval</string>
<string name="cleanup_description">Period to check and remove downloaded files</string>
<string name="compiled_for_debugging">Xəta tapmaq üçün kompayl edildi</string> <string name="compiled_for_debugging">Xəta tapmaq üçün kompayl edildi</string>
<string name="confirmation">Təsdiq</string> <string name="confirmation">Təsdiq</string>
<string name="connecting">Bağlanılır…</string> <string name="connecting">Bağlanılır…</string>
@ -78,8 +75,6 @@
<string name="license_FORMAT">%s lisenziyası</string> <string name="license_FORMAT">%s lisenziyası</string>
<string name="light">ıq</string> <string name="light">ıq</string>
<string name="links">Linklər</string> <string name="links">Linklər</string>
<string name="list_animation">Siyahı Animasiyaları</string>
<string name="list_animation_description">Siyahı canlandırmasını ana səhifədə göstər</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="material_you_desc">Material you rəng temasını istifadə et</string> <string name="material_you_desc">Material you rəng temasını istifadə et</string>
<string name="merging_FORMAT">%s birləşdirilir</string> <string name="merging_FORMAT">%s birləşdirilir</string>
@ -92,7 +87,6 @@
</plurals> </plurals>
<string name="no_applications_available">İstifadə edilə bilən applikasiya yoxdur</string> <string name="no_applications_available">İstifadə edilə bilən applikasiya yoxdur</string>
<string name="no_description_available_DESC">ıqlama yoxdur</string> <string name="no_description_available_DESC">ıqlama yoxdur</string>
<string name="no_internet">İnternet bağlantınız yoxdur</string>
<string name="no_proxy">Proxy yoxdur</string> <string name="no_proxy">Proxy yoxdur</string>
<string name="notify_about_updates">Applikasiyanın yeni versiyaları haqqında bilgiləndir</string> <string name="notify_about_updates">Applikasiyanın yeni versiyaları haqqında bilgiləndir</string>
<string name="number_of_applications">Applikasiya sayı</string> <string name="number_of_applications">Applikasiya sayı</string>
@ -144,16 +138,11 @@
<string name="recently_updated">Bu yaxınlarda yeniləndi</string> <string name="recently_updated">Bu yaxınlarda yeniləndi</string>
<string name="repositories">Anbarlar</string> <string name="repositories">Anbarlar</string>
<string name="repository">Repozitoriya</string> <string name="repository">Repozitoriya</string>
<string name="repository_not_used_DESC">Bu anbardan hələ istifadə olunmayıb. İçindəki proqramlara baxmaq üçün onu yandırın.</string>
<string name="repository_unsigned_DESC">İmzasız. Tətbiq siyahısını yoxlamaq mümkün olmadı. İmzasız depolardan proqramları endirərkən diqqətli olun.</string> <string name="repository_unsigned_DESC">İmzasız. Tətbiq siyahısını yoxlamaq mümkün olmadı. İmzasız depolardan proqramları endirərkən diqqətli olun.</string>
<string name="repository_unreachable">Repozitoriya əlçatmazdır</string> <string name="repository_unreachable">Repozitoriya əlçatmazdır</string>
<string name="requires_FORMAT">%s tələb edir</string> <string name="requires_FORMAT">%s tələb edir</string>
<string name="restart_app">Dəyişiklikləri görmək üçün Droid-ify-ı yenidən başladın</string>
<string name="root_permission">Səssiz Quraşdırma</string>
<string name="root_permission_description">Səssiz quraşdırmalar üçün kök icazəsinə icazə verin</string>
<string name="save">Yadda saxla</string> <string name="save">Yadda saxla</string>
<string name="saving_details">Detallar yadda saxlanılır…</string> <string name="saving_details">Detallar yadda saxlanılır…</string>
<string name="screenshots">Ekran görüntüləri</string>
<string name="search">Axtar</string> <string name="search">Axtar</string>
<string name="select_mirror">Güzgü seçin</string> <string name="select_mirror">Güzgü seçin</string>
<string name="settings">Parametrlər</string> <string name="settings">Parametrlər</string>
@ -173,17 +162,14 @@
<string name="syncing">Sinxronizasiya</string> <string name="syncing">Sinxronizasiya</string>
<string name="syncing_FORMAT">%s sinxronizasiya edilir…</string> <string name="syncing_FORMAT">%s sinxronizasiya edilir…</string>
<string name="system">Sistem</string> <string name="system">Sistem</string>
<string name="target">Hədəf</string>
<string name="theme">Mövzu</string> <string name="theme">Mövzu</string>
<string name="themes">Mövzular</string> <string name="themes">Mövzular</string>
<string name="tracks_or_reports_your_activity">Fəaliyyətinizi izləyir və ya hesabat verir</string> <string name="tracks_or_reports_your_activity">Fəaliyyətinizi izləyir və ya hesabat verir</string>
<string name="unknown">Naməlum</string> <string name="unknown">Naməlum</string>
<string name="unknown_error_DESC">Naməlum xəta.</string> <string name="unknown_error_DESC">Naməlum xəta.</string>
<string name="unknown_FORMAT">Naməlum: %s</string> <string name="unknown_FORMAT">Naməlum: %s</string>
<string name="unsigned">İmzasız</string>
<string name="unstable_updates">Qeyri-sabit yeniləmələr</string> <string name="unstable_updates">Qeyri-sabit yeniləmələr</string>
<string name="unstable_updates_summary">Qeyri-sabit versiyaların quraşdırılmasını təklif edin</string> <string name="unstable_updates_summary">Qeyri-sabit versiyaların quraşdırılmasını təklif edin</string>
<string name="unverified">Doğrulanmamış</string>
<string name="update">Yeniləyin</string> <string name="update">Yeniləyin</string>
<string name="updates">Yeniləmələr</string> <string name="updates">Yeniləmələr</string>
<string name="username">İstifadəçi adı</string> <string name="username">İstifadəçi adı</string>
@ -197,16 +183,11 @@
<string name="website">Veb sayt</string> <string name="website">Veb sayt</string>
<string name="prefs_language_title">Dil</string> <string name="prefs_language_title">Dil</string>
<string name="prefs_personalization">Fərdiləşdirmə</string> <string name="prefs_personalization">Fərdiləşdirmə</string>
<string name="latest">Ən son</string>
<string name="explore">Araşdırın</string>
<string name="update_all">Hamısını yeniləyin</string> <string name="update_all">Hamısını yeniləyin</string>
<string name="installed_applications">Quraşdırılmış proqramlar</string>
<string name="sort_filter">Çeşidləyin və Filtr edin</string>
<string name="new_applications">Yeni tətbiqlər</string>
<string name="show_less">Daha az göstər</string> <string name="show_less">Daha az göstər</string>
<string name="sync_repositories_automatically">Repozitoriyaları avtomatik sinxronlaşdırın</string> <string name="sync_repositories_automatically">Repozitoriyaları avtomatik sinxronlaşdırın</string>
<string name="uninstall">Silin</string> <string name="uninstall">Silin</string>
<string name="upstream_source_code_is_not_free">Yuxarı mənbə kodu pulsuz deyil</string> <string name="upstream_source_code_is_not_free">Yuxarı mənbə kodu pulsuz deyil</string>
<string name="validation_index_error_DESC">İndeksi doğrulamaq mümkün olmadı.</string> <string name="validation_index_error_DESC">İndeksi doğrulamaq mümkün olmadı.</string>
<string name="tap_to_install_DESC">Quraşdırmaq üçün toxunun.</string> <string name="tap_to_install_DESC">Quraşdırmaq üçün toxunun.</string>
</resources> </resources>

View File

@ -1,14 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="action_failed">Не атрымалася выканаць дзеянне</string> <string name="action_failed">Не атрымалася выканаць дзеянне</string>
<string name="allow_collapsing_toolbar">Дазволіць верхняй панэлі праграм пашырацца</string>
<string name="application_not_found">Не ўдалося знайсці гэтую праграму</string> <string name="application_not_found">Не ўдалося знайсці гэтую праграму</string>
<string name="author_website">Сайт аўтара</string> <string name="author_website">Сайт аўтара</string>
<string name="auto_update">Аўтаабнаўленне праграм</string> <string name="auto_update">Аўтаабнаўленне праграм</string>
<string name="add_repository">Дадаць рэпазіторый</string> <string name="add_repository">Дадаць рэпазіторый</string>
<string name="address">Адрас</string> <string name="address">Адрас</string>
<string name="all_applications">Ўсе праграмы</string> <string name="all_applications">Ўсе праграмы</string>
<string name="allow_collapsing_toolbar_DESC">Дазволіць верхнюю панэль праграмы пашыраць і згортваць</string>
<string name="already_exists">Ўжо існуе</string> <string name="already_exists">Ўжо існуе</string>
<string name="always">Заўсёды</string> <string name="always">Заўсёды</string>
<string name="amoled">Чорная</string> <string name="amoled">Чорная</string>
@ -21,7 +19,6 @@
<string name="cancel">Скасаваць</string> <string name="cancel">Скасаваць</string>
<string name="all_applications_up_to_date">Усе вашыя праграмы абноўлены</string> <string name="all_applications_up_to_date">Усе вашыя праграмы абноўлены</string>
<string name="delete">Выдаліць</string> <string name="delete">Выдаліць</string>
<string name="cleanup_description">Перыяд для праверкі і выдалення спампаваных файлаў</string>
<string name="description">Апісанне</string> <string name="description">Апісанне</string>
<string name="details">Дэталі</string> <string name="details">Дэталі</string>
<string name="downloading_FORMAT">Ідзе загрузка %s…</string> <string name="downloading_FORMAT">Ідзе загрузка %s…</string>
@ -80,7 +77,6 @@
<string name="installing">Ўсталяванне</string> <string name="installing">Ўсталяванне</string>
<string name="incompatible_platforms_DESC_FORMAT">Ваша платформа %1$s не падтрымліваецца. Падтрымліваюцца платформы: %2$s.</string> <string name="incompatible_platforms_DESC_FORMAT">Ваша платформа %1$s не падтрымліваецца. Падтрымліваюцца платформы: %2$s.</string>
<string name="repository_unreachable">Рэпазіторый недаступны</string> <string name="repository_unreachable">Рэпазіторый недаступны</string>
<string name="restart_app">Перазапусціце Droid-ify, каб убачыць змены</string>
<string name="incompatible_features_DESC">Адсутныя функцыі.</string> <string name="incompatible_features_DESC">Адсутныя функцыі.</string>
<string name="no_applications_available">Няма даступных праграм</string> <string name="no_applications_available">Няма даступных праграм</string>
<string name="password_missing">Пароль адсутнічае</string> <string name="password_missing">Пароль адсутнічае</string>
@ -96,8 +92,6 @@
<string name="other">Іншае</string> <string name="other">Іншае</string>
<string name="link_copied_to_clipboard">Спасылка скапіявана</string> <string name="link_copied_to_clipboard">Спасылка скапіявана</string>
<string name="processing_FORMAT">Апрацоўваецца %1$s…</string> <string name="processing_FORMAT">Апрацоўваецца %1$s…</string>
<string name="label_copy">Капіяваць</string>
<string name="no_internet">У вас няма падключэння да інтэрнэту</string>
<string name="proxy_port_error_not_int">Порт проксі можа быць толькі цэлым лікам</string> <string name="proxy_port_error_not_int">Порт проксі можа быць толькі цэлым лікам</string>
<string name="number_of_applications">Колькасць праграм</string> <string name="number_of_applications">Колькасць праграм</string>
<string name="incompatible_with_FORMAT">Несумяшчальна з %s</string> <string name="incompatible_with_FORMAT">Несумяшчальна з %s</string>
@ -158,19 +152,15 @@
<string name="plus_more_FORMAT">+%d больш</string> <string name="plus_more_FORMAT">+%d больш</string>
<string name="import_settings_title">Налады імпарту</string> <string name="import_settings_title">Налады імпарту</string>
<string name="incompatible_api_DESC_FORMAT">Ваш %1$s (версія API %2$d) не падтрымліваецца. %3$s</string> <string name="incompatible_api_DESC_FORMAT">Ваш %1$s (версія API %2$d) не падтрымліваецца. %3$s</string>
<string name="unsigned">Без подпісу</string>
<string name="contains_nsfw">Змяшчае NSFW кантэнт</string> <string name="contains_nsfw">Змяшчае NSFW кантэнт</string>
<string name="select_mirror">Выберыце люстэрка</string> <string name="select_mirror">Выберыце люстэрка</string>
<string name="list_animation_description">Паказаць анімацыю спісу на галоўнай старонцы</string>
<string name="syncing_FORMAT">Сінхранізацыя %s…</string> <string name="syncing_FORMAT">Сінхранізацыя %s…</string>
<string name="import_export">Імпарт/Экспарт</string> <string name="import_export">Імпарт/Экспарт</string>
<string name="tracks_or_reports_your_activity">Адсочвае або паведамляе аб вашай дзейнасці</string> <string name="tracks_or_reports_your_activity">Адсочвае або паведамляе аб вашай дзейнасці</string>
<string name="website">Вэб-сайт</string> <string name="website">Вэб-сайт</string>
<string name="merging_FORMAT">Аб\'яднанне %s</string> <string name="merging_FORMAT">Аб\'яднанне %s</string>
<string name="waiting_to_start_download">Чаканне пачатку загрузкі…</string> <string name="waiting_to_start_download">Чаканне пачатку загрузкі…</string>
<string name="explore">Агляд</string>
<string name="suggested">Рэкамендуецца</string> <string name="suggested">Рэкамендуецца</string>
<string name="sort_filter">Сартаваць і фільтраваць</string>
<string name="open_DESC_FORMAT">Адкрыць %s?</string> <string name="open_DESC_FORMAT">Адкрыць %s?</string>
<string name="validation_index_error_DESC">Не ўдалося праверыць індэкс.</string> <string name="validation_index_error_DESC">Не ўдалося праверыць індэкс.</string>
<string name="shizuku_not_alive">Shizuku не працуе</string> <string name="shizuku_not_alive">Shizuku не працуе</string>
@ -181,14 +171,12 @@
<string name="theme">Тэма</string> <string name="theme">Тэма</string>
<string name="socks_proxy">SOCKS проксі</string> <string name="socks_proxy">SOCKS проксі</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="target">Мэтавы</string>
<string name="waiting_to_start_installation">Чаканне пачатку ўстаноўкі…</string> <string name="waiting_to_start_installation">Чаканне пачатку ўстаноўкі…</string>
<string name="signed_using_unsafe_algorithm">Падпісана з выкарыстаннем небяспечнага алгарытму</string> <string name="signed_using_unsafe_algorithm">Падпісана з выкарыстаннем небяспечнага алгарытму</string>
<string name="source_code">Зыходны код</string> <string name="source_code">Зыходны код</string>
<string name="username">Імя карыстальніка</string> <string name="username">Імя карыстальніка</string>
<string name="version_FORMAT">Версія %s</string> <string name="version_FORMAT">Версія %s</string>
<string name="light">Светлая</string> <string name="light">Светлая</string>
<string name="installed_applications">Ўсталяваныя праграмы</string>
<string name="import_settings_DESC">Імпарт налад і абранага з файла</string> <string name="import_settings_DESC">Імпарт налад і абранага з файла</string>
<string name="repository">Рэпазіторый</string> <string name="repository">Рэпазіторый</string>
<string name="unstable_updates_summary">Прапанаваць усталёўваць нестабільныя версіі</string> <string name="unstable_updates_summary">Прапанаваць усталёўваць нестабільныя версіі</string>
@ -196,17 +184,13 @@
<string name="prefs_personalization">Персаналізацыя</string> <string name="prefs_personalization">Персаналізацыя</string>
<string name="signature_FORMAT">Подпіс %s</string> <string name="signature_FORMAT">Подпіс %s</string>
<string name="shizuku_installer">Shizuku устаноўшчык</string> <string name="shizuku_installer">Shizuku устаноўшчык</string>
<string name="new_applications">Новыя праграмы</string>
<string name="uninstall">Выдаліць</string> <string name="uninstall">Выдаліць</string>
<string name="skip">Прапусціць</string> <string name="skip">Прапусціць</string>
<string name="export_settings_title">Налады экспарту</string> <string name="export_settings_title">Налады экспарту</string>
<string name="export_repos_DESC">Экспарт усіх рэпазіторыяў з файла</string> <string name="export_repos_DESC">Экспарт усіх рэпазіторыяў з файла</string>
<string name="import_repos_title">Імпарт рэпазіторыяў</string> <string name="import_repos_title">Імпарт рэпазіторыяў</string>
<string name="screenshots">Скрыншоты</string>
<string name="recently_updated">Нядаўна абноўлены</string> <string name="recently_updated">Нядаўна абноўлены</string>
<string name="sorting_order">Парадак сартавання</string> <string name="sorting_order">Парадак сартавання</string>
<string name="unverified">Неправераныя</string>
<string name="root_permission">Ціхая ўстаноўка</string>
<string name="invalid_fingerprint_format">Няправільны фармат адбітка</string> <string name="invalid_fingerprint_format">Няправільны фармат адбітка</string>
<string name="saving_details">Захаванне даных…</string> <string name="saving_details">Захаванне даных…</string>
<string name="whats_new">Што новага</string> <string name="whats_new">Што новага</string>
@ -218,14 +202,12 @@
<string name="search">Пошук</string> <string name="search">Пошук</string>
<string name="unknown_error_DESC">Невядомая памылка.</string> <string name="unknown_error_DESC">Невядомая памылка.</string>
<string name="themes">Тэмы</string> <string name="themes">Тэмы</string>
<string name="list_animation">Анімацыя спісаў</string>
<string name="home_screen_swiping_DESC">Дазволіць карыстальніку гартаць старонкі на галоўным экране</string> <string name="home_screen_swiping_DESC">Дазволіць карыстальніку гартаць старонкі на галоўным экране</string>
<string name="export_repos_title">Экспарт рэпазіторыяў</string> <string name="export_repos_title">Экспарт рэпазіторыяў</string>
<string name="show_less">Паказаць менш</string> <string name="show_less">Паказаць менш</string>
<string name="settings">Налады</string> <string name="settings">Налады</string>
<string name="upstream_source_code_is_not_free">Зыходны код не з\'яўляецца свабодным</string> <string name="upstream_source_code_is_not_free">Зыходны код не з\'яўляецца свабодным</string>
<string name="versions">Версіі</string> <string name="versions">Версіі</string>
<string name="repository_not_used_DESC">Гэты рэпазіторый яшчэ не выкарыстоўваўся. Вам трэба ўключыць яго для прагляду прымянення ў ім.</string>
<string name="import_repos_DESC">Імпарт усіх рэпазіторыяў з файла</string> <string name="import_repos_DESC">Імпарт усіх рэпазіторыяў з файла</string>
<string name="system">Як у сістэме</string> <string name="system">Як у сістэме</string>
<string name="special_credits">Асаблівыя падзякі</string> <string name="special_credits">Асаблівыя падзякі</string>
@ -233,8 +215,6 @@
<string name="show_older_versions">Паказаць старыя версіі</string> <string name="show_older_versions">Паказаць старыя версіі</string>
<string name="shizuku_not_installed">Shizuku не ўстаноўлена</string> <string name="shizuku_not_installed">Shizuku не ўстаноўлена</string>
<string name="unstable_updates">Нестабільнае абнаўленне</string> <string name="unstable_updates">Нестабільнае абнаўленне</string>
<string name="latest">Апошнія</string>
<string name="root_permission_description">Падайце root-правы для ўключэння ціхай устаноўкі</string>
<string name="unknown_FORMAT">Невядома: %s</string> <string name="unknown_FORMAT">Невядома: %s</string>
<string name="cannot_open_link">Немагчыма адкрыць спасылку</string> <string name="cannot_open_link">Немагчыма адкрыць спасылку</string>
<string name="has_tethered_network">Прывязка да вызначанай сеткавай службы</string> <string name="has_tethered_network">Прывязка да вызначанай сеткавай службы</string>
@ -246,4 +226,15 @@
<string name="ignore_signature">Ігнараваць подпіс</string> <string name="ignore_signature">Ігнараваць подпіс</string>
<string name="installation_failed_DESC">Не ўдалося ўсталяваць %s</string> <string name="installation_failed_DESC">Не ўдалося ўсталяваць %s</string>
<string name="ignore_signature_summary">Ігнараванне праверкі подпісу пры ўстаноўцы apk, для карыстальнікаў LSPosed або прасунутых карыстальнікаў</string> <string name="ignore_signature_summary">Ігнараванне праверкі подпісу пры ўстаноўцы apk, для карыстальнікаў LSPosed або прасунутых карыстальнікаў</string>
</resources> <string name="error_shizuku_service_unavailable">Shizuku не працуе</string>
<string name="error_shizuku_not_running_DESC">Сэрвіс Shizuku не працуе. Калі ласка, праверце праграму Shizuku</string>
<string name="error_shizuku_not_granted">Shizuku няма дазволу</string>
<string name="error_shizuku_not_granted_DESC">Не дадзены дазвол на сэрвіс Shizuku. Калі ласка, праверце праграму Shizuku</string>
<string name="error_shizuku_not_installed">Shizuku не ўсталявана</string>
<string name="error_shizuku_not_installed_DESC">Здаецца, Shizuku не ўсталявана</string>
<string name="insufficient_storage">Не хапае месца</string>
<string name="insufficient_storage_DESC">На вашай прыладзе не хапае памяці для ўсталявання гэтай праграмы. Паспрабуйце вызваліць крыху месца</string>
<string name="open_shizuku">Адкрыць Shizuku</string>
<string name="switch_to_default_installer">Пераключыцца на стандартны</string>
<string name="label_open_video">Відэа</string>
</resources>

View File

@ -55,7 +55,6 @@
<string name="license_FORMAT">%s лиценз</string> <string name="license_FORMAT">%s лиценз</string>
<string name="light">Светла</string> <string name="light">Светла</string>
<string name="links">Линкове</string> <string name="links">Линкове</string>
<string name="list_animation_description">Анимирай списъка на главната страница</string>
<string name="merging_FORMAT">Сливане на %s</string> <string name="merging_FORMAT">Сливане на %s</string>
<string name="name">Име</string> <string name="name">Име</string>
<string name="network_error_DESC">Мрежова грешка</string> <string name="network_error_DESC">Мрежова грешка</string>
@ -66,7 +65,7 @@
<string name="notify_about_updates">Уведомления за актуализации</string> <string name="notify_about_updates">Уведомления за актуализации</string>
<string name="number_of_applications">Брой приложения</string> <string name="number_of_applications">Брой приложения</string>
<string name="ok">Окей</string> <string name="ok">Окей</string>
<string name="only_on_wifi">Само на Wi-Fi</string> <string name="only_on_wifi">Само при Wi-Fi</string>
<string name="open_DESC_FORMAT">Отвори %s\?</string> <string name="open_DESC_FORMAT">Отвори %s\?</string>
<string name="other">Други</string> <string name="other">Други</string>
<string name="parsing_index_error_DESC">Не може да се прочете индекс файла.</string> <string name="parsing_index_error_DESC">Не може да се прочете индекс файла.</string>
@ -79,14 +78,10 @@
<string name="recently_updated">Наскоро обновени</string> <string name="recently_updated">Наскоро обновени</string>
<string name="repositories">Хранилища</string> <string name="repositories">Хранилища</string>
<string name="repository">Хранилище</string> <string name="repository">Хранилище</string>
<string name="repository_not_used_DESC">Това хранилище все още не е използвано.Включете го, за да видите приложенията в него.</string>
<string name="repository_unsigned_DESC">Неподписано. Не може да провери списъка с неподписаните приложения. Внимавайте с тегленето на приложения от неподписани хранилища.</string> <string name="repository_unsigned_DESC">Неподписано. Не може да провери списъка с неподписаните приложения. Внимавайте с тегленето на приложения от неподписани хранилища.</string>
<string name="requires_FORMAT">Изисква %s</string> <string name="requires_FORMAT">Изисква %s</string>
<string name="root_permission">Безшумна Инсталация</string>
<string name="root_permission_description">За безшумни инсталации дайте root разрешение</string>
<string name="save">Запази</string> <string name="save">Запази</string>
<string name="saving_details">Запазване на подробности…</string> <string name="saving_details">Запазване на подробности…</string>
<string name="screenshots">Екранни снимки</string>
<string name="select_mirror">Избери източник</string> <string name="select_mirror">Избери източник</string>
<string name="share">Сподели</string> <string name="share">Сподели</string>
<string name="show_more">Покажи повече</string> <string name="show_more">Покажи повече</string>
@ -100,12 +95,10 @@
<string name="suggested">Предложено</string> <string name="suggested">Предложено</string>
<string name="sync_repositories_automatically">Автоматично синхронизиране на хранилищата</string> <string name="sync_repositories_automatically">Автоматично синхронизиране на хранилищата</string>
<string name="syncing">Синхронизиране</string> <string name="syncing">Синхронизиране</string>
<string name="target">Цел</string>
<string name="theme">Тема</string> <string name="theme">Тема</string>
<string name="tracks_or_reports_your_activity">Проследява или отчита вашата дейност</string> <string name="tracks_or_reports_your_activity">Проследява или отчита вашата дейност</string>
<string name="unknown">Неизвестно</string> <string name="unknown">Неизвестно</string>
<string name="unknown_FORMAT">Неизвестно: %s</string> <string name="unknown_FORMAT">Неизвестно: %s</string>
<string name="unverified">Непроверено</string>
<string name="update">Актуализация</string> <string name="update">Актуализация</string>
<string name="updates">Актуализации</string> <string name="updates">Актуализации</string>
<string name="username">Потребителско име</string> <string name="username">Потребителско име</string>
@ -114,12 +107,7 @@
<string name="waiting_to_start_download">В очакване да започне изтеглянето…</string> <string name="waiting_to_start_download">В очакване да започне изтеглянето…</string>
<string name="whats_new">Какво е новото</string> <string name="whats_new">Какво е новото</string>
<string name="show_less">Покажи по-малко</string> <string name="show_less">Покажи по-малко</string>
<string name="latest">Последни</string>
<string name="explore">Разглеждане</string>
<string name="update_all">Инсталирай всички</string> <string name="update_all">Инсталирай всички</string>
<string name="installed_applications">Инсталирани приложения</string>
<string name="sort_filter">Сортиране &amp; Филтриране</string>
<string name="new_applications">Нови приложения</string>
<string name="anti_features">Антифункции</string> <string name="anti_features">Антифункции</string>
<string name="author_website">Уебстраница на автора</string> <string name="author_website">Уебстраница на автора</string>
<string name="cant_edit_sync_DESC">Не може да се редактират синхронизиращи се хранилища.</string> <string name="cant_edit_sync_DESC">Не може да се редактират синхронизиращи се хранилища.</string>
@ -134,7 +122,6 @@
<string name="incompatible_api_DESC_FORMAT">Вашата %1$s (АПИ версия %2$d) се поддържа. %3$s</string> <string name="incompatible_api_DESC_FORMAT">Вашата %1$s (АПИ версия %2$d) се поддържа. %3$s</string>
<string name="incompatible_older_DESC">Тази версия е по-стара от инсталираната на вашето устройство. Деинсталирайте първо нея.</string> <string name="incompatible_older_DESC">Тази версия е по-стара от инсталираната на вашето устройство. Деинсталирайте първо нея.</string>
<string name="invalid_username_format">Невалиден формат на потребителското име</string> <string name="invalid_username_format">Невалиден формат на потребителското име</string>
<string name="list_animation">Анимации на списъците</string>
<string name="unstable_updates">Нестабилни актуализации</string> <string name="unstable_updates">Нестабилни актуализации</string>
<string name="incompatible_signature_DESC">Тази версия е подписана със сертификат, различен от този, инсталиран на вашето устройство. Деинсталирайте първо нея.</string> <string name="incompatible_signature_DESC">Тази версия е подписана със сертификат, различен от този, инсталиран на вашето устройство. Деинсталирайте първо нея.</string>
<string name="incompatible_versions_summary">Показване на версии, несъвместими с устройството</string> <string name="incompatible_versions_summary">Показване на версии, несъвместими с устройството</string>
@ -170,7 +157,6 @@
<string name="tap_to_install_DESC">Докосни за инсталиране.</string> <string name="tap_to_install_DESC">Докосни за инсталиране.</string>
<string name="unknown_error_DESC">Неизвестна грешка.</string> <string name="unknown_error_DESC">Неизвестна грешка.</string>
<string name="unstable_updates_summary">Предложи инсталирането на нестабилни версии</string> <string name="unstable_updates_summary">Предложи инсталирането на нестабилни версии</string>
<string name="unsigned">Неподписано</string>
<string name="upstream_source_code_is_not_free">Актуалният програмен код вече не е със свободен лиценз</string> <string name="upstream_source_code_is_not_free">Актуалният програмен код вече не е със свободен лиценз</string>
<string name="username_missing">Потребителско име липсва</string> <string name="username_missing">Потребителско име липсва</string>
<string name="validation_index_error_DESC">Не може да валидира индексът.</string> <string name="validation_index_error_DESC">Не може да валидира индексът.</string>
@ -188,24 +174,19 @@
</plurals> </plurals>
<string name="only_on_wifi_with_charging">Само при Wi-Fi и зареждане</string> <string name="only_on_wifi_with_charging">Само при Wi-Fi и зареждане</string>
<string name="cleanup_title">Интервал за почистване на APK</string> <string name="cleanup_title">Интервал за почистване на APK</string>
<string name="cleanup_description">Период за проверка и премахване на изтеглените файлове</string>
<plurals name="hours"> <plurals name="hours">
<item quantity="one">Час</item> <item quantity="one">Час</item>
<item quantity="other">Часа</item> <item quantity="other">Часа</item>
</plurals> </plurals>
<string name="no_internet">Нямате интернет връзка</string>
<string name="allow_collapsing_toolbar">Разрешете горната лента на приложението да се разшири</string>
<string name="io_error_DESC">Невъзможност за извършване на определени действия.</string> <string name="io_error_DESC">Невъзможност за извършване на определени действия.</string>
<string name="allow_collapsing_toolbar_DESC">Разрешете горната лента на приложението да се разширява и свива</string>
<string name="material_you_desc">Използвайте material you цветова тема</string> <string name="material_you_desc">Използвайте material you цветова тема</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="auto_update">Автоматично актуализиране на приложения</string> <string name="auto_update">Автоматично актуализиране на приложения</string>
<string name="installing">Инсталиране</string> <string name="installing">Инсталиране</string>
<string name="auto_update_apps">Опитайте се да инсталирате актуализации автоматично</string> <string name="auto_update_apps">Опитайте се да инсталирате актуализации автоматично</string>
<string name="restart_app">Рестартирайте Droid-ify, за да видите промените</string>
<string name="waiting_to_start_installation">Изчакване за стартиране на инсталацията…</string> <string name="waiting_to_start_installation">Изчакване за стартиране на инсталацията…</string>
<string name="favourites">Любими</string> <string name="favourites">Любими</string>
<string name="enable_repo">Активирайте хранилището</string> <string name="enable_repo">Активирай хранилището</string>
<string name="force_clean_up">Принудително почистване</string> <string name="force_clean_up">Принудително почистване</string>
<string name="force_clean_up_DESC">Почиства излишните файлове</string> <string name="force_clean_up_DESC">Почиства излишните файлове</string>
<string name="repository_unreachable">Хранилището е недостъпно</string> <string name="repository_unreachable">Хранилището е недостъпно</string>
@ -215,7 +196,6 @@
<string name="home_screen_swiping">Плъзгане на началния екран</string> <string name="home_screen_swiping">Плъзгане на началния екран</string>
<string name="contains_nsfw">Съдържа неподходящо за работа съдържание</string> <string name="contains_nsfw">Съдържа неподходящо за работа съдържание</string>
<string name="shizuku_not_alive">Shizuku не работи</string> <string name="shizuku_not_alive">Shizuku не работи</string>
<string name="label_copy">Копирай</string>
<string name="proxy_port_error_not_int">Прокси портът може да бъде само цяло число</string> <string name="proxy_port_error_not_int">Прокси портът може да бъде само цяло число</string>
<string name="home_screen_swiping_DESC">Позволете на потребителя да плъзга между страниците в началния екран</string> <string name="home_screen_swiping_DESC">Позволете на потребителя да плъзга между страниците в началния екран</string>
<string name="repository_not_found">Следното хранилище не бе намерено</string> <string name="repository_not_found">Следното хранилище не бе намерено</string>
@ -225,12 +205,12 @@
<string name="import_settings_title">Внеси Настройки</string> <string name="import_settings_title">Внеси Настройки</string>
<string name="import_settings_DESC">Внасяне на настройки и любими от файл</string> <string name="import_settings_DESC">Внасяне на настройки и любими от файл</string>
<string name="export_settings_title">Изнеси Настройки</string> <string name="export_settings_title">Изнеси Настройки</string>
<string name="export_repos_DESC">Изнеси всички хранилища във файл</string> <string name="export_repos_DESC">Изнасяне на всички хранилища във файл</string>
<string name="import_repos_title">Внеси хранилища</string> <string name="import_repos_title">Внеси хранилища</string>
<string name="export_settings_DESC">Изнасяне на настройки и любими във файл</string> <string name="export_settings_DESC">Изнасяне на настройки и любими във файл</string>
<string name="export_repos_title">Изнеси хранилища</string> <string name="export_repos_title">Изнеси хранилища</string>
<string name="cannot_open_link">Линкът не може да се отвори</string> <string name="cannot_open_link">Линкът не може да се отвори</string>
<string name="import_repos_DESC">Внеси всички хранилища от файл</string> <string name="import_repos_DESC">Внасяне на всички хранилища от файл</string>
<string name="has_tethered_network">Обвързан с определена мрежова услуга</string> <string name="has_tethered_network">Обвързан с определена мрежова услуга</string>
<string name="require_background_access">Изискване на фонов достъп</string> <string name="require_background_access">Изискване на фонов достъп</string>
<string name="require_background_access_DESC">Необходим е фонов достъп, за да стартирате правилно фоновото синхронизиране</string> <string name="require_background_access_DESC">Необходим е фонов достъп, за да стартирате правилно фоновото синхронизиране</string>
@ -242,4 +222,14 @@
<string name="ignore_signature_summary">*Внимание* Игнорирайте проверката на подписа при инсталиране на APK за LSPosed потребители или напреднали потребители</string> <string name="ignore_signature_summary">*Внимание* Игнорирайте проверката на подписа при инсталиране на APK за LSPosed потребители или напреднали потребители</string>
<string name="insufficient_storage">Недостатъчно място</string> <string name="insufficient_storage">Недостатъчно място</string>
<string name="insufficient_storage_DESC">Няма достатъчно свободно място на устройството за инсталиране на това приложение. Опитайте да освободите малко място</string> <string name="insufficient_storage_DESC">Няма достатъчно свободно място на устройството за инсталиране на това приложение. Опитайте да освободите малко място</string>
</resources> <string name="error_shizuku_not_installed">Shizuku Не е инсталиран</string>
<string name="label_open_video">Видео</string>
<string name="switch_to_default_installer">Превключване към подразбиране</string>
<string name="open_shizuku">Отворете Shizuku</string>
<string name="error_shizuku_not_running_DESC">Услугата Shizuku не работи. Моля, проверете в приложението Shizuku</string>
<string name="error_shizuku_not_granted">Липсва разрешение за Shizuku</string>
<string name="error_shizuku_not_granted_DESC">Разрешението за услуга Shizuku не е дадено. Моля, проверете в приложението Shizuku</string>
<string name="error_shizuku_not_installed_DESC">Shizuku изглежда не е инсталиран</string>
<string name="error_shizuku_service_unavailable">Shizuku Не работи</string>
<string name="label_unknown_sdk">Неизвестен (%d)</string>
</resources>

View File

@ -21,7 +21,6 @@
<string name="already_exists">আগে থেকেই আছে</string> <string name="already_exists">আগে থেকেই আছে</string>
<string name="always">সর্বদা</string> <string name="always">সর্বদা</string>
<string name="amoled">অন্ধকার</string> <string name="amoled">অন্ধকার</string>
<string name="allow_collapsing_toolbar">অ্যাপের টপ বার সম্প্রসারিত হতে দাও</string>
<string name="could_not_validate_FORMAT">%s সত্যায়িত করা সম্ভব হয়নি</string> <string name="could_not_validate_FORMAT">%s সত্যায়িত করা সম্ভব হয়নি</string>
<string name="donate">অনুদান</string> <string name="donate">অনুদান</string>
<string name="description">বর্ণনা</string> <string name="description">বর্ণনা</string>
@ -30,7 +29,6 @@
<item quantity="other">ঘণ্টা</item> <item quantity="other">ঘণ্টা</item>
</plurals> </plurals>
<string name="incompatible_api_max_DESC_FORMAT">সর্বোচ্চ এপিআই সংস্করণ %d।</string> <string name="incompatible_api_max_DESC_FORMAT">সর্বোচ্চ এপিআই সংস্করণ %d।</string>
<string name="allow_collapsing_toolbar_DESC">অ্যাপের টপ বার সম্প্রসারিত ও সংকুচিত হতে দাও</string>
<string name="anti_features">অপবৈশিষ্ট্য</string> <string name="anti_features">অপবৈশিষ্ট্য</string>
<string name="has_advertising">বিজ্ঞাপন আছে</string> <string name="has_advertising">বিজ্ঞাপন আছে</string>
<string name="http_proxy">HTTP প্রক্সি</string> <string name="http_proxy">HTTP প্রক্সি</string>
@ -60,7 +58,6 @@
<string name="delete_repository_DESC">রিপোজিটরি ডিলিট করতে চান\?</string> <string name="delete_repository_DESC">রিপোজিটরি ডিলিট করতে চান\?</string>
<string name="edit_repository">রিপোজিটরি সম্পাদনা করুন</string> <string name="edit_repository">রিপোজিটরি সম্পাদনা করুন</string>
<string name="ignore_this_update">এই সংস্করণটি অগ্রাহ্য করো</string> <string name="ignore_this_update">এই সংস্করণটি অগ্রাহ্য করো</string>
<string name="cleanup_description">ডাউনলোড করা নথিগুলো পরীক্ষা এবং অপসারণের সময়কাল</string>
<string name="compiled_for_debugging">ডিবাগিংয়ের জন্য কম্পাইল করা হয়েছে</string> <string name="compiled_for_debugging">ডিবাগিংয়ের জন্য কম্পাইল করা হয়েছে</string>
<string name="bug_tracker">বাগ ট্র্যাকার</string> <string name="bug_tracker">বাগ ট্র্যাকার</string>
<string name="incompatible_signature_DESC">এই সংস্করণটি আপনার ডিভাইসে ইনস্টল করা একটি থেকে একটি ভিন্ন শংসাপত্রের সাথে স্বাক্ষরিত৷ প্রথমে এটি আনইনস্টল করুন।</string> <string name="incompatible_signature_DESC">এই সংস্করণটি আপনার ডিভাইসে ইনস্টল করা একটি থেকে একটি ভিন্ন শংসাপত্রের সাথে স্বাক্ষরিত৷ প্রথমে এটি আনইনস্টল করুন।</string>
@ -91,8 +88,6 @@
<string name="license">লাইসেন্স</string> <string name="license">লাইসেন্স</string>
<string name="license_FORMAT">%s লাইসেন্স</string> <string name="license_FORMAT">%s লাইসেন্স</string>
<string name="light">আলো</string> <string name="light">আলো</string>
<string name="list_animation">তালিকা অ্যানিমেশন</string>
<string name="list_animation_description">প্রধান পৃষ্ঠায় তালিকা অ্যানিমেশন দেখান</string>
<string name="merging_FORMAT">%s মার্জ করা হচ্ছে</string> <string name="merging_FORMAT">%s মার্জ করা হচ্ছে</string>
<string name="material_you">উপাদান আপনি</string> <string name="material_you">উপাদান আপনি</string>
<string name="session_installer">সেশন ইনস্টলার</string> <string name="session_installer">সেশন ইনস্টলার</string>
@ -108,7 +103,6 @@
<item quantity="other">নতুন সংস্করণ সহ %dটি অ্যাপ্লিকেশন।</item> <item quantity="other">নতুন সংস্করণ সহ %dটি অ্যাপ্লিকেশন।</item>
</plurals> </plurals>
<string name="no_applications_installed">কোনো ইনস্টল করা অ্যাপ্লিকেশন নেই</string> <string name="no_applications_installed">কোনো ইনস্টল করা অ্যাপ্লিকেশন নেই</string>
<string name="no_internet">আপনার কোন ইন্টারনেট সংযোগ নেই</string>
<string name="no_matching_applications_found">এই ধরনের কোনো অ্যাপ্লিকেশন খুঁজে পাওয়া যায়নি</string> <string name="no_matching_applications_found">এই ধরনের কোনো অ্যাপ্লিকেশন খুঁজে পাওয়া যায়নি</string>
<string name="number_of_applications">আবেদনের সংখ্যা</string> <string name="number_of_applications">আবেদনের সংখ্যা</string>
<string name="auto_update">স্বয়ংক্রিয়ভাবে অ্যাপ্লিকেশন হালনাগাদ</string> <string name="auto_update">স্বয়ংক্রিয়ভাবে অ্যাপ্লিকেশন হালনাগাদ</string>
@ -129,8 +123,6 @@
<string name="proxy_port">প্রক্সি পর্ট</string> <string name="proxy_port">প্রক্সি পর্ট</string>
<string name="recently_updated">সম্প্রতি আপডেট করা হয়েছে</string> <string name="recently_updated">সম্প্রতি আপডেট করা হয়েছে</string>
<string name="repository">ভান্ডার</string> <string name="repository">ভান্ডার</string>
<string name="repository_not_used_DESC">এই সংগ্রহস্থল এখনও ব্যবহার করা হয় নি. এটিতে থাকা অ্যাপ্লিকেশনগুলি দেখতে এটি চালু করুন।</string>
<string name="restart_app">পরিবর্তনগুলি দেখতে Droid-ify পুনরায় চালু করুন</string>
<string name="show_older_versions">পুরানো সংস্করণ দেখান</string> <string name="show_older_versions">পুরানো সংস্করণ দেখান</string>
<string name="plus_more_FORMAT">+%d আরো</string> <string name="plus_more_FORMAT">+%d আরো</string>
<string name="favourites">প্রিয়</string> <string name="favourites">প্রিয়</string>
@ -149,10 +141,7 @@
<string name="project_website">প্রকল্প ওয়েবসাইট</string> <string name="project_website">প্রকল্প ওয়েবসাইট</string>
<string name="repository_unreachable">রিপোজিটরি পৌঁছানো যায় না</string> <string name="repository_unreachable">রিপোজিটরি পৌঁছানো যায় না</string>
<string name="requires_FORMAT">%s প্রয়োজন</string> <string name="requires_FORMAT">%s প্রয়োজন</string>
<string name="root_permission">নীরব ইনস্টল</string>
<string name="saving_details">বিবরণ সংরক্ষণ করা হচ্ছে…</string> <string name="saving_details">বিবরণ সংরক্ষণ করা হচ্ছে…</string>
<string name="screenshots">স্ক্রিনশট</string>
<string name="root_permission_description">নীরব ইনস্টলেশনের জন্য রুট অনুমতির অনুমতি দিন</string>
<string name="save">সংরক্ষণ</string> <string name="save">সংরক্ষণ</string>
<string name="search">অনুসন্ধান করুন</string> <string name="search">অনুসন্ধান করুন</string>
<string name="select_mirror">একটি আয়না নির্বাচন করুন</string> <string name="select_mirror">একটি আয়না নির্বাচন করুন</string>
@ -172,24 +161,18 @@
<string name="syncing">সিঙ্ক হচ্ছে</string> <string name="syncing">সিঙ্ক হচ্ছে</string>
<string name="system">পদ্ধতি</string> <string name="system">পদ্ধতি</string>
<string name="tap_to_install_DESC">ইনস্টল করতে আলতো চাপুন।</string> <string name="tap_to_install_DESC">ইনস্টল করতে আলতো চাপুন।</string>
<string name="target">টার্গেট</string>
<string name="uninstall">আনইনস্টল করুন</string> <string name="uninstall">আনইনস্টল করুন</string>
<string name="unknown">অজানা</string> <string name="unknown">অজানা</string>
<string name="unknown_error_DESC">অজানা ত্রুটি.</string> <string name="unknown_error_DESC">অজানা ত্রুটি.</string>
<string name="unknown_FORMAT">অজানা: %s</string> <string name="unknown_FORMAT">অজানা: %s</string>
<string name="unsigned">স্বাক্ষরবিহীন</string>
<string name="unverified">যাচাই করা হয়নি</string>
<string name="username">ব্যবহারকারীর নাম</string> <string name="username">ব্যবহারকারীর নাম</string>
<string name="version_FORMAT">সংস্করণ %s</string> <string name="version_FORMAT">সংস্করণ %s</string>
<string name="website">ওয়েবসাইট</string> <string name="website">ওয়েবসাইট</string>
<string name="prefs_personalization">ব্যক্তিগতকরণ</string> <string name="prefs_personalization">ব্যক্তিগতকরণ</string>
<string name="show_less">প্রদর্শন কম</string> <string name="show_less">প্রদর্শন কম</string>
<string name="latest">সর্বশেষ</string>
<string name="sort_filter">বাছাই এবং ফিল্টার</string>
<string name="sync_repositories">সিঙ্ক রিপোজিটরি</string> <string name="sync_repositories">সিঙ্ক রিপোজিটরি</string>
<string name="tracks_or_reports_your_activity">আপনার কার্যকলাপ ট্র্যাক বা রিপোর্ট</string> <string name="tracks_or_reports_your_activity">আপনার কার্যকলাপ ট্র্যাক বা রিপোর্ট</string>
<string name="versions">সংস্করণ</string> <string name="versions">সংস্করণ</string>
<string name="explore">অন্বেষণ</string>
<string name="syncing_FORMAT">%s সিঙ্ক হচ্ছে…</string> <string name="syncing_FORMAT">%s সিঙ্ক হচ্ছে…</string>
<string name="whats_new">নতুন কি</string> <string name="whats_new">নতুন কি</string>
<string name="themes">রঙ</string> <string name="themes">রঙ</string>
@ -204,12 +187,49 @@
<string name="waiting_to_start_installation">ইনস্টলেশন শুরু করার জন্য অপেক্ষা করা হচ্ছে…</string> <string name="waiting_to_start_installation">ইনস্টলেশন শুরু করার জন্য অপেক্ষা করা হচ্ছে…</string>
<string name="upstream_source_code_is_not_free">আপস্ট্রিম সোর্স কোড বিনামূল্যে নয়</string> <string name="upstream_source_code_is_not_free">আপস্ট্রিম সোর্স কোড বিনামূল্যে নয়</string>
<string name="validation_index_error_DESC">সূচক যাচাই করা যায়নি.</string> <string name="validation_index_error_DESC">সূচক যাচাই করা যায়নি.</string>
<string name="installed_applications">ইনস্টল করা অ্যাপ্লিকেশন</string>
<string name="update">হালনাগাদ</string> <string name="update">হালনাগাদ</string>
<string name="waiting_to_start_download">ডাউনলোড শুরু করার জন্য অপেক্ষা করা হচ্ছে…</string> <string name="waiting_to_start_download">ডাউনলোড শুরু করার জন্য অপেক্ষা করা হচ্ছে…</string>
<string name="prefs_language_title">ভাষা</string> <string name="prefs_language_title">ভাষা</string>
<string name="new_applications">নতুন অ্যাপ্লিকেশন</string>
<string name="cannot_open_link">লিংকটি ওপেন করা সম্ভব হয়নি</string> <string name="cannot_open_link">লিংকটি ওপেন করা সম্ভব হয়নি</string>
<string name="import_export">ইম্পোর্ট/এক্সপোর্ট</string> <string name="import_export">ইম্পোর্ট/এক্সপোর্ট</string>
<string name="import_settings_title">সেটিংস ইম্পোর্ট করুন</string> <string name="import_settings_title">সেটিংস ইম্পোর্ট করুন</string>
</resources> <string name="uninstalled_application">অপসারিত</string>
<string name="special_credits">বিশেষ কৃতজ্ঞতাস্বীকার</string>
<string name="error_shizuku_not_granted_DESC">শিজুকু সেবার অনুমতি দেওয়া হয়নি। শিজুকু অ্যাপটি দেখো</string>
<string name="ignore_signature_summary">*সতর্কতা* এপিকে ইন্সটলে সময় স্বাক্ষর যাচাইকরণ এড়াও, এলএসপোজড ব্যবহারকারী বা অগ্রগামী ব্যবহারকারীর জন্য</string>
<string name="contains_nsfw">নিষিদ্ধ বিষয়বস্ত বিদ্যমান</string>
<string name="label_open_video">ভিডিও</string>
<string name="export_settings_title">পছন্দসমূহ রপ্তানি করো</string>
<string name="insufficient_storage">অপর্যাপ্ত জায়গা</string>
<string name="shizuku_not_alive">শিজুকু চলছে না</string>
<string name="error_shizuku_not_running_DESC">শিজুকু চলছে না। অনুগ্রহ করে শিজুকু অ্যাপ দেখো</string>
<string name="export_repos_DESC">সব ভাণ্ডার ফাইলে রপ্তানি</string>
<string name="home_screen_swiping_DESC">ব্যবহারকারীকে মূলপাতাসমূহের মধ্যে টান দেওয়া অনুমোদন করো</string>
<string name="connection_error_DESC">সার্ভারে যুক্ত হতে ব্যর্থ</string>
<string name="shizuku_not_installed">শিজুকু ইন্সটল করা নেই</string>
<string name="installation_failed">ইন্সটল ব্যর্থ</string>
<string name="installation_failed_DESC">%s ইন্সটল করতে ব্যর্থ</string>
<string name="ignore_signature">স্বাক্ষর উপেক্ষা করো</string>
<string name="error_shizuku_service_unavailable">শিজুকু চলছে না</string>
<string name="error_shizuku_not_granted">শিজুকু অনুমতি নেই</string>
<string name="error_shizuku_not_installed">শিজুকু ইন্সটল করা হয়নি</string>
<string name="error_shizuku_not_installed_DESC">শিজুকু মনে হচ্ছে না ইন্সটল করা</string>
<string name="import_settings_DESC">পছন্দসমূহ ফাইল থেকে আমদানি করো</string>
<string name="export_settings_DESC">পছন্দসমূহ ফাইলে রপ্তানি করো</string>
<string name="import_repos_title">ভাণ্ডার আমদানি করো</string>
<string name="import_repos_DESC">ফাইল থেকে সব ভাণ্ডার আমদানি করো</string>
<string name="export_repos_title">ভাণ্ডার রপ্তানি</string>
<string name="has_non_free_components">অমুক্ত অংশ আছে</string>
<string name="has_tethered_network">একটি নির্দিষ্ট নেটওয়ার্ক সেবার সাথে আবদ্ধ</string>
<string name="home_screen_swiping">মূলপাতা টান দেওয়া(সোয়াইপিং)</string>
<string name="socket_error_DESC">সার্ভার নতুন প্যাকেট সরবরাহে ব্যর্থ।</string>
<string name="uninstalled_application_DESC">%s অপসারিত হয়েছে</string>
<string name="insufficient_storage_DESC">এই অ্যাপটি ইন্সটল করার জন্য পর্যাপ্ত জায়গা নেই। কিছু জায়গা পরিষ্কারের চেষ্টা করো</string>
<string name="open_shizuku">শিজুকু খুলো</string>
<string name="proxy_port_error_not_int">প্রক্সি পোর্ট শুধু পূর্ণ ধনাত্মক সংখ্যা হতে পারে</string>
<string name="require_background_access">পটভূমিতে চলার অনুমতি প্রয়োজন</string>
<string name="require_background_access_DESC">পটভূমি অনুমোদন প্রয়োজন পটভূমি সিঙ্কের জন্য</string>
<string name="repository_not_found">এই ভাণ্ডারগুলো পাওয়া যায়নি</string>
<string name="switch_to_default_installer">সহজাতে পরিবর্তন করো</string>
<string name="label_unknown_sdk">অজানা (%d)</string>
</resources>

View File

@ -16,7 +16,6 @@
<string name="changes">Canvis</string> <string name="changes">Canvis</string>
<string name="checking_repository">Comprovant repositori…</string> <string name="checking_repository">Comprovant repositori…</string>
<string name="cleanup_title">Interval de neteja de l\'APK</string> <string name="cleanup_title">Interval de neteja de l\'APK</string>
<string name="cleanup_description">Període per comprovar i eliminar els fitxers descarregats</string>
<string name="compiled_for_debugging">Compilat per a depuració</string> <string name="compiled_for_debugging">Compilat per a depuració</string>
<string name="confirmation">Confirmació</string> <string name="confirmation">Confirmació</string>
<string name="connecting">Connectant…</string> <string name="connecting">Connectant…</string>
@ -87,11 +86,8 @@
<string name="recently_updated">Recentment actualitzat</string> <string name="recently_updated">Recentment actualitzat</string>
<string name="repositories">Repositoris</string> <string name="repositories">Repositoris</string>
<string name="repository">Repositori</string> <string name="repository">Repositori</string>
<string name="repository_not_used_DESC">Aquest repositori no ha estat utilitzat tot i així. Gira\'l damunt per veure les aplicacions en ell.</string>
<string name="repository_unsigned_DESC">Sense signar. No podria verificar la llista d\'aplicació. Ser prudent descarregar aplicacions des de repositoris sense signar.</string> <string name="repository_unsigned_DESC">Sense signar. No podria verificar la llista d\'aplicació. Ser prudent descarregar aplicacions des de repositoris sense signar.</string>
<string name="requires_FORMAT">Requereix %s</string> <string name="requires_FORMAT">Requereix %s</string>
<string name="root_permission">Silenciós Instal·lar</string>
<string name="root_permission_description">Permet permís d\'arrel per silenciós instal·la</string>
<string name="save">Salva</string> <string name="save">Salva</string>
<string name="search">Recerca</string> <string name="search">Recerca</string>
<string name="select_mirror">Selecciona un mirall</string> <string name="select_mirror">Selecciona un mirall</string>
@ -112,10 +108,8 @@
<string name="unknown">Desconegut</string> <string name="unknown">Desconegut</string>
<string name="unknown_error_DESC">Error desconegut.</string> <string name="unknown_error_DESC">Error desconegut.</string>
<string name="unknown_FORMAT">Desconegut: %s</string> <string name="unknown_FORMAT">Desconegut: %s</string>
<string name="unsigned">Sense signar</string>
<string name="unstable_updates">Actualitzacions inestables</string> <string name="unstable_updates">Actualitzacions inestables</string>
<string name="unstable_updates_summary">Suggereix instal·lant versions inestables</string> <string name="unstable_updates_summary">Suggereix instal·lant versions inestables</string>
<string name="unverified">Sense verificar</string>
<string name="update">Actualització</string> <string name="update">Actualització</string>
<string name="updates">Actualitzacions</string> <string name="updates">Actualitzacions</string>
<string name="upstream_source_code_is_not_free">El codi font no és lliure</string> <string name="upstream_source_code_is_not_free">El codi font no és lliure</string>
@ -129,12 +123,7 @@
<string name="prefs_language_title">Llengua</string> <string name="prefs_language_title">Llengua</string>
<string name="prefs_personalization">Personalització</string> <string name="prefs_personalization">Personalització</string>
<string name="show_less">Mostrar menys</string> <string name="show_less">Mostrar menys</string>
<string name="latest">Més recent</string>
<string name="explore">Explora</string>
<string name="update_all">Actualitzar tot</string> <string name="update_all">Actualitzar tot</string>
<string name="installed_applications">Aplicacions instal·lades</string>
<string name="sort_filter">Ordenar i filtrar</string>
<string name="new_applications">Aplicacions noves</string>
<string name="fingerprint">Empremta digital</string> <string name="fingerprint">Empremta digital</string>
<string name="incompatible_platforms_DESC_FORMAT">El vostre %1$s el programa no és recolzat. Programes recolzats: %2$s.</string> <string name="incompatible_platforms_DESC_FORMAT">El vostre %1$s el programa no és recolzat. Programes recolzats: %2$s.</string>
<string name="all_applications">Totes les aplicacions</string> <string name="all_applications">Totes les aplicacions</string>
@ -165,17 +154,14 @@
<string name="license_FORMAT">%s llicència</string> <string name="license_FORMAT">%s llicència</string>
<string name="no_proxy">Cap proxy</string> <string name="no_proxy">Cap proxy</string>
<string name="link_copied_to_clipboard">El nexe va copiar a portapapers</string> <string name="link_copied_to_clipboard">El nexe va copiar a portapapers</string>
<string name="target">Objectiu</string>
<string name="license">Llicència</string> <string name="license">Llicència</string>
<string name="theme">Tema</string> <string name="theme">Tema</string>
<string name="invalid_username_format">Nul username format</string> <string name="invalid_username_format">Nul username format</string>
<string name="launch">Executa</string> <string name="launch">Executa</string>
<string name="light">Llum</string> <string name="light">Llum</string>
<string name="links">Nexes</string> <string name="links">Nexes</string>
<string name="list_animation">Animacions de llista</string>
<string name="size">Mida</string> <string name="size">Mida</string>
<string name="no_applications_installed">Aplicacions instal·lades no</string> <string name="no_applications_installed">Aplicacions instal·lades no</string>
<string name="list_animation_description">L\'espectacle llista animació en la pàgina major</string>
<string name="merging_FORMAT">Fusionant %s</string> <string name="merging_FORMAT">Fusionant %s</string>
<string name="network_error_DESC">Error de xarxa</string> <string name="network_error_DESC">Error de xarxa</string>
<string name="new_updates_available">Versions noves de les aplicacions disponibles</string> <string name="new_updates_available">Versions noves de les aplicacions disponibles</string>
@ -188,15 +174,11 @@
<string name="no_description_available_DESC">Cap descripció disponible</string> <string name="no_description_available_DESC">Cap descripció disponible</string>
<string name="notify_about_updates_summary">Mostra una notificació quan les versions noves són disponibles</string> <string name="notify_about_updates_summary">Mostra una notificació quan les versions noves són disponibles</string>
<string name="saving_details">Salvant detalls…</string> <string name="saving_details">Salvant detalls…</string>
<string name="screenshots">Captures de pantalla</string>
<string name="no_matching_applications_found">No podria trobar qualsevol tals aplicacions</string> <string name="no_matching_applications_found">No podria trobar qualsevol tals aplicacions</string>
<string name="notify_about_updates">Notifica sobre versions noves d\'aplicacions</string> <string name="notify_about_updates">Notifica sobre versions noves d\'aplicacions</string>
<string name="show_older_versions">Mostrar versions més velles</string> <string name="show_older_versions">Mostrar versions més velles</string>
<string name="suggested">Suggerit</string> <string name="suggested">Suggerit</string>
<string name="syncing">Sincronitzant</string> <string name="syncing">Sincronitzant</string>
<string name="no_internet">No teniu cap connexió a Internet</string>
<string name="allow_collapsing_toolbar_DESC">Permet que la barra d\'aplicacions superior s\'expandeixi i es col·lapsi</string>
<string name="allow_collapsing_toolbar">Permet que la barra d\'aplicacions superior s\'expandeixi</string>
<string name="io_error_DESC">No es pot realitzar determinades accions.</string> <string name="io_error_DESC">No es pot realitzar determinades accions.</string>
<string name="auto_update">Actualització automàtica d\'aplicacions</string> <string name="auto_update">Actualització automàtica d\'aplicacions</string>
<string name="auto_update_apps">Intenta instal·lar les actualitzacions automàticament</string> <string name="auto_update_apps">Intenta instal·lar les actualitzacions automàticament</string>
@ -208,7 +190,6 @@
<string name="force_clean_up_DESC">Neteja els fitxers redundants</string> <string name="force_clean_up_DESC">Neteja els fitxers redundants</string>
<string name="enable_repo">Habiliteu el repositori</string> <string name="enable_repo">Habiliteu el repositori</string>
<string name="waiting_to_start_installation">S\'està esperant per iniciar la instal·lació…</string> <string name="waiting_to_start_installation">S\'està esperant per iniciar la instal·lació…</string>
<string name="restart_app">Reinicieu Droid-ify per veure els canvis</string>
<string name="installing">Instal·lació</string> <string name="installing">Instal·lació</string>
<string name="import_settings_DESC">Importa configuració i preferits des de fitxer</string> <string name="import_settings_DESC">Importa configuració i preferits des de fitxer</string>
<string name="has_tethered_network">Enllaçat a un determinat servei de xarxa</string> <string name="has_tethered_network">Enllaçat a un determinat servei de xarxa</string>
@ -222,4 +203,4 @@
<string name="export_repos_title">Exporta els repositoris</string> <string name="export_repos_title">Exporta els repositoris</string>
<string name="export_repos_DESC">Exporta tots els repositoris a un fitxer</string> <string name="export_repos_DESC">Exporta tots els repositoris a un fitxer</string>
<string name="home_screen_swiping">Lliscar la pantalla d\'inici</string> <string name="home_screen_swiping">Lliscar la pantalla d\'inici</string>
</resources> </resources>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="action_failed">کردارەکە شکستیهێنا</string>
<string name="amoled">ڕەش</string>
<string name="dark">تاریک</string>
<string name="address">ناونیشان</string>
<string name="always">هەمووکات</string>
</resources>

View File

@ -78,8 +78,6 @@
<string name="license">Licence</string> <string name="license">Licence</string>
<string name="license_FORMAT">%s licence</string> <string name="license_FORMAT">%s licence</string>
<string name="links">Odkazy</string> <string name="links">Odkazy</string>
<string name="list_animation">List Animací</string>
<string name="list_animation_description">Zobrazit animaci listu na hlavní stránce</string>
<string name="merging_FORMAT">Slučování %s</string> <string name="merging_FORMAT">Slučování %s</string>
<string name="name">Název</string> <string name="name">Název</string>
<string name="network_error_DESC">Chyba sítě</string> <string name="network_error_DESC">Chyba sítě</string>
@ -110,14 +108,10 @@
<string name="promotes_non_free_software">Propaguje ne-svobodný software</string> <string name="promotes_non_free_software">Propaguje ne-svobodný software</string>
<string name="provided_by_FORMAT">Poskytuje %s</string> <string name="provided_by_FORMAT">Poskytuje %s</string>
<string name="proxy">Proxy</string> <string name="proxy">Proxy</string>
<string name="repository_not_used_DESC">Tento zdroj zatím použit. Zapněte jej pro zobrazení aplikací na něm.</string>
<string name="repository_unsigned_DESC">Nepodepsáno. Nezdařilo se ověřit seznam aplikací. Buďte opatrní při stahování aplikací z nepodepsaných zdrojů.</string> <string name="repository_unsigned_DESC">Nepodepsáno. Nezdařilo se ověřit seznam aplikací. Buďte opatrní při stahování aplikací z nepodepsaných zdrojů.</string>
<string name="requires_FORMAT">Vyžaduje %s</string> <string name="requires_FORMAT">Vyžaduje %s</string>
<string name="root_permission">Tichá instalace</string>
<string name="root_permission_description">Povolit root oprávnění pro tiché instalace</string>
<string name="save">Uložit</string> <string name="save">Uložit</string>
<string name="saving_details">Ukládám detaily…</string> <string name="saving_details">Ukládám detaily…</string>
<string name="screenshots">Snímky obrazovky</string>
<string name="search">Hledat</string> <string name="search">Hledat</string>
<string name="share">Sdílet</string> <string name="share">Sdílet</string>
<string name="show_more">Zobrazit více</string> <string name="show_more">Zobrazit více</string>
@ -137,7 +131,6 @@
<string name="syncing_FORMAT">Synchronizuji %s…</string> <string name="syncing_FORMAT">Synchronizuji %s…</string>
<string name="system">Systém</string> <string name="system">Systém</string>
<string name="tap_to_install_DESC">Klikněte pro instalaci.</string> <string name="tap_to_install_DESC">Klikněte pro instalaci.</string>
<string name="target">Cíl</string>
<string name="theme">Téma</string> <string name="theme">Téma</string>
<string name="themes">Témata</string> <string name="themes">Témata</string>
<string name="tracks_or_reports_your_activity">Sleduje nebo hlásí vaší aktivitu</string> <string name="tracks_or_reports_your_activity">Sleduje nebo hlásí vaší aktivitu</string>
@ -145,9 +138,7 @@
<string name="unknown">Neznámé</string> <string name="unknown">Neznámé</string>
<string name="unknown_error_DESC">Neznámá chyba.</string> <string name="unknown_error_DESC">Neznámá chyba.</string>
<string name="unknown_FORMAT">Neznámé: %s</string> <string name="unknown_FORMAT">Neznámé: %s</string>
<string name="unsigned">Nepodepsáno</string>
<string name="unstable_updates">Nestabilní aktualizace</string> <string name="unstable_updates">Nestabilní aktualizace</string>
<string name="unverified">Neověřeno</string>
<string name="update">Aktualizovat</string> <string name="update">Aktualizovat</string>
<string name="updates">Aktualizace</string> <string name="updates">Aktualizace</string>
<string name="upstream_source_code_is_not_free">Originální zdrojový kód není svobodný</string> <string name="upstream_source_code_is_not_free">Originální zdrojový kód není svobodný</string>
@ -163,12 +154,7 @@
<string name="prefs_language_title">Jazyk</string> <string name="prefs_language_title">Jazyk</string>
<string name="prefs_personalization">Personalizace</string> <string name="prefs_personalization">Personalizace</string>
<string name="show_less">Zobrazit méně</string> <string name="show_less">Zobrazit méně</string>
<string name="latest">Nejnovější</string>
<string name="explore">Prozkoumat</string>
<string name="update_all">Aktualizovat vše</string> <string name="update_all">Aktualizovat vše</string>
<string name="installed_applications">Instalované aplikace</string>
<string name="sort_filter">Třídit &amp; Filtrovat</string>
<string name="new_applications">Nové aplikace</string>
<string name="action_failed">Akce se nezdařila</string> <string name="action_failed">Akce se nezdařila</string>
<string name="all_applications">Všechny aplikace</string> <string name="all_applications">Všechny aplikace</string>
<string name="amoled">Černá</string> <string name="amoled">Černá</string>
@ -194,12 +180,8 @@
<item quantity="other">hodin</item> <item quantity="other">hodin</item>
</plurals> </plurals>
<string name="cleanup_title">Interval čištění APK</string> <string name="cleanup_title">Interval čištění APK</string>
<string name="cleanup_description">Období pro kontrolu a odstranění stažených souborů</string>
<string name="only_on_wifi_with_charging">Pouze na Wi-Fi a při nabíjení</string> <string name="only_on_wifi_with_charging">Pouze na Wi-Fi a při nabíjení</string>
<string name="io_error_DESC">Nepodařilo se vykonat některé akce.</string> <string name="io_error_DESC">Nepodařilo se vykonat některé akce.</string>
<string name="no_internet">Nejste připojeni k internetu</string>
<string name="allow_collapsing_toolbar">Povolení rozšíření horního panelu aplikací</string>
<string name="allow_collapsing_toolbar_DESC">Povolení rozbalování a sbalování horního panelu aplikace</string>
<string name="material_you_desc">Použít barevný motiv Material You</string> <string name="material_you_desc">Použít barevný motiv Material You</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="favourites">Oblíbené</string> <string name="favourites">Oblíbené</string>
@ -207,7 +189,6 @@
<string name="force_clean_up">Vynutit vyčištění</string> <string name="force_clean_up">Vynutit vyčištění</string>
<string name="repository_unreachable">Repozitář nedostupný</string> <string name="repository_unreachable">Repozitář nedostupný</string>
<string name="enable_repo">Povolit repozitář</string> <string name="enable_repo">Povolit repozitář</string>
<string name="restart_app">Pro zobrazení změn restartujte Droid-ify</string>
<string name="waiting_to_start_installation">Čekání na spuštění instalace…</string> <string name="waiting_to_start_installation">Čekání na spuštění instalace…</string>
<string name="installing">Instalace</string> <string name="installing">Instalace</string>
<string name="auto_update">Automatická aktualizace aplikací</string> <string name="auto_update">Automatická aktualizace aplikací</string>
@ -221,7 +202,6 @@
<string name="special_credits">Speciální poděkování</string> <string name="special_credits">Speciální poděkování</string>
<string name="home_screen_swiping">Posouvání na domovské stránce</string> <string name="home_screen_swiping">Posouvání na domovské stránce</string>
<string name="home_screen_swiping_DESC">Umožnit uživateli posouvat mezi stránkami na domovské stránce</string> <string name="home_screen_swiping_DESC">Umožnit uživateli posouvat mezi stránkami na domovské stránce</string>
<string name="label_copy">Kopírovat</string>
<string name="repository_not_found">Následující repozitář nebyl nalezen</string> <string name="repository_not_found">Následující repozitář nebyl nalezen</string>
<string name="proxy_port_error_not_int">Port proxy smí být pouze celé číslo</string> <string name="proxy_port_error_not_int">Port proxy smí být pouze celé číslo</string>
<string name="import_settings_title">Importovat nastavení</string> <string name="import_settings_title">Importovat nastavení</string>
@ -245,4 +225,14 @@
<string name="uninstalled_application">Odinstalováno</string> <string name="uninstalled_application">Odinstalováno</string>
<string name="insufficient_storage">Nedostatek místa</string> <string name="insufficient_storage">Nedostatek místa</string>
<string name="insufficient_storage_DESC">Na zařízení není dostatek místa k instalaci této aplikace. Zkuste uvolnit trochu místa</string> <string name="insufficient_storage_DESC">Na zařízení není dostatek místa k instalaci této aplikace. Zkuste uvolnit trochu místa</string>
</resources> <string name="error_shizuku_service_unavailable">Shizuku není spuštěno</string>
<string name="error_shizuku_not_granted">Chybějící oprávnění Shizuku</string>
<string name="error_shizuku_not_granted_DESC">Nebylo uděleno oprávnění ke službě Shizuku. Zkontrolujte prosím aplikaci Shizuku</string>
<string name="error_shizuku_not_installed_DESC">Aplikace Shizuku nejspíše není nainstalována</string>
<string name="switch_to_default_installer">Přepnout na výchozí</string>
<string name="label_unknown_sdk">Neznámé (%d)</string>
<string name="error_shizuku_not_running_DESC">Služba Shizuku není spuštěna. Zkontrolujte prosím aplikaci Shizuku</string>
<string name="error_shizuku_not_installed">Shizuku není nainstalováno</string>
<string name="label_open_video">Video</string>
<string name="open_shizuku">Otevřít Shizuku</string>
</resources>

View File

@ -14,8 +14,6 @@
<string name="cancel">Annuller</string> <string name="cancel">Annuller</string>
<string name="add_repository">Tilføj repository</string> <string name="add_repository">Tilføj repository</string>
<string name="address">Adresse</string> <string name="address">Adresse</string>
<string name="allow_collapsing_toolbar">Lad øverste app-linje udvide sig</string>
<string name="allow_collapsing_toolbar_DESC">Lad øverste app-linje udvide og skjule sig</string>
<string name="anti_features">Anti-funktioner</string> <string name="anti_features">Anti-funktioner</string>
<string name="author_email">Udviklerens e-mail</string> <string name="author_email">Udviklerens e-mail</string>
<string name="author_website">Udviklerens hjemmeside</string> <string name="author_website">Udviklerens hjemmeside</string>
@ -24,7 +22,6 @@
<string name="changelog">Ændringslog</string> <string name="changelog">Ændringslog</string>
<string name="changes">Ændringer</string> <string name="changes">Ændringer</string>
<string name="cleanup_title">APK-oprydningsinterval</string> <string name="cleanup_title">APK-oprydningsinterval</string>
<string name="cleanup_description">Periodisk kontrol og fjernelse af hentede filer</string>
<string name="confirmation">Bekræftelse</string> <string name="confirmation">Bekræftelse</string>
<string name="connecting">Forbinder…</string> <string name="connecting">Forbinder…</string>
<string name="contains_non_free_media">Indeholder ikke-frie medier</string> <string name="contains_non_free_media">Indeholder ikke-frie medier</string>
@ -42,10 +39,10 @@
<string name="downloaded_FORMAT">Hentet %s</string> <string name="downloaded_FORMAT">Hentet %s</string>
<string name="downloading">Henter</string> <string name="downloading">Henter</string>
<string name="downloading_FORMAT">Henter %s…</string> <string name="downloading_FORMAT">Henter %s…</string>
<string name="import_export">Import/Eksport</string> <string name="import_export">Import/eksport</string>
<string name="import_settings_title">Importér Indstillinger</string> <string name="import_settings_title">Importér indstillinger</string>
<string name="import_settings_DESC">Importér indstillinger og favoritter fra fil</string> <string name="import_settings_DESC">Importér indstillinger og favoritter fra fil</string>
<string name="export_settings_title">Eksportér Indstillinger</string> <string name="export_settings_title">Eksportér indstillinger</string>
<string name="favourites">Favoritter</string> <string name="favourites">Favoritter</string>
<string name="file_format_error_DESC">Ugyldigt filformat.</string> <string name="file_format_error_DESC">Ugyldigt filformat.</string>
<string name="fingerprint">Fingeraftryk</string> <string name="fingerprint">Fingeraftryk</string>
@ -63,8 +60,8 @@
<string name="connection_error_DESC">Kunne ikke forbinde til server</string> <string name="connection_error_DESC">Kunne ikke forbinde til server</string>
<string name="ignore_all_updates">Ignorer alle nye versioner</string> <string name="ignore_all_updates">Ignorer alle nye versioner</string>
<string name="incompatible_api_DESC_FORMAT">Din %1$s (API-version %2$d) understøttes ikke. %3$s</string> <string name="incompatible_api_DESC_FORMAT">Din %1$s (API-version %2$d) understøttes ikke. %3$s</string>
<string name="incompatible_api_max_DESC_FORMAT">Maksimal API-version er %d.</string> <string name="incompatible_api_max_DESC_FORMAT">Maks. API-version er %d.</string>
<string name="incompatible_api_min_DESC_FORMAT">Minimum API-version er %d.</string> <string name="incompatible_api_min_DESC_FORMAT">Min. API-version er %d.</string>
<string name="incompatible_features_DESC">Manglende funktioner.</string> <string name="incompatible_features_DESC">Manglende funktioner.</string>
<string name="incompatible_older_DESC">Denne version er ældre end den, der er installeret på din enhed. Afinstaller den først.</string> <string name="incompatible_older_DESC">Denne version er ældre end den, der er installeret på din enhed. Afinstaller den først.</string>
<string name="incompatible_version">Inkompatibel version</string> <string name="incompatible_version">Inkompatibel version</string>
@ -74,7 +71,7 @@
<string name="install">Installer</string> <string name="install">Installer</string>
<string name="install_types">Installationstyper</string> <string name="install_types">Installationstyper</string>
<string name="installer">Installatør</string> <string name="installer">Installatør</string>
<string name="shizuku_installer">Shizuku Installatør</string> <string name="shizuku_installer">Shizuku-installatør</string>
<string name="shizuku_not_alive">Shizuku kører ikke</string> <string name="shizuku_not_alive">Shizuku kører ikke</string>
<string name="shizuku_not_installed">Shizuku er ikke installeret</string> <string name="shizuku_not_installed">Shizuku er ikke installeret</string>
<string name="installing">Installerer</string> <string name="installing">Installerer</string>
@ -83,19 +80,17 @@
<string name="invalid_fingerprint_format">Ugyldigt fingeraftryksformat</string> <string name="invalid_fingerprint_format">Ugyldigt fingeraftryksformat</string>
<string name="invalid_username_format">Ugyldigt brugernavnsformat</string> <string name="invalid_username_format">Ugyldigt brugernavnsformat</string>
<string name="io_error_DESC">Kan ikke udføre visse handlinger.</string> <string name="io_error_DESC">Kan ikke udføre visse handlinger.</string>
<string name="label_copy">Kopiér</string>
<string name="launch">Start</string> <string name="launch">Start</string>
<string name="license">Licens</string> <string name="license">Licens</string>
<string name="license_FORMAT">%s licens</string> <string name="license_FORMAT">%s licens</string>
<string name="light">Lys</string> <string name="light">Lys</string>
<string name="link_copied_to_clipboard">Link kopieret</string> <string name="link_copied_to_clipboard">Link kopieret</string>
<string name="links">Links</string> <string name="links">Links</string>
<string name="list_animation">Listeanimationer</string> <string name="home_screen_swiping">Strygning på startside</string>
<string name="home_screen_swiping">Strygning på Startskærm</string>
<string name="socket_error_DESC">Server kunne ikke levere ny pakke.</string> <string name="socket_error_DESC">Server kunne ikke levere ny pakke.</string>
<string name="http_proxy">HTTP-proxy</string> <string name="http_proxy">HTTP-proxy</string>
<string name="incompatible_platforms_DESC_FORMAT">Din %1$s platform understøttes ikke. Understøttede platforme: %2$s.</string> <string name="incompatible_platforms_DESC_FORMAT">Din %1$s platform understøttes ikke. Understøttede platforme: %2$s.</string>
<string name="root_installer">Root Installatør</string> <string name="root_installer">Root-installatør</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="material_you_desc">Brug Material You-farvetema</string> <string name="material_you_desc">Brug Material You-farvetema</string>
<string name="merging_FORMAT">Fletter %s</string> <string name="merging_FORMAT">Fletter %s</string>
@ -110,7 +105,6 @@
<string name="no_applications_available">Ingen tilgængelige applikationer</string> <string name="no_applications_available">Ingen tilgængelige applikationer</string>
<string name="no_applications_installed">Ingen installerede applikationer</string> <string name="no_applications_installed">Ingen installerede applikationer</string>
<string name="no_description_available_DESC">Ingen beskrivelse tilgængelig</string> <string name="no_description_available_DESC">Ingen beskrivelse tilgængelig</string>
<string name="no_internet">Du har ingen internetforbindelse</string>
<string name="no_matching_applications_found">Kunne ikke finde sådanne applikationer</string> <string name="no_matching_applications_found">Kunne ikke finde sådanne applikationer</string>
<string name="no_proxy">Ingen proxy</string> <string name="no_proxy">Ingen proxy</string>
<string name="notify_about_updates">Underret om opdateringer</string> <string name="notify_about_updates">Underret om opdateringer</string>
@ -119,10 +113,10 @@
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="only_compatible_with_FORMAT">Kun kompatibel med %s</string> <string name="only_compatible_with_FORMAT">Kun kompatibel med %s</string>
<string name="only_on_wifi">Kun på Wi-Fi</string> <string name="only_on_wifi">Kun på Wi-Fi</string>
<string name="only_on_wifi_with_charging">Kun på Wi-Fi &amp; Opladning</string> <string name="only_on_wifi_with_charging">Kun på Wi-Fi og opladning</string>
<string name="open_DESC_FORMAT">Åbn %s?</string> <string name="open_DESC_FORMAT">Åbn %s?</string>
<string name="other">Andet</string> <string name="other">Andet</string>
<string name="parsing_index_error_DESC">Kunne ikke analysere indeksfilen.</string> <string name="parsing_index_error_DESC">Kunne ikke fortolke indeksfilen.</string>
<string name="password">Adgangskode</string> <string name="password">Adgangskode</string>
<string name="password_missing">Manglende adgangskode</string> <string name="password_missing">Manglende adgangskode</string>
<string name="plus_more_FORMAT">+%d mere</string> <string name="plus_more_FORMAT">+%d mere</string>
@ -136,10 +130,8 @@
<string name="proxy_port_error_not_int">Proxyport kan kun være et heltal</string> <string name="proxy_port_error_not_int">Proxyport kan kun være et heltal</string>
<string name="proxy_type">Proxytype</string> <string name="proxy_type">Proxytype</string>
<string name="requires_FORMAT">Kræver %s</string> <string name="requires_FORMAT">Kræver %s</string>
<string name="restart_app">Genstart Droid-ify for at se ændringer</string>
<string name="save">Gem</string> <string name="save">Gem</string>
<string name="saving_details">Gemmer detaljer…</string> <string name="saving_details">Gemmer detaljer…</string>
<string name="screenshots">Skærmbilleder</string>
<string name="settings">Indstillinger</string> <string name="settings">Indstillinger</string>
<string name="share">Del</string> <string name="share">Del</string>
<string name="signed_using_unsafe_algorithm">Signeret med en usikker algoritme</string> <string name="signed_using_unsafe_algorithm">Signeret med en usikker algoritme</string>
@ -153,26 +145,20 @@
<string name="themes">Temaer</string> <string name="themes">Temaer</string>
<string name="tracks_or_reports_your_activity">Sporer eller rapporterer din aktivitet</string> <string name="tracks_or_reports_your_activity">Sporer eller rapporterer din aktivitet</string>
<string name="repositories">Repositories</string> <string name="repositories">Repositories</string>
<string name="repository_not_used_DESC">Dette repository er ikke blevet brugt endnu. Slå det til for at se applikationer i den.</string>
<string name="repository_not_found">Følgende repository blev ikke fundet</string> <string name="repository_not_found">Følgende repository blev ikke fundet</string>
<string name="repository_unsigned_DESC">Usigneret. Kunne ikke verificere applikationslisten. Vær forsigtig med at hente applikationer fra usignerede repositories.</string> <string name="repository_unsigned_DESC">Usigneret. Kunne ikke verificere applikationslisten. Vær forsigtig med at hente applikationer fra usignerede repositories.</string>
<string name="repository_unreachable">Repository utilgængeligt</string> <string name="repository_unreachable">Repository utilgængeligt</string>
<string name="root_permission">Stille Installation</string>
<string name="root_permission_description">Tillad root for stille installationer</string>
<string name="select_mirror">Vælg et mirror</string> <string name="select_mirror">Vælg et mirror</string>
<string name="socks_proxy">SOCKS-proxy</string> <string name="socks_proxy">SOCKS-proxy</string>
<string name="sync_repositories">Synkroniser repositories</string> <string name="sync_repositories">Synkroniser repositories</string>
<string name="sync_repositories_automatically">Synkroniser repositories automatisk</string> <string name="sync_repositories_automatically">Synkroniser repositories automatisk</string>
<string name="target">Mål</string>
<string name="repository">Repository</string> <string name="repository">Repository</string>
<string name="uninstall">Afinstaller</string> <string name="uninstall">Afinstaller</string>
<string name="unknown">Ukendt</string> <string name="unknown">Ukendt</string>
<string name="unknown_error_DESC">Ukendt fejl.</string> <string name="unknown_error_DESC">Ukendt fejl.</string>
<string name="unknown_FORMAT">Ukendt: %s</string> <string name="unknown_FORMAT">Ukendt: %s</string>
<string name="unsigned">Usigneret</string>
<string name="unstable_updates">Ustabile opdateringer</string> <string name="unstable_updates">Ustabile opdateringer</string>
<string name="unstable_updates_summary">Foreslå at installere ustabile versioner</string> <string name="unstable_updates_summary">Foreslå at installere ustabile versioner</string>
<string name="unverified">Ikke verificeret</string>
<string name="upstream_source_code_is_not_free">Den oprindelige kildekode er ikke gratis</string> <string name="upstream_source_code_is_not_free">Den oprindelige kildekode er ikke gratis</string>
<string name="username">Brugernavn</string> <string name="username">Brugernavn</string>
<string name="username_missing">Manglende brugernavn</string> <string name="username_missing">Manglende brugernavn</string>
@ -187,18 +173,13 @@
<string name="prefs_language_title">Sprog</string> <string name="prefs_language_title">Sprog</string>
<string name="prefs_personalization">Personalisering</string> <string name="prefs_personalization">Personalisering</string>
<string name="show_less">Vis mindre</string> <string name="show_less">Vis mindre</string>
<string name="latest">Seneste</string>
<string name="explore">Udforsk</string>
<string name="update_all">Opdater alle</string> <string name="update_all">Opdater alle</string>
<string name="installed_applications">Installerede applikationer</string>
<string name="sort_filter">Sortér &amp; Filtrér</string>
<string name="new_applications">Nye applikationer</string>
<string name="updates">Opdateringer</string> <string name="updates">Opdateringer</string>
<string name="donate">Donér</string> <string name="donate">Donér</string>
<string name="export_settings_DESC">Eksportér indstillinger og favoritter til fil</string> <string name="export_settings_DESC">Eksportér indstillinger og favoritter til fil</string>
<string name="has_non_free_components">Har ikke-frie komponenter</string> <string name="has_non_free_components">Har ikke-frie komponenter</string>
<string name="has_tethered_network">Bundet til en bestemt netværkstjeneste</string> <string name="has_tethered_network">Bundet til en bestemt netværkstjeneste</string>
<string name="home_screen_swiping_DESC">Tillad at stryge mellem sider på startskærm</string> <string name="home_screen_swiping_DESC">Tillad at stryge mellem sider på startside</string>
<string name="ignore_this_update">Ignorer denne version</string> <string name="ignore_this_update">Ignorer denne version</string>
<string name="incompatible_signature_DESC">Denne version er signeret med et andet certifikat end den, der er installeret på din enhed. Afinstaller den først.</string> <string name="incompatible_signature_DESC">Denne version er signeret med et andet certifikat end den, der er installeret på din enhed. Afinstaller den først.</string>
<string name="installed">Installeret</string> <string name="installed">Installeret</string>
@ -220,26 +201,35 @@
<string name="compiled_for_debugging">Kompileret til fejlfinding</string> <string name="compiled_for_debugging">Kompileret til fejlfinding</string>
<string name="delete_repository_DESC">Slet repositoriet?</string> <string name="delete_repository_DESC">Slet repositoriet?</string>
<string name="edit_repository">Rediger repository</string> <string name="edit_repository">Rediger repository</string>
<string name="import_repos_title">Importér Repositories</string> <string name="import_repos_title">Importér repositories</string>
<string name="import_repos_DESC">Importér alle repositories fra fil</string> <string name="import_repos_DESC">Importér alle repositories fra fil</string>
<string name="export_repos_title">Eksportér Repositories</string> <string name="export_repos_title">Eksportér repositories</string>
<string name="export_repos_DESC">Eksportér alle repositories til fil</string> <string name="export_repos_DESC">Eksportér alle repositories til fil</string>
<string name="enable_repo">Aktivér repositoriet</string> <string name="enable_repo">Aktivér repositoriet</string>
<string name="credits">Krediteringer</string> <string name="credits">Krediteringer</string>
<string name="update">Opdatering</string> <string name="update">Opdatering</string>
<string name="list_animation_description">Vis listeanimation på hovedsiden</string>
<string name="require_background_access_DESC">Baggrundsadgang er nødvendig for at køre baggrundssynkronisering korrekt</string> <string name="require_background_access_DESC">Baggrundsadgang er nødvendig for at køre baggrundssynkronisering korrekt</string>
<string name="require_background_access">Kræver Baggrundsadgang</string> <string name="require_background_access">Kræver baggrundsadgang</string>
<string name="legacy_installer">Ældre Installatør</string> <string name="legacy_installer">Ældre installatør</string>
<string name="session_installer">Session Installatør</string> <string name="session_installer">Sessionsinstallatør</string>
<string name="special_credits">Særlige Krediteringer</string> <string name="special_credits">Særlige Krediteringer</string>
<string name="contains_nsfw">Indeholder potentielt stødende indhold</string> <string name="contains_nsfw">Indeholder potentielt stødende indhold</string>
<string name="ignore_signature_summary">*Advarsel* Ignorer signaturverifikation ved APK-installation; for LSPosed- eller avancerede brugere</string> <string name="ignore_signature_summary">*Advarsel* Ignorer signaturverifikation ved APK-installation. For LSPosed- eller avancerede brugere</string>
<string name="installation_failed">Installation Mislykkedes</string> <string name="installation_failed">Installation mislykkedes</string>
<string name="installation_failed_DESC">Kunne ikke installere %s</string> <string name="installation_failed_DESC">Kunne ikke installere %s</string>
<string name="uninstalled_application">Afinstalleret</string> <string name="uninstalled_application">Afinstalleret</string>
<string name="uninstalled_application_DESC">%s blev afinstalleret</string> <string name="uninstalled_application_DESC">%s blev afinstalleret</string>
<string name="ignore_signature">Ignorer Signatur</string> <string name="ignore_signature">Ignorer signatur</string>
<string name="insufficient_storage">Utilstrækkelig plads</string> <string name="insufficient_storage">Utilstrækkelig plads</string>
<string name="insufficient_storage_DESC">Enheden har ikke nok ledig plads til at installere denne applikation. Prøv at frigøre noget plads</string> <string name="insufficient_storage_DESC">Enheden har ikke nok ledig plads til at installere denne applikation. Prøv at frigøre noget plads</string>
</resources> <string name="error_shizuku_not_running_DESC">Shizuku-tjenesten kører ikke. Tjek venligst i Shizuku-appen</string>
<string name="error_shizuku_not_granted">Shizuku-tilladelse mangler</string>
<string name="error_shizuku_not_granted_DESC">Shizuku-tilladelse er ikke givet. Tjek venligst i Shizuku-appen</string>
<string name="error_shizuku_not_installed">Shizuku ikke installeret</string>
<string name="error_shizuku_not_installed_DESC">Shizuku ser ikke ud til at være installeret</string>
<string name="open_shizuku">Åbn Shizuku</string>
<string name="label_unknown_sdk">Ukendt (%d)</string>
<string name="switch_to_default_installer">Skift til standard</string>
<string name="label_open_video">Video</string>
<string name="error_shizuku_service_unavailable">Shizuku kører ikke</string>
</resources>

View File

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="add_repository">Repository hinzufügen</string> <string name="add_repository">Paketquelle hinzufügen</string>
<string name="all_applications">Alle Anwendungen</string> <string name="all_applications">Alle Apps</string>
<string name="all_applications_up_to_date">All deine Anwendungen sind aktuell</string> <string name="all_applications_up_to_date">Alle Apps sind aktuell</string>
<string name="already_exists">Bereits vorhanden</string> <string name="already_exists">Bereits vorhanden</string>
<string name="always">Immer</string> <string name="always">Immer</string>
<string name="address">Adresse</string> <string name="address">Adresse</string>
<string name="action_failed">Vorgang fehlgeschlagen</string> <string name="action_failed">Vorgang fehlgeschlagen</string>
<string name="amoled">Schwarz</string> <string name="amoled">Schwarz</string>
<string name="application">Anwendung</string> <string name="application">App</string>
<string name="anti_features">Unerwünschte Merkmale</string> <string name="anti_features">Unerwünschte Merkmale</string>
<string name="available">Entdecken</string> <string name="available">Entdecken</string>
<string name="cancel">Abbrechen</string> <string name="cancel">Abbrechen</string>
<string name="application_not_found">Diese Anwendung konnte nicht gefunden werden</string> <string name="application_not_found">App konnte nicht gefunden werden</string>
<string name="bug_tracker">Fehlerverwaltung</string> <string name="bug_tracker">Fehlerverwaltung</string>
<string name="changelog">Änderungsprotokoll</string> <string name="changelog">Änderungsprotokoll</string>
<string name="cant_edit_sync_DESC">Die Paketquelle kann nicht bearbeitet werden, da sie gerade synchronisiert wird.</string> <string name="cant_edit_sync_DESC">Die Paketquelle kann nicht bearbeitet werden, da sie gerade synchronisiert wird.</string>
<string name="checking_repository">Paketquelle wird abgefragt </string> <string name="checking_repository">Paketquelle wird abgefragt </string>
<string name="compiled_for_debugging">Kompiliert für die Fehlersuche</string> <string name="compiled_for_debugging">Kompiliert für die Fehlersuche</string>
<string name="connecting">Verbinde </string> <string name="connecting">Wird verbunden </string>
<string name="confirmation">Bestätigung</string> <string name="confirmation">Bestätigung</string>
<string name="could_not_validate_FORMAT">Konnte %s nicht validieren</string> <string name="could_not_validate_FORMAT">%s konnte nicht überprüft werden</string>
<string name="dark">Dunkel</string> <string name="dark">Dunkel</string>
<string name="contains_non_free_media">Enthält nicht-freie Medien</string> <string name="contains_non_free_media">Enthält nicht-freie Medien</string>
<string name="credits">Mitwirkende</string> <string name="credits">Mitwirkende</string>
@ -37,146 +37,132 @@
<string name="incompatible_features_DESC">Fehlende Funktionen.</string> <string name="incompatible_features_DESC">Fehlende Funktionen.</string>
<string name="incompatible_platforms_DESC_FORMAT">Deine %1$s-Plattform wird nicht unterstützt. Unterstützte Plattformen: %2$s.</string> <string name="incompatible_platforms_DESC_FORMAT">Deine %1$s-Plattform wird nicht unterstützt. Unterstützte Plattformen: %2$s.</string>
<string name="installed">Installiert</string> <string name="installed">Installiert</string>
<string name="incompatible_versions_summary">Mit dem Gerät inkompatible Anwendungsversionen anzeigen</string> <string name="incompatible_versions_summary">Mit diesem Gerät inkompatible App-Versionen anzeigen</string>
<string name="install_types">Installationstypen</string> <string name="install_types">Installationsarten</string>
<string name="install">Installieren</string> <string name="install">Installieren</string>
<string name="integrity_check_error_DESC">Integrität konnte nicht überprüft werden.</string> <string name="integrity_check_error_DESC">Integrität konnte nicht überprüft werden.</string>
<string name="invalid_metadata_error_DESC">Ungültige Metadaten.</string> <string name="invalid_metadata_error_DESC">Ungültige Metadaten.</string>
<string name="invalid_signature_error_DESC">Ungültige Signatur.</string> <string name="invalid_signature_error_DESC">Ungültige Signatur.</string>
<string name="launch">Öffnen</string> <string name="launch">Öffnen</string>
<string name="light">Hell</string> <string name="light">Hell</string>
<string name="list_animation_description">Listenanimation auf der Hauptseite anzeigen</string> <string name="new_updates_available">Neue Versionen von Apps verfügbar</string>
<string name="new_updates_available">Neue Anwendungsversionen verfügbar</string>
<string name="never">Nie</string> <string name="never">Nie</string>
<string name="network_error_DESC">Netzwerkfehler</string> <string name="network_error_DESC">Netzwerkfehler</string>
<string name="notify_about_updates">Über neue Versionen benachrichtigen</string> <string name="notify_about_updates">Update-Benachrichtigungen</string>
<string name="no_description_available_DESC">Keine Beschreibung vorhanden</string> <string name="no_description_available_DESC">Keine Beschreibung vorhanden</string>
<string name="no_matching_applications_found">Keine derartigen Anwendungen konnten gefunden werden</string> <string name="no_matching_applications_found">Keine derartigen Apps auffindbar</string>
<string name="no_applications_installed">Keine installierten Anwendungen</string> <string name="no_applications_installed">Keine installierten Apps</string>
<string name="only_on_wifi">Nur bei Wi-Fi</string> <string name="only_on_wifi">Nur mit WLAN</string>
<string name="open_DESC_FORMAT">Öffne %s\?</string> <string name="open_DESC_FORMAT">%s öffnen?</string>
<string name="other">Andere</string> <string name="other">Andere</string>
<string name="number_of_applications">Anzahl der Anwendungen</string> <string name="number_of_applications">Anzahl der Apps</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="permissions">Berechtigungen</string> <string name="permissions">Berechtigungen</string>
<string name="plus_more_FORMAT">+%d mehr</string> <string name="plus_more_FORMAT">+%d mehr</string>
<string name="settings">Einstellungen</string> <string name="settings">Einstellungen</string>
<string name="processing_FORMAT">Verarbeitung %1$s …</string> <string name="processing_FORMAT">%1$s wird verarbeitet …</string>
<string name="promotes_non_free_software">Bewirbt unfreie Software</string> <string name="promotes_non_free_software">Bewirbt nicht-freie Software</string>
<string name="proxy">Proxy</string> <string name="proxy">Proxy</string>
<string name="repositories">Paketquellen</string> <string name="repositories">Paketquellen</string>
<string name="requires_FORMAT">Benötigt %s</string> <string name="requires_FORMAT">Benötigt %s</string>
<string name="root_permission">Stumme Installation</string>
<string name="save">Speichern</string> <string name="save">Speichern</string>
<string name="saving_details">Details werden gespeichert </string> <string name="saving_details">Details werden gespeichert </string>
<string name="screenshots">Bildschirmfotos</string>
<string name="skip">Überspringen</string> <string name="skip">Überspringen</string>
<string name="suggested">Empfohlen</string> <string name="suggested">Empfohlen</string>
<string name="syncing">Synchronisierung</string> <string name="syncing">Synchronisierung</string>
<string name="themes">Themen</string> <string name="themes">Designs</string>
<string name="target">Ziel</string>
<string name="unknown">Unbekannt</string> <string name="unknown">Unbekannt</string>
<string name="uninstall">Deinstallation</string> <string name="uninstall">Deinstallieren</string>
<string name="unsigned">Unsigniert</string>
<string name="update">Aktualisierung</string> <string name="update">Aktualisierung</string>
<string name="version">Version</string> <string name="version">Version</string>
<string name="versions">Versionen</string> <string name="versions">Versionen</string>
<string name="whats_new">Was gibt es Neues</string> <string name="whats_new">Neu hinzugefügt</string>
<string name="waiting_to_start_download">Warten auf den Downloadbeginn …</string> <string name="waiting_to_start_download">Warten auf den Start des Downloads …</string>
<string name="validation_index_error_DESC">Der Index konnte nicht validiert werden.</string> <string name="validation_index_error_DESC">Der Index konnte nicht überprüft werden.</string>
<string name="website">Webseite</string> <string name="website">Website</string>
<string name="changes">Änderungen</string> <string name="changes">Änderungen</string>
<string name="author_email">Autor-E-Mail-Adresse</string> <string name="author_email">E-Mail-Adresse</string>
<string name="could_not_download_FORMAT">Konnte %s nicht herunterladen</string> <string name="could_not_download_FORMAT">%s konnte nicht heruntergeladen werden</string>
<string name="author_website">Autor-Webseite</string> <string name="author_website">Website</string>
<string name="delete">Löschen</string> <string name="delete">Löschen</string>
<string name="recently_updated">Zuletzt aktualisiert</string> <string name="recently_updated">Kürzlich aktualisiert</string>
<string name="license">Lizenz</string> <string name="license">Lizenz</string>
<string name="could_not_sync_FORMAT">Konnte %s nicht synchronisieren</string> <string name="could_not_sync_FORMAT">%s konnte nicht synchronisiert werden</string>
<string name="only_compatible_with_FORMAT">Nur kompatibel mit %s</string> <string name="only_compatible_with_FORMAT">Nur kompatibel mit %s</string>
<string name="license_FORMAT">%s-Lizenz</string> <string name="license_FORMAT">%s-Lizenz</string>
<string name="link_copied_to_clipboard">Link kopiert</string> <string name="link_copied_to_clipboard">Link kopiert</string>
<string name="project_website">Projekt-Website</string> <string name="project_website">Website des Projekts</string>
<string name="proxy_type">Proxy Typ</string> <string name="proxy_type">Proxy-Art</string>
<string name="repository">Paketquelle</string> <string name="repository">Paketquelle</string>
<string name="parsing_index_error_DESC">Die Indexdatei konnte nicht geparst werden.</string> <string name="parsing_index_error_DESC">Die Indexdatei konnte nicht geparst werden.</string>
<string name="password">Passwort</string> <string name="password">Passwort</string>
<string name="notify_about_updates_summary">Eine Benachrichtigung anzeigen, wenn neue Versionen verfügbar sind</string> <string name="notify_about_updates_summary">Benachrichtigung anzeigen, wenn neue Versionen verfügbar sind</string>
<string name="provided_by_FORMAT">Bereitgestellt von %s</string> <string name="provided_by_FORMAT">Bereitgestellt von %s</string>
<string name="source_code_no_longer_available">Quellcode nicht mehr verfügbar</string> <string name="source_code_no_longer_available">Quellcode nicht mehr verfügbar</string>
<string name="invalid_username_format">Ungültiges Benutzernamen-Format</string> <string name="invalid_username_format">Ungültiges Format des Benutzernamens</string>
<string name="no_proxy">Kein Proxy</string> <string name="no_proxy">Kein Proxy</string>
<string name="password_missing">Passwort fehlt</string> <string name="password_missing">Passwort fehlt</string>
<string name="source_code">Quellcode</string> <string name="source_code">Quellcode</string>
<string name="sync_repositories">Paketquellen synchronisieren</string> <string name="sync_repositories">Paketquellen synchronisieren</string>
<string name="sync_repositories_automatically">Paketquellen automatisch synchronisieren</string> <string name="sync_repositories_automatically">Paketquellen automatisch synchronisieren</string>
<string name="repository_not_used_DESC">Diese Paketquelle wurde noch nicht verwendet. Aktivieren Sie es, um die darin enthaltenen Anwendungen anzuzeigen.</string>
<string name="signature_FORMAT">Signatur %s</string> <string name="signature_FORMAT">Signatur %s</string>
<string name="has_non_free_dependencies">Enthält nicht-freie Abhängigkeiten</string> <string name="has_non_free_dependencies">Enthält nicht-freie Abhängigkeiten</string>
<string name="incompatible_version">Inkompatible Version</string> <string name="incompatible_version">Inkompatible Version</string>
<string name="system">System</string> <string name="system">System</string>
<string name="theme">Thema</string> <string name="theme">Design</string>
<string name="ignore_all_updates">Alle neuen Versionen ignorieren</string> <string name="ignore_all_updates">Alle neuen Versionen ignorieren</string>
<string name="ignore_this_update">Diese Version ignorieren</string> <string name="ignore_this_update">Diese Version ignorieren</string>
<string name="size">Größe</string> <string name="size">Größe</string>
<string name="updates">Aktualisierungen</string> <string name="updates">Updates</string>
<string name="username">Benutzername</string> <string name="username">Benutzername</string>
<string name="version_FORMAT">Version %s</string> <string name="version_FORMAT">Version %s</string>
<string name="downloading">Herunterladen</string> <string name="downloading">Wird heruntergeladen </string>
<string name="share">Teilen</string> <string name="share">Teilen</string>
<string name="show_more">Zeige mehr</string> <string name="show_more">Mehr anzeigen</string>
<string name="show_older_versions">Ältere Versionen zeigen</string> <string name="show_older_versions">Ältere Versionen anzeigen</string>
<string name="unverified">Ungeprüft</string>
<string name="username_missing">Benutzername fehlt</string> <string name="username_missing">Benutzername fehlt</string>
<string name="edit_repository">Paketquelle bearbeiten</string> <string name="edit_repository">Paketquelle bearbeiten</string>
<string name="file_format_error_DESC">Ungültiges Dateiformat.</string> <string name="file_format_error_DESC">Ungültiges Dateiformat.</string>
<string name="downloading_FORMAT">%s wird heruntergeladen </string> <string name="downloading_FORMAT">%s wird heruntergeladen </string>
<string name="incompatible_with_FORMAT">Inkompatibel mit %s</string> <string name="incompatible_with_FORMAT">Inkompatibel mit %s</string>
<string name="incompatible_versions">Inkompatible Versionen</string> <string name="incompatible_versions">Inkompatible Versionen</string>
<string name="invalid_address">Ungültige Adresse</string> <string name="invalid_address">Ungültige Adresse</string>
<string name="invalid_fingerprint_format">Ungültiges Fingerabdruckformat</string> <string name="invalid_fingerprint_format">Ungültiges Fingerabdruckformat</string>
<string name="invalid_permissions_error_DESC">Ungültige Berechtigungen.</string> <string name="invalid_permissions_error_DESC">Ungültige Berechtigungen.</string>
<string name="promotes_non_free_network_services">Bewirbt unfreie Netzwerkdienste</string> <string name="promotes_non_free_network_services">Bewirbt nicht-freie Netzwerkdienste</string>
<string name="unknown_FORMAT">Unbekannt: %s</string> <string name="unknown_FORMAT">Unbekannt: %s</string>
<string name="unknown_error_DESC">Unbekannter Fehler.</string> <string name="unknown_error_DESC">Unbekannter Fehler.</string>
<string name="syncing_FORMAT">Synchronisierung %s …</string> <string name="syncing_FORMAT">%s wird synchronisiert …</string>
<string name="incompatible_signature_DESC">Diese Version ist mit einem anderen Zertifikat signiert, als die auf Deinem Gerät installierte. Deinstalliere diese zuerst.</string> <string name="incompatible_signature_DESC">Diese Version ist mit einem anderen Zertifikat signiert als die auf deinem Gerät installierte Version. Deinstalliere diese zuerst.</string>
<string name="delete_repository_DESC">Die Paketquelle löschen\?</string> <string name="delete_repository_DESC">Paketquelle löschen?</string>
<string name="incompatible_older_DESC">Diese Version ist älter als die auf deinem Gerät installierte. Deinstalliere diese zuerst.</string> <string name="incompatible_older_DESC">Diese Version ist älter als diejenige, die auf deinem Gerät installiert ist. Deinstalliere diese zuerst.</string>
<string name="incompatible_api_DESC_FORMAT">Deine %1$s (API-Version %2$d) wird nicht unterstützt. %3$s</string> <string name="incompatible_api_DESC_FORMAT">Deine %1$s (API-Version %2$d) wird nicht unterstützt. %3$s</string>
<string name="incompatible_api_min_DESC_FORMAT">Die minimale API-Version ist %d.</string> <string name="incompatible_api_min_DESC_FORMAT">Die minimale API-Version ist %d.</string>
<string name="repository_unsigned_DESC">Nicht signiert. Die Anwendungsliste konnte nicht verifiziert werden. Sei vorsichtig beim Herunterladen von Anwendungen aus nicht signierten Paketquellen.</string> <string name="repository_unsigned_DESC">Nicht signiert. Die App-Liste konnte nicht verifiziert werden. Sei beim Herunterladen von Apps aus nicht signierten Paketquellen vorsichtig.</string>
<string name="unstable_updates_summary">Installation von instabilen Versionen vorschlagen</string> <string name="unstable_updates_summary">Installation von instabilen Versionen vorschlagen</string>
<string name="unstable_updates">Instabile Aktualisierungen</string> <string name="unstable_updates">Instabile Updates</string>
<string name="root_permission_description">Root-Rechte für stille Installationen zulassen</string> <string name="proxy_host">Proxy-Host</string>
<string name="proxy_host">Proxy Host</string> <string name="tap_to_install_DESC">Zum Installieren tippen.</string>
<string name="tap_to_install_DESC">Tippe um zu installieren.</string> <string name="tracks_or_reports_your_activity">Verfolgt oder versendet deine Aktivitäten</string>
<string name="tracks_or_reports_your_activity">Verfolgt oder erfasst deine Aktivitäten</string> <string name="proxy_port">Proxy-Port</string>
<string name="proxy_port">Proxy Port</string> <string name="search">Suchen</string>
<string name="search">Suche</string>
<string name="sorting_order">Sortierreihenfolge</string> <string name="sorting_order">Sortierreihenfolge</string>
<string name="socks_proxy">SOCKS Proxy</string> <string name="socks_proxy">SOCKS-Proxy</string>
<string name="no_applications_available">Keine Anwendungen verfügbar</string> <string name="no_applications_available">Keine Apps verfügbar</string>
<plurals name="new_updates_DESC_FORMAT"> <plurals name="new_updates_DESC_FORMAT">
<item quantity="one">%d Anwendung hat eine neue Version.</item> <item quantity="one">%d App hat eine neue Version.</item>
<item quantity="other">%d Anwendungen haben eine neue Version.</item> <item quantity="other">%d Apps haben eine neue Version.</item>
</plurals> </plurals>
<string name="signed_using_unsafe_algorithm">Mit einem unsicheren Algorithmus signiert</string> <string name="signed_using_unsafe_algorithm">Mit einem unsicheren Algorithmus signiert</string>
<string name="select_mirror">Wähle einen Spiegel</string> <string name="select_mirror">Wähle einen Spiegel</string>
<string name="list_animation">Animationen anzeigen</string>
<string name="links">Links</string> <string name="links">Links</string>
<string name="merging_FORMAT">Führe %s zusammen</string> <string name="merging_FORMAT">%s wird zusammengeführt </string>
<string name="name">Name</string> <string name="name">Name</string>
<string name="upstream_source_code_is_not_free">Der Upstream-Quellcode ist nicht frei</string> <string name="upstream_source_code_is_not_free">Der Upstream-Quellcode ist nicht frei</string>
<string name="prefs_language_title">Sprache</string> <string name="prefs_language_title">Sprache</string>
<string name="prefs_personalization">Personalisierung</string> <string name="prefs_personalization">Personalisieren</string>
<string name="show_less">Weniger anzeigen</string> <string name="show_less">Weniger anzeigen</string>
<string name="latest">Neueste</string>
<string name="explore">Entdecken</string>
<string name="update_all">Alle aktualisieren</string> <string name="update_all">Alle aktualisieren</string>
<string name="installed_applications">Installierte Anwendungen</string>
<string name="sort_filter">Sortieren und filtern</string>
<string name="new_applications">Neue Anwendungen</string>
<plurals name="days"> <plurals name="days">
<item quantity="one">Tag</item> <item quantity="one">Tag</item>
<item quantity="other">Tage</item> <item quantity="other">Tage</item>
@ -185,61 +171,65 @@
<item quantity="one">Stunde</item> <item quantity="one">Stunde</item>
<item quantity="other">Stunden</item> <item quantity="other">Stunden</item>
</plurals> </plurals>
<string name="only_on_wifi_with_charging">Nur während des Ladevorgangs und aktiviertem WLAN</string> <string name="only_on_wifi_with_charging">Nur mit WLAN während des Aufladens</string>
<string name="installer">Installationsmethode</string> <string name="installer">Installationsmethode</string>
<string name="cleanup_description">Zeitraum zum Prüfen und Entfernen heruntergeladener Dateien</string>
<string name="cleanup_title">APK-Bereinigungsintervall</string> <string name="cleanup_title">APK-Bereinigungsintervall</string>
<string name="root_installer">Root-Installation</string> <string name="root_installer">Root-Installation</string>
<string name="legacy_installer">Alte Installationsmethode</string> <string name="legacy_installer">Alte Installationsmethode</string>
<string name="session_installer">Sitzungs-Installation</string> <string name="session_installer">Sitzungsinstallation</string>
<string name="shizuku_installer">Shizuku-Installation</string> <string name="shizuku_installer">Shizuku-Installation</string>
<string name="io_error_DESC">Bestimmte Aktionen können nicht durchgeführt werden.</string> <string name="io_error_DESC">Bestimmte Aktionen können nicht durchgeführt werden.</string>
<string name="no_internet">Sie haben keine Internetverbindung</string>
<string name="allow_collapsing_toolbar">Erweiterung der oberen Anwendungsleiste zulassen</string>
<string name="allow_collapsing_toolbar_DESC">Das Erweitern und Reduzieren der oberen Anwendungsleiste erlauben</string>
<string name="favourites">Favoriten</string> <string name="favourites">Favoriten</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="material_you_desc">Material You-Farbschema verwenden</string> <string name="material_you_desc">Material You-Farbschema verwenden</string>
<string name="repository_unreachable">Repository unerreichbar</string> <string name="repository_unreachable">Paketquelle unerreichbar</string>
<string name="force_clean_up">Aufräumen erzwingen</string> <string name="force_clean_up">Bereinigung erzwingen</string>
<string name="enable_repo">Repository aktivieren</string> <string name="enable_repo">Paketquelle aktivieren</string>
<string name="force_clean_up_DESC">Entfernt doppelte Dateien</string> <string name="force_clean_up_DESC">Entfernt doppelte Dateien</string>
<string name="installing">Installation</string> <string name="installing">Wird installiert </string>
<string name="restart_app">Starten Sie Droid-ify neu, um die Änderungen zu sehen</string> <string name="waiting_to_start_installation">Warten auf den Start der Installation </string>
<string name="waiting_to_start_installation">Warten auf den Beginn der Installation </string>
<string name="auto_update">Apps automatisch aktualisieren</string> <string name="auto_update">Apps automatisch aktualisieren</string>
<string name="auto_update_apps">Versuche, Updates automatisch zu installieren</string> <string name="auto_update_apps">Updates möglichst automatisch installieren</string>
<string name="has_non_free_components">Hat nicht-freie Komponenten</string> <string name="has_non_free_components">Enthält nicht-freie Komponenten</string>
<string name="socket_error_DESC">Server konnte kein neues Datenpaket liefern.</string> <string name="socket_error_DESC">Server konnte kein neues Datenpaket liefern.</string>
<string name="shizuku_not_alive">Shizuku läuft nicht</string> <string name="shizuku_not_alive">Shizuku läuft nicht</string>
<string name="contains_nsfw">Enthält für den Arbeitsplatz unangemessene Inhalte</string> <string name="contains_nsfw">Enthält für den Arbeitsplatz unangemessene Inhalte</string>
<string name="connection_error_DESC">Verbindung zum Server nicht möglich</string> <string name="connection_error_DESC">Verbindung zum Server nicht möglich</string>
<string name="shizuku_not_installed">Shizuku ist nicht installiert</string> <string name="shizuku_not_installed">Shizuku ist nicht installiert</string>
<string name="home_screen_swiping">Wischgesten</string> <string name="home_screen_swiping">Wischgesten</string>
<string name="home_screen_swiping_DESC">Dem Benutzer erlauben, auf dem Startbildschirm zwischen den Seiten zu wischen</string> <string name="home_screen_swiping_DESC">Durch Wischen nach links/rechts zwischen Seiten navigieren</string>
<string name="special_credits">Besonderer Dank</string> <string name="special_credits">Besonderer Dank</string>
<string name="label_copy">Kopieren</string>
<string name="proxy_port_error_not_int">Proxy-Port muss eine natürliche Zahl sein</string> <string name="proxy_port_error_not_int">Proxy-Port muss eine natürliche Zahl sein</string>
<string name="repository_not_found">Folgende Repos konnten nicht gefunden werden</string> <string name="repository_not_found">Folgende Paketquelle konnten nicht gefunden werden</string>
<string name="import_settings_title">Einstellungen importieren</string> <string name="import_settings_title">Einstellungen importieren</string>
<string name="import_export">Importieren/Exportieren</string> <string name="import_export">Importieren/Exportieren</string>
<string name="import_settings_DESC">Importiere Einstellung und Favoriten von Datei</string> <string name="import_settings_DESC">Einstellungen und Favoriten aus Datei importieren</string>
<string name="export_settings_title">Einstellungen exportieren</string> <string name="export_settings_title">Einstellungen exportieren</string>
<string name="export_repos_DESC">Alle Repositories in eine Datei exportieren</string> <string name="export_repos_DESC">Paketquellen in eine Datei exportieren</string>
<string name="import_repos_title">Importiere eine Sammlung</string> <string name="import_repos_title">Paketquellen importieren</string>
<string name="export_settings_DESC">Einstellungen und Favoriten in eine Datei exportieren</string> <string name="export_settings_DESC">Einstellungen und Favoriten in eine Datei exportieren</string>
<string name="export_repos_title">Repositories exportieren</string> <string name="export_repos_title">Paketquellen exportieren</string>
<string name="import_repos_DESC">Alle Repositories aus einer Datei importieren</string> <string name="import_repos_DESC">Paketquellen aus einer Datei importieren</string>
<string name="cannot_open_link">Link kann nicht geöffnet werden</string> <string name="cannot_open_link">Link kann nicht geöffnet werden</string>
<string name="has_tethered_network">An einen bestimmten Netzwerkdienst gebunden</string> <string name="has_tethered_network">An einen bestimmten Netzwerkdienst gebunden</string>
<string name="ignore_signature">Signatur ignorieren</string> <string name="ignore_signature">Signatur ignorieren</string>
<string name="ignore_signature_summary">*Achtung* Signaturüberprüfung bei der Installation der APK ignorieren. Für LSPosed-Benutzer oder Experten.</string> <string name="ignore_signature_summary">*Achtung* Signaturüberprüfung bei der Installation der APK ignorieren. Für LSPosed-Benutzer oder Experten</string>
<string name="installation_failed">Installation fehlgeschlagen</string> <string name="installation_failed">Installation fehlgeschlagen</string>
<string name="uninstalled_application_DESC">%s wurde deinstalliert</string> <string name="uninstalled_application_DESC">%s wurde deinstalliert</string>
<string name="installation_failed_DESC">%s konnte nicht installiert werden</string> <string name="installation_failed_DESC">%s konnte nicht installiert werden</string>
<string name="uninstalled_application">Deinstalliert</string> <string name="uninstalled_application">Deinstalliert</string>
<string name="require_background_access">Hintergrundzugriff anfordern</string> <string name="require_background_access">Hintergrundzugriff anfordern</string>
<string name="require_background_access_DESC">Hintergrundzugriff ist erforderlich, um die Hintergrundsynchronisation ordnungsgemäß durchzuführen</string> <string name="require_background_access_DESC">Hintergrundzugriff ist erforderlich, um die Hintergrundsynchronisation ordnungsgemäß durchzuführen</string>
<string name="insufficient_storage">Nicht genug Speicherplatz</string> <string name="insufficient_storage">Nicht genügend Speicherplatz</string>
<string name="insufficient_storage_DESC">Es gibt nicht genug Speicherplatz, um diese Anwendung zu installieren. Versuche etwas Platz zu schafffen.</string> <string name="insufficient_storage_DESC">Nicht genügend Speicherplatz, um diese App zu installieren. Versuche, etwas Platz zu schaffen</string>
</resources> <string name="error_shizuku_not_granted">Shizuku-Erlaubnis fehlt</string>
<string name="error_shizuku_not_granted_DESC">Erlaubnis für den Shizuku-Dienst ist nicht gewährt. Bitte in der Shizuku-App prüfen</string>
<string name="error_shizuku_not_installed">Shizuku nicht installiert</string>
<string name="error_shizuku_not_installed_DESC">Shizuku scheint nicht installiert zu sein</string>
<string name="switch_to_default_installer">Auf Standard wechseln</string>
<string name="open_shizuku">Shizuku öffnen</string>
<string name="error_shizuku_service_unavailable">Shizuku läuft nicht</string>
<string name="error_shizuku_not_running_DESC">Der Shizuku-Dienst läuft nicht. Bitte in der Shizuku-App prüfen</string>
<string name="label_open_video">Video</string>
<string name="label_unknown_sdk">Unbekannt (%d)</string>
</resources>

View File

@ -64,8 +64,6 @@
<string name="light">Φωτεινό</string> <string name="light">Φωτεινό</string>
<string name="link_copied_to_clipboard">Ο σύνδεσμος αντιγράφηκε</string> <string name="link_copied_to_clipboard">Ο σύνδεσμος αντιγράφηκε</string>
<string name="links">Σύνδεσμοι</string> <string name="links">Σύνδεσμοι</string>
<string name="list_animation">Κινήσεις Λίστας</string>
<string name="list_animation_description">Εμφάνιση κινήσεων λίστας στην αρχική σελίδα</string>
<string name="merging_FORMAT">Συγχώνευση %s</string> <string name="merging_FORMAT">Συγχώνευση %s</string>
<string name="name">Όνομα</string> <string name="name">Όνομα</string>
<string name="network_error_DESC">Σφάλμα δικτύου</string> <string name="network_error_DESC">Σφάλμα δικτύου</string>
@ -107,10 +105,8 @@
<string name="repository">Αποθετήριο</string> <string name="repository">Αποθετήριο</string>
<string name="repository_unsigned_DESC">Μη υπογεγραμμένο. Αδυναμία επαλήθευσης της λίστας εφαρμογών. Προσέχετε όταν κατεβάζετε εφαρμογές από μη υπογεγραμμένα αποθετήρια.</string> <string name="repository_unsigned_DESC">Μη υπογεγραμμένο. Αδυναμία επαλήθευσης της λίστας εφαρμογών. Προσέχετε όταν κατεβάζετε εφαρμογές από μη υπογεγραμμένα αποθετήρια.</string>
<string name="requires_FORMAT">Απαιτεί %s</string> <string name="requires_FORMAT">Απαιτεί %s</string>
<string name="root_permission">Σιωπηλή Εγκατάσταση</string>
<string name="save">Αποθήκευση</string> <string name="save">Αποθήκευση</string>
<string name="saving_details">Αποθήκευση λεπτομερειών…</string> <string name="saving_details">Αποθήκευση λεπτομερειών…</string>
<string name="screenshots">Στιγμιότυπα οθόνης</string>
<string name="search">Αναζήτηση</string> <string name="search">Αναζήτηση</string>
<string name="select_mirror">Επιλέξτε ένα mirror</string> <string name="select_mirror">Επιλέξτε ένα mirror</string>
<string name="share">Κοινοποίηση</string> <string name="share">Κοινοποίηση</string>
@ -139,11 +135,8 @@
<string name="unstable_updates_summary">Πρόταση για εγκατάσταση ασταθών εκδόσεων</string> <string name="unstable_updates_summary">Πρόταση για εγκατάσταση ασταθών εκδόσεων</string>
<string name="update">Ενημέρωση</string> <string name="update">Ενημέρωση</string>
<string name="updates">Ενημερώσεις</string> <string name="updates">Ενημερώσεις</string>
<string name="unsigned">Μη υπογεγραμμένο</string>
<string name="unverified">Μη επιβεβαιωμένο</string>
<string name="delete">Διαγραφή</string> <string name="delete">Διαγραφή</string>
<string name="recently_updated">Πρόσφατα ενημερωμένα</string> <string name="recently_updated">Πρόσφατα ενημερωμένα</string>
<string name="target">Στόχος</string>
<string name="whats_new">Τι νέο υπάρχει</string> <string name="whats_new">Τι νέο υπάρχει</string>
<string name="upstream_source_code_is_not_free">Upstream source code is not free</string> <string name="upstream_source_code_is_not_free">Upstream source code is not free</string>
<string name="username">Όνομα χρήστη</string> <string name="username">Όνομα χρήστη</string>
@ -161,28 +154,20 @@
<string name="incompatible_signature_DESC">Αυτή η έκδοση είναι υπογεγραμμένη με ένα διαφορετικό πιστοποιητικό από αυτή που είναι εγκατεστημένη στη συσκευή σας. Απεγκαταστήστε εκείνη πρώτα.</string> <string name="incompatible_signature_DESC">Αυτή η έκδοση είναι υπογεγραμμένη με ένα διαφορετικό πιστοποιητικό από αυτή που είναι εγκατεστημένη στη συσκευή σας. Απεγκαταστήστε εκείνη πρώτα.</string>
<string name="integrity_check_error_DESC">Αδυναμία ελέγχου ακεραιότητας.</string> <string name="integrity_check_error_DESC">Αδυναμία ελέγχου ακεραιότητας.</string>
<string name="invalid_signature_error_DESC">Μη έγκυρη υπογραφή.</string> <string name="invalid_signature_error_DESC">Μη έγκυρη υπογραφή.</string>
<string name="repository_not_used_DESC">Αυτό το αποθετήριο δεν έχει χρησιμοποιηθεί ακόμα. Χρειάζεται να το ενεργοποιήσετε για να δείτε τις εφαρμογές που παρέχει.</string>
<string name="signed_using_unsafe_algorithm">Έχει υπογραφεί χρησιμοποιώντας έναν μη ασφαλή αλγόριθμο</string> <string name="signed_using_unsafe_algorithm">Έχει υπογραφεί χρησιμοποιώντας έναν μη ασφαλή αλγόριθμο</string>
<string name="socks_proxy">SOCKS διακομιστής μεσολάβησης</string> <string name="socks_proxy">SOCKS διακομιστής μεσολάβησης</string>
<string name="tracks_or_reports_your_activity">Καταγράφει ή αναφέρει τη δραστηριότητά σας</string> <string name="tracks_or_reports_your_activity">Καταγράφει ή αναφέρει τη δραστηριότητά σας</string>
<string name="validation_index_error_DESC">Αδυναμία επαλήθευσης δείκτη.</string> <string name="validation_index_error_DESC">Αδυναμία επαλήθευσης δείκτη.</string>
<string name="root_permission_description">Επιτρέψτε την άδεια root για σιωπηλή εγκατάσταση</string>
<string name="amoled">Amoled</string> <string name="amoled">Amoled</string>
<string name="prefs_personalization">Εξατομίκευση</string> <string name="prefs_personalization">Εξατομίκευση</string>
<string name="prefs_language_title">Γλώσσα</string> <string name="prefs_language_title">Γλώσσα</string>
<string name="latest">Πρόσφατα</string>
<string name="sort_filter">Ταξινόμηση &amp; Φιλτράρισμα</string>
<string name="installed_applications">Εγκατεστημένες εφαρμογές</string>
<string name="installer">Πρόγραμμα Εγκατάστασης</string> <string name="installer">Πρόγραμμα Εγκατάστασης</string>
<string name="legacy_installer">Παλιό πρόγραμμα Εγκατάστασης</string> <string name="legacy_installer">Παλιό πρόγραμμα Εγκατάστασης</string>
<string name="session_installer">Πρόγραμμα Εκατάστασης Συνεδρίας</string> <string name="session_installer">Πρόγραμμα Εκατάστασης Συνεδρίας</string>
<string name="root_installer">Πρόγραμμα Εγκατάστασης Root</string> <string name="root_installer">Πρόγραμμα Εγκατάστασης Root</string>
<string name="shizuku_installer">Πρόγραμμα Εγκατάστασης Shizuku</string> <string name="shizuku_installer">Πρόγραμμα Εγκατάστασης Shizuku</string>
<string name="show_less">Εμφάνιση Λιγότερων</string> <string name="show_less">Εμφάνιση Λιγότερων</string>
<string name="explore">Εξερεύνηση</string>
<string name="update_all">Ενημέρωση όλων</string> <string name="update_all">Ενημέρωση όλων</string>
<string name="new_applications">Νέες εφαρμογές</string>
<string name="cleanup_description">Period to check and remove downloaded files</string>
<string name="cleanup_title">APK cleanup interval</string> <string name="cleanup_title">APK cleanup interval</string>
<plurals name="days"> <plurals name="days">
<item quantity="one">Ημέρα</item> <item quantity="one">Ημέρα</item>
@ -194,9 +179,6 @@
</plurals> </plurals>
<string name="only_on_wifi_with_charging">Μόνο σε Wi-Fi και Φόρτιση</string> <string name="only_on_wifi_with_charging">Μόνο σε Wi-Fi και Φόρτιση</string>
<string name="io_error_DESC">Δεν είναι δυνατή η εκτέλεση ορισμένων ενεργειών.</string> <string name="io_error_DESC">Δεν είναι δυνατή η εκτέλεση ορισμένων ενεργειών.</string>
<string name="no_internet">Δεν έχετε σύνδεση στο διαδίκτυο</string>
<string name="allow_collapsing_toolbar">Να επιτρέπεται η Επέκταση της Γραμμής Κορυφαίων Εφαρμογών</string>
<string name="allow_collapsing_toolbar_DESC">Να επιτρέπεται η επέκταση και σύμπτυξη της επάνω γραμμής εφαρμογών</string>
<string name="material_you_desc">Χρησιμοποιήστε material you με θέμα το χρώμα σας</string> <string name="material_you_desc">Χρησιμοποιήστε material you με θέμα το χρώμα σας</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="favourites">Αγαπημένα</string> <string name="favourites">Αγαπημένα</string>
@ -204,7 +186,6 @@
<string name="force_clean_up">Αναγκαστική εκκαθάριση</string> <string name="force_clean_up">Αναγκαστική εκκαθάριση</string>
<string name="force_clean_up_DESC">Καθαρίζει τα περιττά αρχεία</string> <string name="force_clean_up_DESC">Καθαρίζει τα περιττά αρχεία</string>
<string name="enable_repo">Ενεργοποιήστε το αποθετήριο</string> <string name="enable_repo">Ενεργοποιήστε το αποθετήριο</string>
<string name="restart_app">Επανεκκινήστε το Droid-ify για να δείτε αλλαγές</string>
<string name="installing">Εγκατάσταση</string> <string name="installing">Εγκατάσταση</string>
<string name="waiting_to_start_installation">Αναμονή για έναρξη εγκατάστασης…</string> <string name="waiting_to_start_installation">Αναμονή για έναρξη εγκατάστασης…</string>
<string name="auto_update">Αυτόματη ενημέρωση εφαρμογών</string> <string name="auto_update">Αυτόματη ενημέρωση εφαρμογών</string>
@ -218,7 +199,6 @@
<string name="shizuku_not_installed">Το Shizuku δεν είναι εγκατεστημένο</string> <string name="shizuku_not_installed">Το Shizuku δεν είναι εγκατεστημένο</string>
<string name="home_screen_swiping">Σύρσιμο Αρχικής Οθόνης</string> <string name="home_screen_swiping">Σύρσιμο Αρχικής Οθόνης</string>
<string name="home_screen_swiping_DESC">Επιτρέψτε στον χρήστη να συρθεί μεταξύ σελίδων στην αρχική οθόνη</string> <string name="home_screen_swiping_DESC">Επιτρέψτε στον χρήστη να συρθεί μεταξύ σελίδων στην αρχική οθόνη</string>
<string name="label_copy">Αντιγραφή</string>
<string name="repository_not_found">Το παρακάτω αποθετήριο δεν βρέθηκε</string> <string name="repository_not_found">Το παρακάτω αποθετήριο δεν βρέθηκε</string>
<string name="proxy_port_error_not_int">Η θύρα Proxy μπορεί να είναι μόνο Ακέραιος</string> <string name="proxy_port_error_not_int">Η θύρα Proxy μπορεί να είναι μόνο Ακέραιος</string>
<string name="import_settings_title">Εισαγωγή Ρυθμίσεων</string> <string name="import_settings_title">Εισαγωγή Ρυθμίσεων</string>
@ -240,4 +220,16 @@
<string name="uninstalled_application_DESC">Το %s απεγκαταστάθηκε</string> <string name="uninstalled_application_DESC">Το %s απεγκαταστάθηκε</string>
<string name="ignore_signature">Αγνόησή Υπογραφής</string> <string name="ignore_signature">Αγνόησή Υπογραφής</string>
<string name="ignore_signature_summary">Αγνοήστε την επαλήθευση υπογραφής κατά την εγκατάσταση apk, για χρήστες με LSP ή προχωρημένους χρήστες</string> <string name="ignore_signature_summary">Αγνοήστε την επαλήθευση υπογραφής κατά την εγκατάσταση apk, για χρήστες με LSP ή προχωρημένους χρήστες</string>
</resources> <string name="error_shizuku_not_granted_DESC">Η άδεια λειτουργίας της υπηρεσίας Shizuku δεν έχει παραχωρηθεί. Ελέγξτε την εφαρμογή Shizuku</string>
<string name="error_shizuku_not_installed">Το Shizuku δεν έχει εγκατασταθεί</string>
<string name="open_shizuku">Άνοιγμα Shizuku</string>
<string name="label_unknown_sdk">Άγνωστο (%d)</string>
<string name="label_open_video">Βίντεο</string>
<string name="switch_to_default_installer">Μετάβαση στην Προεπιλογή</string>
<string name="insufficient_storage">Ανεπαρκής Χώρος</string>
<string name="error_shizuku_not_installed_DESC">Το Shizuku δεν φαίνεται να είναι εγκατεστημένο</string>
<string name="error_shizuku_service_unavailable">Δεν είναι σε λειτουργία το Shizuku</string>
<string name="error_shizuku_not_running_DESC">Η υπηρεσία Shizuku δεν λειτουργεί. Ελέγξτε την εφαρμογή Shizuku</string>
<string name="error_shizuku_not_granted">Λείπει η άδεια Shizuku</string>
<string name="insufficient_storage_DESC">Δεν υπάρχει αρκετός ελεύθερος χώρος στη συσκευή για την εγκατάσταση αυτής της εφαρμογής. Προσπαθήστε να ελευθερώσετε και να καθαρίσετε λίγο χώρο</string>
</resources>

View File

@ -29,14 +29,12 @@
<string name="incompatible_api_DESC_FORMAT">Via %1$s (API-versio %2$d) ne estas subtenata. %3$s</string> <string name="incompatible_api_DESC_FORMAT">Via %1$s (API-versio %2$d) ne estas subtenata. %3$s</string>
<string name="only_on_wifi">Nur sur Wi-Fi</string> <string name="only_on_wifi">Nur sur Wi-Fi</string>
<string name="password">Pasvorto</string> <string name="password">Pasvorto</string>
<string name="unsigned">Nesubskribita</string>
<string name="cleanup_title">Intervalo de purigado de APK</string> <string name="cleanup_title">Intervalo de purigado de APK</string>
<string name="repository_unsigned_DESC">Nesubskribita. Ne eblis kontroli la aplikliston. Atentu elŝutante aplikaĵojn el nesubskribitaj deponejoj.</string> <string name="repository_unsigned_DESC">Nesubskribita. Ne eblis kontroli la aplikliston. Atentu elŝutante aplikaĵojn el nesubskribitaj deponejoj.</string>
<string name="contains_nsfw">Enhavas enhavon ne taŭgan por laboro</string> <string name="contains_nsfw">Enhavas enhavon ne taŭgan por laboro</string>
<string name="select_mirror">Elekti spegulon</string> <string name="select_mirror">Elekti spegulon</string>
<string name="has_non_free_components">Havas neliberajn komponantojn</string> <string name="has_non_free_components">Havas neliberajn komponantojn</string>
<string name="promotes_non_free_network_services">Promocias ne-liberajn retservojn</string> <string name="promotes_non_free_network_services">Promocias ne-liberajn retservojn</string>
<string name="list_animation_description">Montri listanimacion sur la ĉefpaĝo</string>
<string name="syncing_FORMAT">Sinkroniganta %s…</string> <string name="syncing_FORMAT">Sinkroniganta %s…</string>
<string name="favourites">Favoratoj</string> <string name="favourites">Favoratoj</string>
<string name="tracks_or_reports_your_activity">Spuras aŭ raportas vian agadon</string> <string name="tracks_or_reports_your_activity">Spuras aŭ raportas vian agadon</string>
@ -49,11 +47,9 @@
<string name="waiting_to_start_download">Atendante komenci elŝuton…</string> <string name="waiting_to_start_download">Atendante komenci elŝuton…</string>
<string name="auto_update_apps">Provi instali ĝisdatigojn aŭtomate</string> <string name="auto_update_apps">Provi instali ĝisdatigojn aŭtomate</string>
<string name="downloading">Elŝutanta</string> <string name="downloading">Elŝutanta</string>
<string name="explore">Esplori</string>
<string name="notify_about_updates_summary">Montri sciigon kiam novaj versioj estas disponebla</string> <string name="notify_about_updates_summary">Montri sciigon kiam novaj versioj estas disponebla</string>
<string name="suggested">Sugestita</string> <string name="suggested">Sugestita</string>
<string name="permissions">Permesoj</string> <string name="permissions">Permesoj</string>
<string name="sort_filter">Ordigi ⳤ Filtri</string>
<string name="open_DESC_FORMAT">Ĉu malfermi %s\?</string> <string name="open_DESC_FORMAT">Ĉu malfermi %s\?</string>
<string name="validation_index_error_DESC">Indekso ne povis esti validigita.</string> <string name="validation_index_error_DESC">Indekso ne povis esti validigita.</string>
<string name="new_updates_available">Novaj versioj de aplikaĵoj disponeblaj</string> <string name="new_updates_available">Novaj versioj de aplikaĵoj disponeblaj</string>
@ -70,7 +66,6 @@
<string name="installing">Instalante</string> <string name="installing">Instalante</string>
<string name="incompatible_platforms_DESC_FORMAT">Via %1$s platformo ne estas subtenata. Subtenataj platformoj: %2$s.</string> <string name="incompatible_platforms_DESC_FORMAT">Via %1$s platformo ne estas subtenata. Subtenataj platformoj: %2$s.</string>
<string name="repository_unreachable">Deponejo neatingebla</string> <string name="repository_unreachable">Deponejo neatingebla</string>
<string name="restart_app">Rekomenci Droid-ify por vidi ŝanĝojn</string>
<string name="username_missing">Uzantnomo mankas</string> <string name="username_missing">Uzantnomo mankas</string>
<string name="connecting">Konektanta…</string> <string name="connecting">Konektanta…</string>
<string name="update">Ĝisdatigi</string> <string name="update">Ĝisdatigi</string>
@ -83,7 +78,6 @@
<string name="password_missing">Pasvorto mankas</string> <string name="password_missing">Pasvorto mankas</string>
<string name="description">Priskribo</string> <string name="description">Priskribo</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="target">Celo</string>
<string name="waiting_to_start_installation">Atendante komenci instaladon…</string> <string name="waiting_to_start_installation">Atendante komenci instaladon…</string>
<string name="installed">Instalita</string> <string name="installed">Instalita</string>
<string name="name">Nomo</string> <string name="name">Nomo</string>
@ -94,7 +88,6 @@
<string name="version_FORMAT">Versio %s</string> <string name="version_FORMAT">Versio %s</string>
<string name="light">Hela</string> <string name="light">Hela</string>
<string name="all_applications">Ĉiuj aplikaĵoj</string> <string name="all_applications">Ĉiuj aplikaĵoj</string>
<string name="installed_applications">Instalitaj aplikaĵoj</string>
<string name="proxy_port">Prokura haveno</string> <string name="proxy_port">Prokura haveno</string>
<string name="requires_FORMAT">Postulas %s</string> <string name="requires_FORMAT">Postulas %s</string>
<string name="delete">Forigi</string> <string name="delete">Forigi</string>
@ -112,10 +105,7 @@
<string name="link_copied_to_clipboard">Ligilo kopiita</string> <string name="link_copied_to_clipboard">Ligilo kopiita</string>
<string name="processing_FORMAT">Prilaboranta %1$s…</string> <string name="processing_FORMAT">Prilaboranta %1$s…</string>
<string name="shizuku_installer">Shizuku Instalilo</string> <string name="shizuku_installer">Shizuku Instalilo</string>
<string name="new_applications">Novaj aplikaĵoj</string>
<string name="label_copy">Kopii</string>
<string name="uninstall">Malinstali</string> <string name="uninstall">Malinstali</string>
<string name="no_internet">Vi ne havas interretan konekton</string>
<string name="skip">Preterpasi</string> <string name="skip">Preterpasi</string>
<string name="downloaded_FORMAT">Elŝutis %s</string> <string name="downloaded_FORMAT">Elŝutis %s</string>
<string name="cant_edit_sync_DESC">Ne povas redakti deponejon ĉar ĝi nun sinkronigas.</string> <string name="cant_edit_sync_DESC">Ne povas redakti deponejon ĉar ĝi nun sinkronigas.</string>
@ -125,7 +115,6 @@
<string name="network_error_DESC">Reteraro</string> <string name="network_error_DESC">Reteraro</string>
<string name="license">Licenco</string> <string name="license">Licenco</string>
<string name="auto_update">Aŭtomate ĝisdatigi aplikaĵoj</string> <string name="auto_update">Aŭtomate ĝisdatigi aplikaĵoj</string>
<string name="screenshots">Ekrankopioj</string>
<string name="recently_updated">Lastatempe ĝisdatigita</string> <string name="recently_updated">Lastatempe ĝisdatigita</string>
<string name="already_exists">Jam ekzistas</string> <string name="already_exists">Jam ekzistas</string>
<string name="author_website">Aŭtora retejo</string> <string name="author_website">Aŭtora retejo</string>
@ -133,12 +122,10 @@
<string name="edit_repository">Redakti deponejon</string> <string name="edit_repository">Redakti deponejon</string>
<string name="sorting_order">Ordo de ordigo</string> <string name="sorting_order">Ordo de ordigo</string>
<string name="ignore_this_update">Ignori ĉi tiun version</string> <string name="ignore_this_update">Ignori ĉi tiun version</string>
<string name="unverified">Nekontrolita</string>
<string name="http_proxy">HTTP prokurilo</string> <string name="http_proxy">HTTP prokurilo</string>
<string name="application">Aplikaĵo</string> <string name="application">Aplikaĵo</string>
<string name="always">Ĉiam</string> <string name="always">Ĉiam</string>
<string name="address">Adreso</string> <string name="address">Adreso</string>
<string name="root_permission">Silenta Instalo</string>
<string name="no_description_available_DESC">Neniu priskribo disponebla</string> <string name="no_description_available_DESC">Neniu priskribo disponebla</string>
<string name="invalid_fingerprint_format">Nevalida fingrospura formato</string> <string name="invalid_fingerprint_format">Nevalida fingrospura formato</string>
<string name="saving_details">Konservanta detalojn…</string> <string name="saving_details">Konservanta detalojn…</string>
@ -163,7 +150,6 @@
<string name="search">Serĉi</string> <string name="search">Serĉi</string>
<string name="unknown_error_DESC">Nekonata eraro.</string> <string name="unknown_error_DESC">Nekonata eraro.</string>
<string name="themes">Haŭtoj</string> <string name="themes">Haŭtoj</string>
<string name="list_animation">Enlistigi Animacioj</string>
<string name="file_format_error_DESC">Nevalida dosierformato.</string> <string name="file_format_error_DESC">Nevalida dosierformato.</string>
<plurals name="new_updates_DESC_FORMAT"> <plurals name="new_updates_DESC_FORMAT">
<item quantity="one">%d aplikaĵo havas novan version.</item> <item quantity="one">%d aplikaĵo havas novan version.</item>
@ -176,15 +162,12 @@
<string name="project_website">Projekto retejo</string> <string name="project_website">Projekto retejo</string>
<string name="donate">Donaci</string> <string name="donate">Donaci</string>
<string name="add_repository">Aldoni deponejo</string> <string name="add_repository">Aldoni deponejo</string>
<string name="allow_collapsing_toolbar">Permesi al Supra Aplika Breto Etendi</string>
<string name="could_not_validate_FORMAT">Ne eblis validigi %s</string> <string name="could_not_validate_FORMAT">Ne eblis validigi %s</string>
<string name="available">Esplori</string> <string name="available">Esplori</string>
<string name="show_less">Montri Malpli</string> <string name="show_less">Montri Malpli</string>
<string name="never">Neniam</string> <string name="never">Neniam</string>
<string name="no_matching_applications_found">Ne povis trovi tiajn aplikaĵojn</string> <string name="no_matching_applications_found">Ne povis trovi tiajn aplikaĵojn</string>
<string name="incompatible_api_min_DESC_FORMAT">La minimuma API-versio estas %d.</string> <string name="incompatible_api_min_DESC_FORMAT">La minimuma API-versio estas %d.</string>
<string name="allow_collapsing_toolbar_DESC">Permesi al supra aplika breto etendi kaj maletendi</string>
<string name="cleanup_description">Periodo por kontroli kaj forigi elŝutitajn dosierojn</string>
<string name="settings">Agordoj</string> <string name="settings">Agordoj</string>
<string name="invalid_address">Nevalida adreso</string> <string name="invalid_address">Nevalida adreso</string>
<string name="upstream_source_code_is_not_free">La kontraŭflua fontkodo ne estas libera</string> <string name="upstream_source_code_is_not_free">La kontraŭflua fontkodo ne estas libera</string>
@ -193,7 +176,6 @@
<string name="checking_repository">Kontrolanta deponejon…</string> <string name="checking_repository">Kontrolanta deponejon…</string>
<string name="versions">Versioj</string> <string name="versions">Versioj</string>
<string name="incompatible_api_max_DESC_FORMAT">La maksimuma API-versio estas %d.</string> <string name="incompatible_api_max_DESC_FORMAT">La maksimuma API-versio estas %d.</string>
<string name="repository_not_used_DESC">Ĉi tiu deponejo ankoraŭ ne estis uzata. Ŝaltu ĝin por vidi la aplikaĵojn en ĝi.</string>
<string name="parsing_index_error_DESC">Ne eblis analizi la indeksan dosieron.</string> <string name="parsing_index_error_DESC">Ne eblis analizi la indeksan dosieron.</string>
<string name="install">Instali</string> <string name="install">Instali</string>
<string name="system">Sistemo</string> <string name="system">Sistemo</string>
@ -215,10 +197,8 @@
<string name="integrity_check_error_DESC">Ne eblis kontroli integrecon.</string> <string name="integrity_check_error_DESC">Ne eblis kontroli integrecon.</string>
<string name="application_not_found">Ne eblis trovi tiun aplikaĵon</string> <string name="application_not_found">Ne eblis trovi tiun aplikaĵon</string>
<string name="proxy">Prokurilo</string> <string name="proxy">Prokurilo</string>
<string name="latest">Plej lasta</string>
<string name="credits">Kreditoj</string> <string name="credits">Kreditoj</string>
<string name="repositories">Deponejoj</string> <string name="repositories">Deponejoj</string>
<string name="root_permission_description">Permesi radikan permeson por silenta instalo</string>
<string name="compiled_for_debugging">Kompilita por sencimigado</string> <string name="compiled_for_debugging">Kompilita por sencimigado</string>
<string name="unknown_FORMAT">Nekonata: %s</string> <string name="unknown_FORMAT">Nekonata: %s</string>
<string name="import_settings_title">Importi Agordojn</string> <string name="import_settings_title">Importi Agordojn</string>
@ -240,4 +220,4 @@
<string name="has_tethered_network">Ligita al certa retservo</string> <string name="has_tethered_network">Ligita al certa retservo</string>
<string name="require_background_access">Postuli fonan aliron</string> <string name="require_background_access">Postuli fonan aliron</string>
<string name="require_background_access_DESC">Fona aliro estas bezonata por ĝuste fari fonan sinkronigon</string> <string name="require_background_access_DESC">Fona aliro estas bezonata por ĝuste fari fonan sinkronigon</string>
</resources> </resources>

View File

@ -74,8 +74,6 @@
<string name="light">Claro</string> <string name="light">Claro</string>
<string name="link_copied_to_clipboard">Enlace copiado</string> <string name="link_copied_to_clipboard">Enlace copiado</string>
<string name="links">Enlaces</string> <string name="links">Enlaces</string>
<string name="list_animation">Animaciones de la lista</string>
<string name="list_animation_description">Mostrar la animación de la lista en la página principal</string>
<string name="merging_FORMAT">Uniendo %s</string> <string name="merging_FORMAT">Uniendo %s</string>
<string name="name">Nombre</string> <string name="name">Nombre</string>
<string name="network_error_DESC">Error de red</string> <string name="network_error_DESC">Error de red</string>
@ -112,14 +110,10 @@
<string name="recently_updated">Actualizado recientemente</string> <string name="recently_updated">Actualizado recientemente</string>
<string name="repositories">Repositorios</string> <string name="repositories">Repositorios</string>
<string name="repository">Repositorio</string> <string name="repository">Repositorio</string>
<string name="repository_not_used_DESC">Este repositorio no se ha utilizado todavía. Actívalo para ver las aplicaciones que contiene.</string>
<string name="repository_unsigned_DESC">Sin firmar. No se ha podido verificar la lista de aplicaciones. Ten cuidado al descargar aplicaciones de repositorios no firmados.</string> <string name="repository_unsigned_DESC">Sin firmar. No se ha podido verificar la lista de aplicaciones. Ten cuidado al descargar aplicaciones de repositorios no firmados.</string>
<string name="requires_FORMAT">Requiere %s</string> <string name="requires_FORMAT">Requiere %s</string>
<string name="root_permission">Instalación silenciosa</string>
<string name="root_permission_description">Conceder permiso root para las instalaciones silenciosas</string>
<string name="save">Guardar</string> <string name="save">Guardar</string>
<string name="saving_details">Guardando detalles…</string> <string name="saving_details">Guardando detalles…</string>
<string name="screenshots">Capturas de pantalla</string>
<string name="search">Buscar</string> <string name="search">Buscar</string>
<string name="select_mirror">Selecciona un espejo</string> <string name="select_mirror">Selecciona un espejo</string>
<string name="share">Compartir</string> <string name="share">Compartir</string>
@ -140,7 +134,6 @@
<string name="syncing_FORMAT">Sincronizando %s…</string> <string name="syncing_FORMAT">Sincronizando %s…</string>
<string name="system">Sistema</string> <string name="system">Sistema</string>
<string name="tap_to_install_DESC">Pulse para instalar.</string> <string name="tap_to_install_DESC">Pulse para instalar.</string>
<string name="target">Objetivo</string>
<string name="theme">Tema</string> <string name="theme">Tema</string>
<string name="themes">Temas</string> <string name="themes">Temas</string>
<string name="tracks_or_reports_your_activity">Rastrea o informa de tu actividad</string> <string name="tracks_or_reports_your_activity">Rastrea o informa de tu actividad</string>
@ -148,10 +141,8 @@
<string name="unknown">Desconocido</string> <string name="unknown">Desconocido</string>
<string name="unknown_error_DESC">Error desconocido.</string> <string name="unknown_error_DESC">Error desconocido.</string>
<string name="unknown_FORMAT">Desconocido: %s</string> <string name="unknown_FORMAT">Desconocido: %s</string>
<string name="unsigned">No firmado</string>
<string name="unstable_updates">Actualizaciones inestables</string> <string name="unstable_updates">Actualizaciones inestables</string>
<string name="unstable_updates_summary">Sugerir la instalación de versiones inestables</string> <string name="unstable_updates_summary">Sugerir la instalación de versiones inestables</string>
<string name="unverified">No verificado</string>
<string name="update">Actualizar</string> <string name="update">Actualizar</string>
<string name="updates">Actualizaciones</string> <string name="updates">Actualizaciones</string>
<string name="upstream_source_code_is_not_free">El código fuente no es libre</string> <string name="upstream_source_code_is_not_free">El código fuente no es libre</string>
@ -172,19 +163,13 @@
<string name="prefs_language_title">Idioma</string> <string name="prefs_language_title">Idioma</string>
<string name="prefs_personalization">Personalización</string> <string name="prefs_personalization">Personalización</string>
<string name="show_less">Mostrar menos</string> <string name="show_less">Mostrar menos</string>
<string name="latest">Lo más reciente</string>
<string name="explore">Explorar</string>
<string name="installed_applications">Aplicaciones instaladas</string>
<string name="sort_filter">Ordenar y filtrar</string>
<string name="update_all">Actualizar todo</string> <string name="update_all">Actualizar todo</string>
<string name="new_applications">Nuevas aplicaciones</string>
<string name="root_installer">Instalador root</string> <string name="root_installer">Instalador root</string>
<string name="installer">Instalador</string> <string name="installer">Instalador</string>
<string name="session_installer">Instalador de sesión</string> <string name="session_installer">Instalador de sesión</string>
<string name="legacy_installer">Instalador heredado</string> <string name="legacy_installer">Instalador heredado</string>
<string name="shizuku_installer">Instalador de Shizuku</string> <string name="shizuku_installer">Instalador de Shizuku</string>
<string name="cleanup_title">Intervalo de limpieza de APKs</string> <string name="cleanup_title">Intervalo de limpieza de APKs</string>
<string name="cleanup_description">Periodo para comprobar y eliminar los archivos descargados</string>
<plurals name="days"> <plurals name="days">
<item quantity="one">Día</item> <item quantity="one">Día</item>
<item quantity="many">Días</item> <item quantity="many">Días</item>
@ -197,9 +182,6 @@
</plurals> </plurals>
<string name="only_on_wifi_with_charging">Solo en Wi-Fi y cargando</string> <string name="only_on_wifi_with_charging">Solo en Wi-Fi y cargando</string>
<string name="io_error_DESC">No se pueden realizar ciertas acciones.</string> <string name="io_error_DESC">No se pueden realizar ciertas acciones.</string>
<string name="no_internet">No hay conexión a internet</string>
<string name="allow_collapsing_toolbar">Permitir que la barra superior de la aplicación se expanda</string>
<string name="allow_collapsing_toolbar_DESC">Permitir que la barra superior de la aplicación se expanda y se contraiga</string>
<string name="material_you_desc">Utiliza el tema de color Material You</string> <string name="material_you_desc">Utiliza el tema de color Material You</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="favourites">Favoritas</string> <string name="favourites">Favoritas</string>
@ -208,7 +190,6 @@
<string name="force_clean_up_DESC">Limpiar archivos redundantes</string> <string name="force_clean_up_DESC">Limpiar archivos redundantes</string>
<string name="enable_repo">Habilitar el repositorio</string> <string name="enable_repo">Habilitar el repositorio</string>
<string name="installing">Instalando</string> <string name="installing">Instalando</string>
<string name="restart_app">Reinicia Droid-ify para ver los cambios</string>
<string name="waiting_to_start_installation">Esperando para iniciar la instalación…</string> <string name="waiting_to_start_installation">Esperando para iniciar la instalación…</string>
<string name="auto_update">Actualizar automáticamente las aplicaciones</string> <string name="auto_update">Actualizar automáticamente las aplicaciones</string>
<string name="auto_update_apps">Intentar instalar actualizaciones automáticamente</string> <string name="auto_update_apps">Intentar instalar actualizaciones automáticamente</string>
@ -221,7 +202,6 @@
<string name="special_credits">Gracias a</string> <string name="special_credits">Gracias a</string>
<string name="home_screen_swiping">Deslizamiento por la pantalla de inicio</string> <string name="home_screen_swiping">Deslizamiento por la pantalla de inicio</string>
<string name="home_screen_swiping_DESC">Permitir al usuario pasar de una página a otra en la pantalla de inicio</string> <string name="home_screen_swiping_DESC">Permitir al usuario pasar de una página a otra en la pantalla de inicio</string>
<string name="label_copy">Copiar</string>
<string name="repository_not_found">No pudimos encontrar el siguiente repositorio</string> <string name="repository_not_found">No pudimos encontrar el siguiente repositorio</string>
<string name="proxy_port_error_not_int">El puerto del proxy sólo puede ser un número entero</string> <string name="proxy_port_error_not_int">El puerto del proxy sólo puede ser un número entero</string>
<string name="import_settings_title">Importar configuración</string> <string name="import_settings_title">Importar configuración</string>
@ -245,4 +225,14 @@
<string name="uninstalled_application">Desinstalada</string> <string name="uninstalled_application">Desinstalada</string>
<string name="insufficient_storage">Espacio insuficiente</string> <string name="insufficient_storage">Espacio insuficiente</string>
<string name="insufficient_storage_DESC">No hay suficiente espacio libre en el dispositivo para instalar esta aplicación. Intente liberar algo de espacio</string> <string name="insufficient_storage_DESC">No hay suficiente espacio libre en el dispositivo para instalar esta aplicación. Intente liberar algo de espacio</string>
</resources> <string name="error_shizuku_not_granted_DESC">No se permitió el permiso del servicio de Shizuku. Por favor revisa la app de Shizuku</string>
<string name="error_shizuku_service_unavailable">Shizuku no está ejecutando</string>
<string name="error_shizuku_not_running_DESC">El servicio de Shizuku no está corriendo. Por favor revisa la app de Shizuku</string>
<string name="error_shizuku_not_granted">Falta permiso de Shizuku</string>
<string name="error_shizuku_not_installed">Shizuku no está instalado</string>
<string name="error_shizuku_not_installed_DESC">Al parecer Shizuku no está instalado</string>
<string name="open_shizuku">Abrir Shizuku</string>
<string name="switch_to_default_installer">Cambiar a Predeterminado</string>
<string name="label_open_video">Video</string>
<string name="label_unknown_sdk">Desconocido (%d)</string>
</resources>

View File

@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="cannot_open_link">Linki ei saa avada</string>
<string name="auto_update_apps">Ürita värskendusi automaatselt paigaldada</string>
<string name="add_repository">Lisa hoidla</string>
<string name="address">Aadress</string>
<string name="all_applications">Kõik rakendused</string>
<string name="already_exists">Juba olemas</string>
<string name="always">Alati</string>
<string name="amoled">Must</string>
<string name="anti_features">Anti-omadused</string>
<string name="application">Rakendus</string>
<string name="author_email">Autori e-post</string>
<string name="author_website">Autori veebileht</string>
<string name="auto_update">Uuenda rakendusi automaatselt</string>
<string name="available">Avasta</string>
<string name="bug_tracker">Veajälgija</string>
<string name="cancel">Tühista</string>
<string name="cant_edit_sync_DESC">Hoidlat ei saa redigeerida, kuna seda sünkroonitakse praegu.</string>
<string name="changelog">Muudatuste logi</string>
<string name="changes">Muudatused</string>
<string name="checking_repository">Hoidla kontrollimine…</string>
<string name="cleanup_title">APK puhastamise intervall</string>
<string name="confirmation">Kinnitus</string>
<string name="contains_non_free_media">Sisaldab mittevaba meediat</string>
<string name="could_not_download_FORMAT">%s allalaadimine nurjus</string>
<string name="could_not_sync_FORMAT">%s sünkroonimine nurjus</string>
<string name="credits">Tänud</string>
<string name="dark">Tume</string>
<string name="delete">Kustuta</string>
<string name="description">Kirjeldus</string>
<string name="details">Üksikasjad</string>
<string name="donate">Anneta</string>
<string name="downloading">Allalaadimine</string>
<string name="downloaded_FORMAT">%s laeti alla</string>
<string name="edit_repository">Redigeeri hoidlat</string>
<string name="error_shizuku_service_unavailable">Shizuku ei tööta</string>
<string name="error_shizuku_not_running_DESC">Shizuku teenus ei tööta. Palun kontrolli Shizuku rakendust</string>
<string name="error_shizuku_not_granted">Shizuku luba puudub</string>
<string name="error_shizuku_not_installed">Shizuku pole paigaldatud</string>
<string name="import_export">Impordi/ekspordi</string>
<string name="import_settings_title">Impordi seaded</string>
<string name="import_settings_DESC">Impordi seaded ja lemmikud failist</string>
<string name="export_settings_title">Ekspordi seaded</string>
<string name="import_repos_DESC">Impordi kõik hoidlad failist</string>
<string name="export_repos_title">Ekspordi hoidlad</string>
<string name="export_repos_DESC">Ekspordi kõik hoidlad faili</string>
<string name="enable_repo">Luba hoidla</string>
<string name="favourites">Lemmikud</string>
<string name="fingerprint">Sõrmejälg</string>
<string name="force_clean_up">Sundpuhastus</string>
<string name="force_clean_up_DESC">Puhastab üleliigsed failid</string>
<string name="has_advertising">Sisaldab reklaami</string>
<string name="has_non_free_dependencies">Omab mittevabu sõltuvusi</string>
<string name="has_non_free_components">Omab mittevabu komponente</string>
<string name="has_security_vulnerabilities">Omab turvaauke</string>
<string name="has_tethered_network">Seotud teatud võrguteenusega</string>
<string name="home_screen_swiping">Avakuval nipsamine</string>
<string name="home_screen_swiping_DESC">Luba kasutajal avakuval nipsates lehti vahetada</string>
<plurals name="hours">
<item quantity="one">tund</item>
<item quantity="other">tundi</item>
</plurals>
<string name="connection_error_DESC">Serveriga ei õnnestunud ühendust luua</string>
<string name="http_proxy">HTTP puhverserver</string>
<string name="incompatible_api_max_DESC_FORMAT">Suurim API versioon on %d.</string>
<string name="incompatible_features_DESC">Puuduvad funktsioonid.</string>
<string name="incompatible_older_DESC">See versioon on vanem kui seadmesse paigaldatud versioon. Esmalt eemalda see.</string>
<string name="incompatible_version">Ühildumatu versioon</string>
<string name="action_failed">Toiming nurjus</string>
<string name="all_applications_up_to_date">Kõik rakendused on ajakohased</string>
<plurals name="days">
<item quantity="one">päev</item>
<item quantity="other">päeva</item>
</plurals>
<string name="application_not_found">Seda rakendust ei leitud</string>
<string name="error_shizuku_not_granted_DESC">Shizuku teenusele pole luba antud. Palun kontrolli Shizuku rakendust</string>
<string name="compiled_for_debugging">Kompileeritud silumiseks</string>
<string name="could_not_validate_FORMAT">%s valideerimine nurjus</string>
<string name="connecting">Ühendamine…</string>
<string name="socket_error_DESC">Serveril ei õnnestunud uut paketti pakkuda.</string>
<string name="incompatible_versions">Ühildumatud versioonid</string>
<string name="delete_repository_DESC">Kas kustutada hoidla?</string>
<string name="import_repos_title">Impordi hoidlad</string>
<string name="downloading_FORMAT">%s allalaadimine…</string>
<string name="incompatible_api_DESC_FORMAT">Sinu %1$s (API versioon %2$d) ei ole toetatud. %3$s</string>
<string name="error_shizuku_not_installed_DESC">Paistab, et Shizuku pole paigaldatud</string>
<string name="export_settings_DESC">Ekspordi seaded ja lemmikud faili</string>
<string name="file_format_error_DESC">Kehtetu failivorming.</string>
<string name="incompatible_api_min_DESC_FORMAT">Väikseim API versioon on %d.</string>
<string name="incompatible_platforms_DESC_FORMAT">Sinu %1$s platvormi ei toetata. Toetatud platvormid: %2$s.</string>
<string name="ignore_all_updates">Eira kõiki uusi versioone</string>
<string name="ignore_this_update">Eira seda versiooni</string>
<string name="http_error_DESC">Vigane serveri vastus.</string>
<string name="incompatible_signature_DESC">See versioon on allkirjastatud erineva sertifikaadiga kui seadmesse paigaldatu. Esmalt eemalda see.</string>
<string name="uninstalled_application">Eemaldatud</string>
<string name="integrity_check_error_DESC">Terviklikkust ei saanud kontrollida.</string>
<string name="invalid_address">Vigane aadress</string>
<string name="invalid_metadata_error_DESC">Vigased metaandmed.</string>
<string name="invalid_permissions_error_DESC">Vigased load.</string>
<string name="invalid_signature_error_DESC">Vigane allkiri.</string>
<string name="io_error_DESC">Teatud toiminguid ei saa teha.</string>
<string name="launch">Käivita</string>
<string name="license">Litsents</string>
<string name="license_FORMAT">%s litsents</string>
<string name="light">Hele</string>
<string name="material_you_desc">Kasuta Material You värviskeemi</string>
<string name="merging_FORMAT">Ühendatakse %s</string>
<string name="name">Nimi</string>
<string name="new_updates_available">Saadaval on rakenduste uued versioonid</string>
<plurals name="new_updates_DESC_FORMAT">
<item quantity="one">%d uuendus saadaval.</item>
<item quantity="other">%d uuendust saadaval.</item>
</plurals>
<string name="no_applications_available">Saadaolevaid rakendusi pole</string>
<string name="no_proxy">Puhverserverita</string>
<string name="notify_about_updates">Teavita uuendustest</string>
<string name="notify_about_updates_summary">Kuva märguanne, kui uued versioonid on saadaval</string>
<string name="number_of_applications">Rakenduste arv</string>
<string name="ok">OK</string>
<string name="only_compatible_with_FORMAT">Ühildub ainult %s</string>
<string name="only_on_wifi">Ainult WiFi võrgus</string>
<string name="open_DESC_FORMAT">Kas avada %s?</string>
<string name="other">Muud</string>
<string name="password">Parool</string>
<string name="permissions">Load</string>
<string name="project_website">Projekti veebileht</string>
<string name="promotes_non_free_network_services">Reklaamib mittevabu võrguteenuseid</string>
<string name="provided_by_FORMAT">Pakub %s</string>
<string name="proxy_host">Puhverserveri masin</string>
<string name="proxy_type">Puhverserveri tüüp</string>
<string name="repository">Hoidla</string>
<string name="repository_not_found">Järgnevat hoidlat ei leitud</string>
<string name="repository_unreachable">Hoidla on kättesaamatu</string>
<string name="requires_FORMAT">Nõuab %s</string>
<string name="require_background_access">Küsi taustal töötamise luba</string>
<string name="require_background_access_DESC">Taustal sünkroonimise õnnestumiseks tuleb lubada taustal töötamine</string>
<string name="save">Salvesta</string>
<string name="saving_details">Üksikasjade salvestamine…</string>
<string name="settings">Seaded</string>
<string name="share">Jaga</string>
<string name="show_more">Näita rohkem</string>
<string name="signature_FORMAT">Allkiri %s</string>
<string name="signed_using_unsafe_algorithm">Allkirjastatud kasutades ebaturvalist algoritmi</string>
<string name="skip">Jäta vahele</string>
<string name="special_credits">Eriline tänu</string>
<string name="suggested">Soovitatud</string>
<string name="sync_repositories">Sünkrooni hoidlad</string>
<string name="syncing_FORMAT">%s sünkroonimine…</string>
<string name="system">Süsteem</string>
<string name="tap_to_install_DESC">Puuduta paigaldamiseks.</string>
<string name="tracks_or_reports_your_activity">Jälgib või teatab sinu tegevusest</string>
<string name="ignore_signature">Eira allkirja</string>
<string name="no_applications_installed">Paigaldatud rakendusi pole</string>
<string name="network_error_DESC">Võrgu viga</string>
<string name="links">Lingid</string>
<string name="never">Mitte kunagi</string>
<string name="installation_failed_DESC">%s paigaldamine nurjus</string>
<string name="uninstalled_application_DESC">%s eemaldati</string>
<string name="invalid_fingerprint_format">Vigane sõrmejäljevorming</string>
<string name="invalid_username_format">Vigane kasutajanime vorming</string>
<string name="link_copied_to_clipboard">Link kopeeriti</string>
<string name="material_you">Material You</string>
<string name="open_shizuku">Ava Shizuku</string>
<string name="no_matching_applications_found">Selliseid rakendusi ei leitud</string>
<string name="recently_updated">Hiljuti värskendatud</string>
<string name="no_description_available_DESC">Kirjeldus puudub</string>
<string name="only_on_wifi_with_charging">Ainult WiFi võrgus laadimise ajal</string>
<string name="plus_more_FORMAT">+%d veel</string>
<string name="promotes_non_free_software">Reklaamib mittevaba tarkvara</string>
<string name="proxy_port_error_not_int">Puhverserveri port saab olla ainult täisarv</string>
<string name="show_older_versions">Kuva vanemad versioonid</string>
<string name="themes">Teemad</string>
<string name="parsing_index_error_DESC">Indeksfaili ei saanud sõeluda.</string>
<string name="proxy_port">Puhverserveri port</string>
<string name="repositories">Hoidlad</string>
<string name="password_missing">Parool puudub</string>
<string name="proxy">Puhverserver</string>
<string name="sorting_order">Sorteerimisjärjekord</string>
<string name="repository_unsigned_DESC">Allkirjata. Rakenduste loendit ei saanud kinnitada. Ole ettevaatlik rakenduste allalaadimisel allkirjastamata hoidlatest.</string>
<string name="size">Suurus</string>
<string name="source_code_no_longer_available">Lähtekood pole enam saadaval</string>
<string name="switch_to_default_installer">Lülitu vaikeseadele</string>
<string name="syncing">Sünkroonimine</string>
<string name="uninstall">Eemalda</string>
<string name="unknown_error_DESC">Tundmatu viga.</string>
<string name="unstable_updates_summary">Paku ebastabiilsete versioonide paigaldamist</string>
<string name="unknown">Tundmatu</string>
<string name="unknown_FORMAT">Tundmatu: %s</string>
<string name="unstable_updates">Ebastabiilsed uuendused</string>
<string name="processing_FORMAT">%1$s töötlemine…</string>
<string name="search">Otsi</string>
<string name="select_mirror">Vali peegel</string>
<string name="socks_proxy">SOCKS puhverserver</string>
<string name="source_code">Lähtekood</string>
<string name="sync_repositories_automatically">Sünkrooni hoidlad automaatselt</string>
<string name="theme">Teema</string>
<string name="incompatible_versions_summary">Kuva seadmega mitteühilduvaid rakenduste versioone</string>
<string name="incompatible_with_FORMAT">Ei ühildu rakendusega %s</string>
<string name="install">Paigalda</string>
<string name="install_types">Paigalduse tüübid</string>
<string name="installer">Paigaldaja</string>
<string name="insufficient_storage">Ruumi napib</string>
<string name="legacy_installer">Vana paigaldaja</string>
<string name="session_installer">Seansi paigaldaja</string>
<string name="shizuku_installer">Shizuku paigaldaja</string>
<string name="root_installer">Root-paigaldaja</string>
<string name="shizuku_not_alive">Shizuku ei tööta</string>
<string name="shizuku_not_installed">Shizuku pole paigaldatud</string>
<string name="installed">Paigaldatud</string>
<string name="installing">Paigaldamine</string>
<string name="installation_failed">Paigaldamine nurjus</string>
<string name="ignore_signature_summary">*Hoiatus* LSPosed-kasutajate või edasijõudnud kasutajate puhul eira APK paigaldamisel allkirja kontrollimist</string>
<string name="update">Uuendus</string>
<string name="upstream_source_code_is_not_free">Algne lähtekood ei ole vaba</string>
<string name="username">Kasutajanimi</string>
<string name="username_missing">Kasutajanimi puudub</string>
<string name="validation_index_error_DESC">Indeksit ei saadud kinnitada.</string>
<string name="version">Versioon</string>
<string name="version_FORMAT">Versioon %s</string>
<string name="versions">Versioonid</string>
<string name="waiting_to_start_installation">Paigaldamise alustamise ootel…</string>
<string name="whats_new">Mis on uut</string>
<string name="website">Veebileht</string>
<string name="update_all">Uuenda kõik</string>
<string name="label_open_video">Video</string>
<string name="label_unknown_sdk">Tundmatu (%d)</string>
<string name="show_less">Näita vähem</string>
<string name="prefs_personalization">Kohandamine</string>
<string name="contains_nsfw">Sisaldab NSFW sisu</string>
<string name="insufficient_storage_DESC">Seadmes pole selle rakenduse paigaldamiseks piisavalt vaba ruumi. Proovi ruumi vabastada</string>
<string name="waiting_to_start_download">Allalaadimise alustamise ootel…</string>
<string name="prefs_language_title">Keel</string>
<string name="updates">Uuendused</string>
</resources>

View File

@ -50,7 +50,6 @@
<string name="invalid_metadata_error_DESC">فراداده نادرست.</string> <string name="invalid_metadata_error_DESC">فراداده نادرست.</string>
<string name="invalid_permissions_error_DESC">دسترسی‌های نادرست.</string> <string name="invalid_permissions_error_DESC">دسترسی‌های نادرست.</string>
<string name="links">پیوندها</string> <string name="links">پیوندها</string>
<string name="list_animation_description">نمایش لیست انیمیشن در صفحه‌اصلی</string>
<string name="merging_FORMAT">ادغام %s</string> <string name="merging_FORMAT">ادغام %s</string>
<string name="name">نام</string> <string name="name">نام</string>
<string name="network_error_DESC">خطای شبکه</string> <string name="network_error_DESC">خطای شبکه</string>
@ -63,19 +62,16 @@
<string name="other">دیگر</string> <string name="other">دیگر</string>
<string name="parsing_index_error_DESC">فایل ایندکس قابل تحلیل نیست.</string> <string name="parsing_index_error_DESC">فایل ایندکس قابل تحلیل نیست.</string>
<string name="open_DESC_FORMAT">باز کردن %s ؟</string> <string name="open_DESC_FORMAT">باز کردن %s ؟</string>
<string name="repository_not_used_DESC">این مخزن هنوز استفاده نشده. فعالش کنید تا کاره‌های داخلش را ببینید.</string>
<string name="unknown">ناشناس</string> <string name="unknown">ناشناس</string>
<string name="uninstall">پاک کردن</string> <string name="uninstall">پاک کردن</string>
<string name="waiting_to_start_download">منتظر شروع بارگیری…</string> <string name="waiting_to_start_download">منتظر شروع بارگیری…</string>
<string name="versions">نسخه‌ها</string> <string name="versions">نسخه‌ها</string>
<string name="cleanup_title">وقفه پاکسازی APK</string> <string name="cleanup_title">وقفه پاکسازی APK</string>
<string name="cleanup_description">دوره بررسی و پاک سازی داده های دریافت شده</string>
<string name="has_security_vulnerabilities">دارای حفره‌های امنیتی</string> <string name="has_security_vulnerabilities">دارای حفره‌های امنیتی</string>
<string name="http_error_DESC">پاسخ نادرست از مرکز.</string> <string name="http_error_DESC">پاسخ نادرست از مرکز.</string>
<string name="incompatible_versions">نسخه‌های ناسازگار</string> <string name="incompatible_versions">نسخه‌های ناسازگار</string>
<string name="incompatible_with_FORMAT">ناسازگاری با %s</string> <string name="incompatible_with_FORMAT">ناسازگاری با %s</string>
<string name="invalid_signature_error_DESC">امضای نادرست.</string> <string name="invalid_signature_error_DESC">امضای نادرست.</string>
<string name="list_animation">لیست انیمیشن‌ها</string>
<string name="no_applications_available">هیچ کاره‌ای فراهم نیست</string> <string name="no_applications_available">هیچ کاره‌ای فراهم نیست</string>
<string name="no_applications_installed">هیچ کاره‌ای نصب نیست</string> <string name="no_applications_installed">هیچ کاره‌ای نصب نیست</string>
<string name="number_of_applications">تعداد کاره‌ها</string> <string name="number_of_applications">تعداد کاره‌ها</string>
@ -85,7 +81,6 @@
<string name="plus_more_FORMAT">%d+ بیشتر</string> <string name="plus_more_FORMAT">%d+ بیشتر</string>
<string name="settings">تنظیمات</string> <string name="settings">تنظیمات</string>
<string name="processing_FORMAT">درحال‌پردازش %1$s …</string> <string name="processing_FORMAT">درحال‌پردازش %1$s …</string>
<string name="root_permission_description">اجازه دسترسی روت برای نصب بدون‌پرسش</string>
<string name="signature_FORMAT">امضاء %s</string> <string name="signature_FORMAT">امضاء %s</string>
<string name="signed_using_unsafe_algorithm">با الگوریتم ناامن امضاء شده</string> <string name="signed_using_unsafe_algorithm">با الگوریتم ناامن امضاء شده</string>
<string name="size">حجم</string> <string name="size">حجم</string>
@ -103,10 +98,7 @@
<string name="prefs_language_title">زبان</string> <string name="prefs_language_title">زبان</string>
<string name="prefs_personalization">شخصی‌سازی</string> <string name="prefs_personalization">شخصی‌سازی</string>
<string name="show_less">نمایش کمتر</string> <string name="show_less">نمایش کمتر</string>
<string name="latest">آخرین</string>
<string name="explore">اکتشاف</string>
<string name="update_all">به‌روزرسانی همه</string> <string name="update_all">به‌روزرسانی همه</string>
<string name="sort_filter">ترتیب و فیلتر</string>
<string name="root_installer">نصاب روت</string> <string name="root_installer">نصاب روت</string>
<string name="installer">نصاب</string> <string name="installer">نصاب</string>
<string name="legacy_installer">نصاب قدیمی</string> <string name="legacy_installer">نصاب قدیمی</string>
@ -126,8 +118,6 @@
<string name="repositories">مخازن</string> <string name="repositories">مخازن</string>
<string name="repository">مخزن</string> <string name="repository">مخزن</string>
<string name="requires_FORMAT">نیازمند %s</string> <string name="requires_FORMAT">نیازمند %s</string>
<string name="root_permission">نصب بدون‌پرسش</string>
<string name="screenshots">اسکرین‌شات‌ها</string>
<string name="search">جستجو</string> <string name="search">جستجو</string>
<string name="select_mirror">از کجا دریافت کنم</string> <string name="select_mirror">از کجا دریافت کنم</string>
<string name="share">هم‌رسانی</string> <string name="share">هم‌رسانی</string>
@ -135,15 +125,12 @@
<string name="saving_details">درحال‌ذخیره جزییات…</string> <string name="saving_details">درحال‌ذخیره جزییات…</string>
<string name="show_more">نمایش بیشتر</string> <string name="show_more">نمایش بیشتر</string>
<string name="show_older_versions">نمایش نسخه‌های قدیمی</string> <string name="show_older_versions">نمایش نسخه‌های قدیمی</string>
<string name="target">هدف</string>
<string name="theme">پوسته</string> <string name="theme">پوسته</string>
<string name="themes">پوسته‌ها</string> <string name="themes">پوسته‌ها</string>
<string name="tracks_or_reports_your_activity">رهگیری یا گزارش فعالیت شما</string> <string name="tracks_or_reports_your_activity">رهگیری یا گزارش فعالیت شما</string>
<string name="unknown_error_DESC">خطای ناشناخته.</string> <string name="unknown_error_DESC">خطای ناشناخته.</string>
<string name="unknown_FORMAT">ناشناخته: %s</string> <string name="unknown_FORMAT">ناشناخته: %s</string>
<string name="unsigned">امضاءنشده</string>
<string name="unstable_updates">به‌روزرسانی های ناپایدار</string> <string name="unstable_updates">به‌روزرسانی های ناپایدار</string>
<string name="unverified">تایید نشده</string>
<string name="username">نام‌کاربری</string> <string name="username">نام‌کاربری</string>
<string name="validation_index_error_DESC">ایندکس قابل تایید شدن نیست.</string> <string name="validation_index_error_DESC">ایندکس قابل تایید شدن نیست.</string>
<string name="whats_new">چه خبر</string> <string name="whats_new">چه خبر</string>
@ -188,20 +175,14 @@
<string name="unstable_updates_summary">پیشنهاد نصب نسخه‌های آزمایشی</string> <string name="unstable_updates_summary">پیشنهاد نصب نسخه‌های آزمایشی</string>
<string name="source_code_no_longer_available">کد منبع دیگر موجود نیست</string> <string name="source_code_no_longer_available">کد منبع دیگر موجود نیست</string>
<string name="syncing_FORMAT">در حال به‌روزرسانی %s …</string> <string name="syncing_FORMAT">در حال به‌روزرسانی %s …</string>
<string name="installed_applications">کاره‌های نصب شده</string>
<string name="updates">به‌روزرسانی ها</string> <string name="updates">به‌روزرسانی ها</string>
<string name="new_applications">کاره‌های جدید</string>
<string name="version">نسخه</string> <string name="version">نسخه</string>
<string name="version_FORMAT">نسخه %s</string> <string name="version_FORMAT">نسخه %s</string>
<string name="no_internet">شما هیچ اتصال اینترنتی ندارید</string>
<string name="io_error_DESC">قادر به انجام برخی اقدامات خاص نیست.</string> <string name="io_error_DESC">قادر به انجام برخی اقدامات خاص نیست.</string>
<string name="allow_collapsing_toolbar">به نوار بالای کاره اجازه دهید تا گسترش یابد</string>
<string name="allow_collapsing_toolbar_DESC">به نوار بالای کاره اجازه دهید تا گسترده و فشرده شود</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="material_you_desc">از پوسته رنگی material you استفاده کنید</string> <string name="material_you_desc">از پوسته رنگی material you استفاده کنید</string>
<string name="enable_repo">مخزن را فعال کنید</string> <string name="enable_repo">مخزن را فعال کنید</string>
<string name="force_clean_up">پاکسازی اجباری</string> <string name="force_clean_up">پاکسازی اجباری</string>
<string name="restart_app">برای مشاهده تغییرات، Droid-ify را مجددا راه اندازی کنید</string>
<string name="favourites">موارد دلخواه</string> <string name="favourites">موارد دلخواه</string>
<string name="repository_unreachable">مخزن قابل دسترسی نیست</string> <string name="repository_unreachable">مخزن قابل دسترسی نیست</string>
<string name="auto_update">به‌روز رسانی خودکار کاره‌ها</string> <string name="auto_update">به‌روز رسانی خودکار کاره‌ها</string>
@ -230,7 +211,6 @@
<string name="socket_error_DESC">سرور در فراهم کردن بسته اطلاعات جدید ناموفق بود</string> <string name="socket_error_DESC">سرور در فراهم کردن بسته اطلاعات جدید ناموفق بود</string>
<string name="connection_error_DESC">اتصال به سرور ناموفق بود</string> <string name="connection_error_DESC">اتصال به سرور ناموفق بود</string>
<string name="home_screen_swiping">حرکات صفحه اصلی</string> <string name="home_screen_swiping">حرکات صفحه اصلی</string>
<string name="label_copy">کپی</string>
<string name="special_credits">تشکر ویژه</string> <string name="special_credits">تشکر ویژه</string>
<string name="require_background_access">نیازمند دسترسی پس‌زمینه</string> <string name="require_background_access">نیازمند دسترسی پس‌زمینه</string>
<string name="require_background_access_DESC">برای همگام‌سازی در پس‌زمینه، دسترسی پس‌زمینه لازم است</string> <string name="require_background_access_DESC">برای همگام‌سازی در پس‌زمینه، دسترسی پس‌زمینه لازم است</string>
@ -241,4 +221,16 @@
<string name="ignore_signature_summary">هنگام نصب APK ، برای کاربران Lsposed یا کاربران پیشرفته ، تأیید امضا نادیده گرفته شود</string> <string name="ignore_signature_summary">هنگام نصب APK ، برای کاربران Lsposed یا کاربران پیشرفته ، تأیید امضا نادیده گرفته شود</string>
<string name="installation_failed_DESC">نصب %s ناموفق بود</string> <string name="installation_failed_DESC">نصب %s ناموفق بود</string>
<string name="insufficient_storage_DESC">فضای کافی برای نصب این برنامه روی دستگاه وجود ندارد. سعی کنید مقداری فضا خالی کنید.</string> <string name="insufficient_storage_DESC">فضای کافی برای نصب این برنامه روی دستگاه وجود ندارد. سعی کنید مقداری فضا خالی کنید.</string>
</resources> <string name="label_targets_sdk">هدف: اندروید %s</string>
<string name="error_shizuku_not_granted">دسترسی شیزوکو داده نشده</string>
<string name="error_shizuku_not_running_DESC">خدمت شیزوکو اجرا نمیشود. لطفا برنامه شیزوکو را بررسی کنید</string>
<string name="error_shizuku_service_unavailable">شیزوکو اجرا نمیشود</string>
<string name="error_shizuku_not_granted_DESC">مجوز خدمت شیزوکو داده نشده. لطفا برنامه شیزوکو را بررسی کنید</string>
<string name="error_shizuku_not_installed">شیزوکو نصب نشده</string>
<string name="error_shizuku_not_installed_DESC">به نظر میرسد شیزوکو نصب نشده</string>
<string name="insufficient_storage">فضای ناکافی</string>
<string name="open_shizuku">بازکردن شیزوکو</string>
<string name="switch_to_default_installer">جابه‌جایی به عادی</string>
<string name="label_open_video">ویدیو</string>
<string name="label_unknown_sdk">ناشناخته (%d)</string>
</resources>

View File

@ -72,8 +72,6 @@
<string name="light">Vaalea</string> <string name="light">Vaalea</string>
<string name="link_copied_to_clipboard">Linkki kopioitu</string> <string name="link_copied_to_clipboard">Linkki kopioitu</string>
<string name="links">Linkit</string> <string name="links">Linkit</string>
<string name="list_animation">Luettelon animaatiot</string>
<string name="list_animation_description">Näytä luettelon animaatio pääsivulla</string>
<string name="merging_FORMAT">Yhdistetään %s</string> <string name="merging_FORMAT">Yhdistetään %s</string>
<string name="name">Nimi</string> <string name="name">Nimi</string>
<string name="network_error_DESC">Verkkovirhe</string> <string name="network_error_DESC">Verkkovirhe</string>
@ -95,11 +93,8 @@
<string name="repositories">Ohjelmavarastot</string> <string name="repositories">Ohjelmavarastot</string>
<string name="repository">Ohjelmavarasto</string> <string name="repository">Ohjelmavarasto</string>
<string name="requires_FORMAT">Vaatii %s</string> <string name="requires_FORMAT">Vaatii %s</string>
<string name="root_permission">Hiljainen asennus</string>
<string name="root_permission_description">Salli pääkäyttäjän oikeudet hiljaisiin asennuksiin</string>
<string name="save">Tallenna</string> <string name="save">Tallenna</string>
<string name="saving_details">Tallennetaan tietoja…</string> <string name="saving_details">Tallennetaan tietoja…</string>
<string name="screenshots">Kuvakaappaukset</string>
<string name="only_on_wifi">Vain Wi-Fi verkossa</string> <string name="only_on_wifi">Vain Wi-Fi verkossa</string>
<string name="open_DESC_FORMAT">Avaa %s\?</string> <string name="open_DESC_FORMAT">Avaa %s\?</string>
<string name="other">Muut</string> <string name="other">Muut</string>
@ -127,9 +122,7 @@
<string name="unknown">Tuntematon</string> <string name="unknown">Tuntematon</string>
<string name="unknown_error_DESC">Tuntematon virhe.</string> <string name="unknown_error_DESC">Tuntematon virhe.</string>
<string name="unknown_FORMAT">Tuntematon: %s</string> <string name="unknown_FORMAT">Tuntematon: %s</string>
<string name="unsigned">Allekirjoittamaton</string>
<string name="unstable_updates">Epävakaat päivitykset</string> <string name="unstable_updates">Epävakaat päivitykset</string>
<string name="unverified">Vahvistamaton</string>
<string name="upstream_source_code_is_not_free">Alkuperäinen lähdekoodi ei ole vapaa</string> <string name="upstream_source_code_is_not_free">Alkuperäinen lähdekoodi ei ole vapaa</string>
<string name="website">Verkkosivusto</string> <string name="website">Verkkosivusto</string>
<string name="whats_new">Mitä uutta</string> <string name="whats_new">Mitä uutta</string>
@ -151,13 +144,11 @@
<string name="syncing_FORMAT">Synkronoidaan %s…</string> <string name="syncing_FORMAT">Synkronoidaan %s…</string>
<string name="themes">Teemat</string> <string name="themes">Teemat</string>
<string name="theme">Teema</string> <string name="theme">Teema</string>
<string name="target">Kohde</string>
<string name="tap_to_install_DESC">Napauta asentaaksesi.</string> <string name="tap_to_install_DESC">Napauta asentaaksesi.</string>
<string name="system">Järjestelmä</string> <string name="system">Järjestelmä</string>
<string name="update">Päivitys</string> <string name="update">Päivitys</string>
<string name="updates">Päivitykset</string> <string name="updates">Päivitykset</string>
<string name="promotes_non_free_software">Edistää ei-vapaita ohjelmia</string> <string name="promotes_non_free_software">Edistää ei-vapaita ohjelmia</string>
<string name="repository_not_used_DESC">Tätä ohjelmavarastoa ei ole vielä käytetty. Ota se käyttöön nähdäksesi siinä olevat sovellukset.</string>
<string name="proxy">Välityspalvelin</string> <string name="proxy">Välityspalvelin</string>
<string name="repository_unsigned_DESC">Allekirjoittamaton. Sovellusluetteloa ei voitu tarkistaa. Ole varovainen ladatessasi sovelluksia allekirjoittamattomista arkistoista.</string> <string name="repository_unsigned_DESC">Allekirjoittamaton. Sovellusluetteloa ei voitu tarkistaa. Ole varovainen ladatessasi sovelluksia allekirjoittamattomista arkistoista.</string>
<string name="version_FORMAT">Versio %s</string> <string name="version_FORMAT">Versio %s</string>
@ -171,19 +162,13 @@
<string name="prefs_personalization">Mukauttaminen</string> <string name="prefs_personalization">Mukauttaminen</string>
<string name="prefs_language_title">Kieli</string> <string name="prefs_language_title">Kieli</string>
<string name="show_less">Näytä vähemmän</string> <string name="show_less">Näytä vähemmän</string>
<string name="latest">Uusimmat</string>
<string name="explore">Tutustu</string>
<string name="update_all">Päivitä kaikki</string> <string name="update_all">Päivitä kaikki</string>
<string name="installed_applications">Asennetut sovellukset</string>
<string name="new_applications">Uudet sovellukset</string>
<string name="sort_filter">Lajittele ja suodata</string>
<string name="installer">Asentaja</string> <string name="installer">Asentaja</string>
<string name="legacy_installer">Vanha asentaja</string> <string name="legacy_installer">Vanha asentaja</string>
<string name="session_installer">Sessioasentaja</string> <string name="session_installer">Sessioasentaja</string>
<string name="root_installer">Root-asentaja</string> <string name="root_installer">Root-asentaja</string>
<string name="shizuku_installer">Shizuku-asentaja</string> <string name="shizuku_installer">Shizuku-asentaja</string>
<string name="cleanup_title">APK:n puhdistusväli</string> <string name="cleanup_title">APK:n puhdistusväli</string>
<string name="cleanup_description">Aika ladattujen tiedostojen tarkistamiseen ja poistamiseen</string>
<plurals name="days"> <plurals name="days">
<item quantity="one">Päivä</item> <item quantity="one">Päivä</item>
<item quantity="other">Päivää</item> <item quantity="other">Päivää</item>
@ -194,9 +179,6 @@
</plurals> </plurals>
<string name="only_on_wifi_with_charging">Vain Wi-Fi verkossa latauksessa ollessa</string> <string name="only_on_wifi_with_charging">Vain Wi-Fi verkossa latauksessa ollessa</string>
<string name="io_error_DESC">Tiettyjä toimia ei voida suorittaa.</string> <string name="io_error_DESC">Tiettyjä toimia ei voida suorittaa.</string>
<string name="allow_collapsing_toolbar">Salli yläosan sovelluspalkin laajeneminen</string>
<string name="allow_collapsing_toolbar_DESC">Salli yläosan sovelluspalkin laajentua ja tiivistyä</string>
<string name="no_internet">Ei internet yhteyttä</string>
<string name="favourites">Suosikit</string> <string name="favourites">Suosikit</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="material_you_desc">Käytä Material You -väriteemaa</string> <string name="material_you_desc">Käytä Material You -väriteemaa</string>
@ -205,7 +187,6 @@
<string name="force_clean_up_DESC">Puhdistaa tarpeettomat tiedostot</string> <string name="force_clean_up_DESC">Puhdistaa tarpeettomat tiedostot</string>
<string name="repository_unreachable">Ohjelmavarasto ei ole tavoitettavissa</string> <string name="repository_unreachable">Ohjelmavarasto ei ole tavoitettavissa</string>
<string name="installing">Asentaa</string> <string name="installing">Asentaa</string>
<string name="restart_app">Käynnistä Droid-ify uudelleen nähdäksesi muutokset</string>
<string name="waiting_to_start_installation">Odotetaan asennuksen aloittamista…</string> <string name="waiting_to_start_installation">Odotetaan asennuksen aloittamista…</string>
<string name="auto_update">Päivitä sovelluksia automaattisesti</string> <string name="auto_update">Päivitä sovelluksia automaattisesti</string>
<string name="auto_update_apps">Yritä asentaa päivitykset automaattisesti</string> <string name="auto_update_apps">Yritä asentaa päivitykset automaattisesti</string>
@ -215,7 +196,6 @@
<string name="shizuku_not_alive">Shizuku ei ole käynnissä</string> <string name="shizuku_not_alive">Shizuku ei ole käynnissä</string>
<string name="connection_error_DESC">Palvelimeen ei saatu yhteyttä</string> <string name="connection_error_DESC">Palvelimeen ei saatu yhteyttä</string>
<string name="socket_error_DESC">Palvelin ei pystynyt toimittamaan uutta pakettia.</string> <string name="socket_error_DESC">Palvelin ei pystynyt toimittamaan uutta pakettia.</string>
<string name="label_copy">Kopioi</string>
<string name="proxy_port_error_not_int">Välityspalvelimen portti voi olla vain kokonaisluku</string> <string name="proxy_port_error_not_int">Välityspalvelimen portti voi olla vain kokonaisluku</string>
<string name="home_screen_swiping_DESC">Salli käyttäjän pyyhkäistä sivujen välillä aloitusnäytössä</string> <string name="home_screen_swiping_DESC">Salli käyttäjän pyyhkäistä sivujen välillä aloitusnäytössä</string>
<string name="repository_not_found">Seuraavaa ohjelmavarastoa ei löytynyt</string> <string name="repository_not_found">Seuraavaa ohjelmavarastoa ei löytynyt</string>

View File

@ -61,7 +61,6 @@
<string name="source_code">Code source</string> <string name="source_code">Code source</string>
<string name="size">Taille</string> <string name="size">Taille</string>
<string name="skip">Ignorer</string> <string name="skip">Ignorer</string>
<string name="screenshots">Captures décran</string>
<string name="search">Chercher</string> <string name="search">Chercher</string>
<string name="select_mirror">Sélectionnez un miroir</string> <string name="select_mirror">Sélectionnez un miroir</string>
<string name="share">Partager</string> <string name="share">Partager</string>
@ -81,7 +80,6 @@
<string name="website">Site internet</string> <string name="website">Site internet</string>
<string name="update">Mettre à jour</string> <string name="update">Mettre à jour</string>
<string name="updates">Mises à jour</string> <string name="updates">Mises à jour</string>
<string name="unverified">Non vérifié</string>
<string name="unknown_FORMAT">Inconnu : %s</string> <string name="unknown_FORMAT">Inconnu : %s</string>
<string name="unknown_error_DESC">Erreur inconnue.</string> <string name="unknown_error_DESC">Erreur inconnue.</string>
<string name="add_repository">Ajouter un dépôt</string> <string name="add_repository">Ajouter un dépôt</string>
@ -98,7 +96,6 @@
<string name="uninstall">Désinstaller</string> <string name="uninstall">Désinstaller</string>
<string name="could_not_validate_FORMAT">Impossible de valider %s</string> <string name="could_not_validate_FORMAT">Impossible de valider %s</string>
<string name="system">Système</string> <string name="system">Système</string>
<string name="target">Cible</string>
<string name="password_missing">Mot de passe manquant</string> <string name="password_missing">Mot de passe manquant</string>
<string name="credits">Crédits</string> <string name="credits">Crédits</string>
<string name="themes">Thèmes</string> <string name="themes">Thèmes</string>
@ -116,7 +113,6 @@
<string name="incompatible_features_DESC">Fonctionnalités manquantes.</string> <string name="incompatible_features_DESC">Fonctionnalités manquantes.</string>
<string name="incompatible_older_DESC">Cette version est plus ancienne que celle qui est installée sur votre appareil. Désinstallez-la dabord.</string> <string name="incompatible_older_DESC">Cette version est plus ancienne que celle qui est installée sur votre appareil. Désinstallez-la dabord.</string>
<string name="notify_about_updates_summary">Afficher une notification quand de nouvelles versions sont disponibles</string> <string name="notify_about_updates_summary">Afficher une notification quand de nouvelles versions sont disponibles</string>
<string name="repository_not_used_DESC">Ce dépôt na pas encore été utilisé. Activez-le pour voir les applications quil contient.</string>
<string name="no_applications_installed">Aucune application installée</string> <string name="no_applications_installed">Aucune application installée</string>
<string name="repository_unsigned_DESC">Non signé. Impossible de vérifier la liste dapplis. Soyez prudents lorsque vous téléchargez des applis de dépôts non signés.</string> <string name="repository_unsigned_DESC">Non signé. Impossible de vérifier la liste dapplis. Soyez prudents lorsque vous téléchargez des applis de dépôts non signés.</string>
<string name="has_non_free_dependencies">Dépend dapplications qui ne sont pas libres</string> <string name="has_non_free_dependencies">Dépend dapplications qui ne sont pas libres</string>
@ -143,8 +139,6 @@
<string name="proxy_host">Adresse du proxy</string> <string name="proxy_host">Adresse du proxy</string>
<string name="proxy_port">Port du proxy</string> <string name="proxy_port">Port du proxy</string>
<string name="proxy_type">Type de proxy</string> <string name="proxy_type">Type de proxy</string>
<string name="root_permission">Installation silencieuse</string>
<string name="root_permission_description">Autoriser la permission de lutilisateur root pour les installations silencieuses</string>
<string name="show_older_versions">Afficher les versions plus anciennes</string> <string name="show_older_versions">Afficher les versions plus anciennes</string>
<string name="signature_FORMAT">Signature %s</string> <string name="signature_FORMAT">Signature %s</string>
<string name="signed_using_unsafe_algorithm">Signé avec un algorithme qui nest pas sécurisé</string> <string name="signed_using_unsafe_algorithm">Signé avec un algorithme qui nest pas sécurisé</string>
@ -152,7 +146,6 @@
<string name="sync_repositories_automatically">Synchroniser les dépôts automatiquement</string> <string name="sync_repositories_automatically">Synchroniser les dépôts automatiquement</string>
<string name="syncing">Synchronisation en cours</string> <string name="syncing">Synchronisation en cours</string>
<string name="syncing_FORMAT">Synchronisation de %s en cours…</string> <string name="syncing_FORMAT">Synchronisation de %s en cours…</string>
<string name="unsigned">Non-signé</string>
<string name="unstable_updates">Mises à jour instables</string> <string name="unstable_updates">Mises à jour instables</string>
<string name="upstream_source_code_is_not_free">Le code source nest pas entièrement libre</string> <string name="upstream_source_code_is_not_free">Le code source nest pas entièrement libre</string>
<string name="username">Nom dutilisateur</string> <string name="username">Nom dutilisateur</string>
@ -161,30 +154,22 @@
<string name="incompatible_api_DESC_FORMAT">Votre %1$s (API version %2$d) nest pas pris en charge. %3$s</string> <string name="incompatible_api_DESC_FORMAT">Votre %1$s (API version %2$d) nest pas pris en charge. %3$s</string>
<string name="integrity_check_error_DESC">Impossible de vérifier lintégrité.</string> <string name="integrity_check_error_DESC">Impossible de vérifier lintégrité.</string>
<string name="invalid_fingerprint_format">Format dempreinte digitale non valide</string> <string name="invalid_fingerprint_format">Format dempreinte digitale non valide</string>
<string name="list_animation">Animations de listes</string>
<string name="processing_FORMAT">Traitement de %1$s…</string> <string name="processing_FORMAT">Traitement de %1$s…</string>
<string name="parsing_index_error_DESC">Impossible danalyser le fichier dindex.</string> <string name="parsing_index_error_DESC">Impossible danalyser le fichier dindex.</string>
<string name="merging_FORMAT">Fusionner %s</string> <string name="merging_FORMAT">Fusionner %s</string>
<string name="list_animation_description">Afficher lanimation de la liste sur la page principale</string>
<string name="sorting_order">Ordre de tri</string> <string name="sorting_order">Ordre de tri</string>
<string name="requires_FORMAT">Requiert %s</string> <string name="requires_FORMAT">Requiert %s</string>
<string name="unstable_updates_summary">Suggérer linstallation de versions instables</string> <string name="unstable_updates_summary">Suggérer linstallation de versions instables</string>
<string name="prefs_language_title">Langue</string> <string name="prefs_language_title">Langue</string>
<string name="prefs_personalization">Personnalisation</string> <string name="prefs_personalization">Personnalisation</string>
<string name="show_less">Afficher moins</string> <string name="show_less">Afficher moins</string>
<string name="latest">Le plus récent</string>
<string name="sort_filter">Trier et filtrer</string>
<string name="new_applications">Nouvelles applications</string>
<string name="explore">Explorer</string>
<string name="update_all">Tout mettre à jour</string> <string name="update_all">Tout mettre à jour</string>
<string name="installed_applications">Applications installées</string>
<string name="session_installer">Installateur de session</string> <string name="session_installer">Installateur de session</string>
<string name="legacy_installer">Installateur hérité</string> <string name="legacy_installer">Installateur hérité</string>
<string name="installer">Installateur</string> <string name="installer">Installateur</string>
<string name="root_installer">Installateur racine</string> <string name="root_installer">Installateur racine</string>
<string name="shizuku_installer">Installateur Shizuku</string> <string name="shizuku_installer">Installateur Shizuku</string>
<string name="cleanup_title">Intervalle de nettoyage des APK</string> <string name="cleanup_title">Intervalle de nettoyage des APK</string>
<string name="cleanup_description">Période de vérification et de suppression des fichiers téléchargés</string>
<plurals name="days"> <plurals name="days">
<item quantity="one">Jour</item> <item quantity="one">Jour</item>
<item quantity="many">Jours</item> <item quantity="many">Jours</item>
@ -197,9 +182,6 @@
</plurals> </plurals>
<string name="only_on_wifi_with_charging">Uniquement sur Wi-Fi et charge</string> <string name="only_on_wifi_with_charging">Uniquement sur Wi-Fi et charge</string>
<string name="io_error_DESC">Impossible deffectuer certaines actions.</string> <string name="io_error_DESC">Impossible deffectuer certaines actions.</string>
<string name="no_internet">Vous navez pas de connexion internet</string>
<string name="allow_collapsing_toolbar_DESC">Autoriser l\'expansion et la réduction de la barre supérieure de l\'appli</string>
<string name="allow_collapsing_toolbar">Autoriser l\'expansion de la barre supérieure de l\'appli</string>
<string name="material_you_desc">Utiliser le thème couleur Material You</string> <string name="material_you_desc">Utiliser le thème couleur Material You</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="favourites">Favoris</string> <string name="favourites">Favoris</string>
@ -208,7 +190,6 @@
<string name="force_clean_up_DESC">Nettoyer les fichiers redondants</string> <string name="force_clean_up_DESC">Nettoyer les fichiers redondants</string>
<string name="enable_repo">Activer le dépôt</string> <string name="enable_repo">Activer le dépôt</string>
<string name="installing">Installation</string> <string name="installing">Installation</string>
<string name="restart_app">Redémarrez Droid-ify pour voir les changements</string>
<string name="waiting_to_start_installation">En attente du démarrage de l\'installation…</string> <string name="waiting_to_start_installation">En attente du démarrage de l\'installation…</string>
<string name="auto_update">Mise à jour automatique des applis</string> <string name="auto_update">Mise à jour automatique des applis</string>
<string name="auto_update_apps">Essayez d\'installer les mises à jour automatiquement</string> <string name="auto_update_apps">Essayez d\'installer les mises à jour automatiquement</string>
@ -221,7 +202,6 @@
<string name="special_credits">Crédits spéciaux</string> <string name="special_credits">Crédits spéciaux</string>
<string name="home_screen_swiping">Balayage de l\'écran d\'accueil</string> <string name="home_screen_swiping">Balayage de l\'écran d\'accueil</string>
<string name="home_screen_swiping_DESC">Permettre à l\'utilisateur de passer d\'une page à l\'autre dans l\'écran d\'accueil</string> <string name="home_screen_swiping_DESC">Permettre à l\'utilisateur de passer d\'une page à l\'autre dans l\'écran d\'accueil</string>
<string name="label_copy">Copier</string>
<string name="repository_not_found">Le dépôt suivant n\'a pas été trouvé</string> <string name="repository_not_found">Le dépôt suivant n\'a pas été trouvé</string>
<string name="proxy_port_error_not_int">Le port proxy ne peut être qu\'un entier</string> <string name="proxy_port_error_not_int">Le port proxy ne peut être qu\'un entier</string>
<string name="import_settings_title">Importer les paramètres</string> <string name="import_settings_title">Importer les paramètres</string>
@ -245,4 +225,14 @@
<string name="ignore_signature_summary">*Attention* Ignorer la vérification de la signature lors de l\'installation de l\'APK, pour les utilisateurs de LSPosed ou les utilisateurs avancés</string> <string name="ignore_signature_summary">*Attention* Ignorer la vérification de la signature lors de l\'installation de l\'APK, pour les utilisateurs de LSPosed ou les utilisateurs avancés</string>
<string name="insufficient_storage">Espace insuffisant</string> <string name="insufficient_storage">Espace insuffisant</string>
<string name="insufficient_storage_DESC">Il n\'y a pas assez d\'espace libre sur l\'appareil pour installer cette application. Essayez de libérer de l\'espace</string> <string name="insufficient_storage_DESC">Il n\'y a pas assez d\'espace libre sur l\'appareil pour installer cette application. Essayez de libérer de l\'espace</string>
</resources> <string name="error_shizuku_not_running_DESC">Le service Shizuku n\'est pas démarré. Merci de le vérifier dans l\'appli Shizuku</string>
<string name="error_shizuku_service_unavailable">Shizuku Non Démarré</string>
<string name="error_shizuku_not_granted">Permission Shizuku Manquante</string>
<string name="error_shizuku_not_granted_DESC">La permission du service Shizuku n\'est pas accordée. Merci de la vérifier via l\'appli Shizuku</string>
<string name="error_shizuku_not_installed">Shizuku Non Installé</string>
<string name="error_shizuku_not_installed_DESC">Shizuku ne semble pas être installé</string>
<string name="open_shizuku">Ouvrir Shizuku</string>
<string name="label_open_video">Vidéo</string>
<string name="label_unknown_sdk">Inconnu (%d)</string>
<string name="switch_to_default_installer">Rétablir par défaut</string>
</resources>

View File

@ -13,7 +13,6 @@
<string name="changes">Cambios</string> <string name="changes">Cambios</string>
<string name="checking_repository">Comprobando o repositorio…</string> <string name="checking_repository">Comprobando o repositorio…</string>
<string name="cleanup_title">APK cleanup interval</string> <string name="cleanup_title">APK cleanup interval</string>
<string name="cleanup_description">Period to check and remove downloaded files</string>
<string name="compiled_for_debugging">Compilado para a depuración</string> <string name="compiled_for_debugging">Compilado para a depuración</string>
<string name="confirmation">Confirmación</string> <string name="confirmation">Confirmación</string>
<string name="connecting">Conectando…</string> <string name="connecting">Conectando…</string>
@ -38,8 +37,6 @@
<string name="light">Claro</string> <string name="light">Claro</string>
<string name="link_copied_to_clipboard">A ligazón copiouse no portapapeis</string> <string name="link_copied_to_clipboard">A ligazón copiouse no portapapeis</string>
<string name="links">Ligazóns</string> <string name="links">Ligazóns</string>
<string name="list_animation">Lista de animacións</string>
<string name="list_animation_description">Mostra a animación da lista na páxina principal</string>
<string name="merging_FORMAT">Fusionando %s</string> <string name="merging_FORMAT">Fusionando %s</string>
<string name="name">Nome</string> <string name="name">Nome</string>
<string name="network_error_DESC">Erro na rede</string> <string name="network_error_DESC">Erro na rede</string>
@ -74,9 +71,7 @@
<string name="recently_updated">Actualizado recentemente</string> <string name="recently_updated">Actualizado recentemente</string>
<string name="repositories">Repositorios</string> <string name="repositories">Repositorios</string>
<string name="repository">Repositorio</string> <string name="repository">Repositorio</string>
<string name="repository_not_used_DESC">Este repositorio aínda non se utilizou. Accéndeo para ver as aplicacións nel.</string>
<string name="requires_FORMAT">Require %s</string> <string name="requires_FORMAT">Require %s</string>
<string name="screenshots">Capturas de pantalla</string>
<string name="search">Procurar</string> <string name="search">Procurar</string>
<string name="select_mirror">Seleccione un espello</string> <string name="select_mirror">Seleccione un espello</string>
<string name="share">Compartir</string> <string name="share">Compartir</string>
@ -91,7 +86,6 @@
<string name="sync_repositories_automatically">Sincronizar repositorios automaticamente</string> <string name="sync_repositories_automatically">Sincronizar repositorios automaticamente</string>
<string name="syncing">Sincronización</string> <string name="syncing">Sincronización</string>
<string name="system">Sistema</string> <string name="system">Sistema</string>
<string name="target">Obxectivo</string>
<string name="theme">Tema</string> <string name="theme">Tema</string>
<string name="themes">Temas</string> <string name="themes">Temas</string>
<string name="tracks_or_reports_your_activity">Rastrexa ou informa da túa actividade</string> <string name="tracks_or_reports_your_activity">Rastrexa ou informa da túa actividade</string>
@ -100,7 +94,6 @@
<string name="unknown_error_DESC">Erro descoñecido.</string> <string name="unknown_error_DESC">Erro descoñecido.</string>
<string name="unknown_FORMAT">Descoñecido: %s</string> <string name="unknown_FORMAT">Descoñecido: %s</string>
<string name="unstable_updates">Actualizacións inestables</string> <string name="unstable_updates">Actualizacións inestables</string>
<string name="unverified">Sen verificar</string>
<string name="update">Actualizar</string> <string name="update">Actualizar</string>
<string name="updates">Actualizacións</string> <string name="updates">Actualizacións</string>
<string name="upstream_source_code_is_not_free">O código fonte non é ceibe</string> <string name="upstream_source_code_is_not_free">O código fonte non é ceibe</string>
@ -114,12 +107,7 @@
<string name="prefs_language_title">Lingua</string> <string name="prefs_language_title">Lingua</string>
<string name="prefs_personalization">Personalización</string> <string name="prefs_personalization">Personalización</string>
<string name="show_less">Mostrar menos</string> <string name="show_less">Mostrar menos</string>
<string name="latest">Derradeiro</string>
<string name="explore">Explora</string>
<string name="update_all">Actualiza todo</string> <string name="update_all">Actualiza todo</string>
<string name="installed_applications">Aplicacións instaladas</string>
<string name="sort_filter">Ordear e filtrar</string>
<string name="new_applications">Novas aplicacións</string>
<string name="all_applications">Todas as aplicacións</string> <string name="all_applications">Todas as aplicacións</string>
<string name="amoled">Negro</string> <string name="amoled">Negro</string>
<string name="already_exists">Xa existe</string> <string name="already_exists">Xa existe</string>
@ -178,8 +166,6 @@
<string name="only_on_wifi">Só con wifi</string> <string name="only_on_wifi">Só con wifi</string>
<string name="only_on_wifi_with_charging">Só con wifi e carga</string> <string name="only_on_wifi_with_charging">Só con wifi e carga</string>
<string name="proxy">Proxy</string> <string name="proxy">Proxy</string>
<string name="root_permission">Instalación silenciosa</string>
<string name="root_permission_description">Permitir permiso de root para instalacións silenciosas</string>
<string name="repository_unsigned_DESC">Sen asinar. Non se puido verificar a lista de solicitudes. Teña coidado ao descargar aplicacións desde repositorios sen asinar.</string> <string name="repository_unsigned_DESC">Sen asinar. Non se puido verificar a lista de solicitudes. Teña coidado ao descargar aplicacións desde repositorios sen asinar.</string>
<string name="save">Gardar</string> <string name="save">Gardar</string>
<string name="sorting_order">Orde de clasificación</string> <string name="sorting_order">Orde de clasificación</string>
@ -189,14 +175,10 @@
<string name="show_older_versions">Mostrar versións antigas</string> <string name="show_older_versions">Mostrar versións antigas</string>
<string name="source_code">Código fonte</string> <string name="source_code">Código fonte</string>
<string name="tap_to_install_DESC">Preme para instalar.</string> <string name="tap_to_install_DESC">Preme para instalar.</string>
<string name="unsigned">Sen asinar</string>
<string name="unstable_updates_summary">Suxire instalar versións inestables</string> <string name="unstable_updates_summary">Suxire instalar versións inestables</string>
<string name="validation_index_error_DESC">Non se puido validar o índice.</string> <string name="validation_index_error_DESC">Non se puido validar o índice.</string>
<string name="version">Versión</string> <string name="version">Versión</string>
<string name="io_error_DESC">Non se poden realizar determinadas accións.</string> <string name="io_error_DESC">Non se poden realizar determinadas accións.</string>
<string name="no_internet">Non tes conexión a internet</string>
<string name="allow_collapsing_toolbar">Permitir ca barra das aplicacións superior se amplíe</string>
<string name="allow_collapsing_toolbar_DESC">Permitir cas barras das aplicacións superior se amplíe e contraiga</string>
<string name="material_you">Material You</string> <string name="material_you">Material You</string>
<string name="material_you_desc">Usalo material que coloree o tema</string> <string name="material_you_desc">Usalo material que coloree o tema</string>
<string name="favourites">Favoritas</string> <string name="favourites">Favoritas</string>
@ -205,7 +187,6 @@
<string name="force_clean_up">Forzala limpeza</string> <string name="force_clean_up">Forzala limpeza</string>
<string name="enable_repo">Activalo repositorio</string> <string name="enable_repo">Activalo repositorio</string>
<string name="installing">Instalando</string> <string name="installing">Instalando</string>
<string name="restart_app">Reinicia Droid-ify para velos cambios</string>
<string name="waiting_to_start_installation">Agardando para iniciala instalación…</string> <string name="waiting_to_start_installation">Agardando para iniciala instalación…</string>
<string name="auto_update">Actualizacións automática das aplicacións</string> <string name="auto_update">Actualizacións automática das aplicacións</string>
<string name="auto_update_apps">Tenta instalar actualizacións automaticamente</string> <string name="auto_update_apps">Tenta instalar actualizacións automaticamente</string>
@ -213,4 +194,4 @@
<string name="import_export">Importación/exportación</string> <string name="import_export">Importación/exportación</string>
<string name="import_settings_DESC">Importalos axustes e favoritos dende un fixeiro</string> <string name="import_settings_DESC">Importalos axustes e favoritos dende un fixeiro</string>
<string name="cannot_open_link">Non se pode abrir a ligazón</string> <string name="cannot_open_link">Non se pode abrir a ligazón</string>
</resources> </resources>

Some files were not shown because too many files have changed in this diff Show More