mirror of
https://github.com/f-droid/fdroidclient.git
synced 2026-05-19 05:55:04 -04:00
Merge branch 'compileSdk35' into 'master'
Compile with SDK 35 (Android 15) Closes #2607 See merge request fdroid/fdroidclient!1452
This commit is contained in:
@@ -1,2 +1,26 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{kt, kts}]
|
||||
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||
# Disable wildcard imports entirely
|
||||
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||
ij_kotlin_packages_to_use_import_on_demand = unset
|
||||
max_line_length = 100
|
||||
ktlint_code_style = android_studio
|
||||
ktlint_standard = enabled
|
||||
ktlint_experimental = disabled
|
||||
ktlint_standard_wrapping = disabled
|
||||
ktlint_standard_argument-list-wrapping = disabled
|
||||
ktlint_standard_import-ordering = disabled
|
||||
ktlint_standard_multiline-if-else = disabled
|
||||
ktlint_standard_trailing-comma-on-call-site = disabled
|
||||
ktlint_standard_trailing-comma-on-declaration-site = disabled
|
||||
ktlint_standard_no-blank-line-before-rbrace = disabled
|
||||
ktlint_standard_function-expression-body = disabled
|
||||
ktlint_standard_class-signature = disabled
|
||||
ktlint_standard_function-naming = disabled # for compose only
|
||||
ktlint_standard_function-signature = disabled
|
||||
@@ -32,7 +32,7 @@ workflow:
|
||||
- test -e $cmdline_tools_latest && export PATH="$cmdline_tools_latest:$PATH"
|
||||
|
||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdk\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
||||
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdk = "\([0-9][0-9]*\)".*,\1,p' gradle/libs.versions.toml`
|
||||
- echo y | sdkmanager "platforms;android-${ANDROID_COMPILE_SDK}" > /dev/null
|
||||
# index-v1.jar tests need SHA1 support still, TODO use apksig to validate JAR sigs
|
||||
- sed -i 's,SHA1 denyAfter 20[0-9][0-9],SHA1 denyAfter 2026,'
|
||||
@@ -218,8 +218,9 @@ libs database schema:
|
||||
- apt-get update
|
||||
- apt-get -qy --no-install-recommends install openjdk-17-jdk-headless git sdkmanager
|
||||
- export ANDROID_HOME=/opt/android-sdk
|
||||
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdk\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
||||
- export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdk = "\([0-9][0-9]*\)".*,\1,p' gradle/libs.versions.toml`
|
||||
- sdkmanager "platforms;android-$ANDROID_COMPILE_SDK" "build-tools;$ANDROID_COMPILE_SDK.0.0"
|
||||
- sdkmanager "build-tools;34.0.0" # something (AGP?) still pulls in old build-tools
|
||||
- ./gradlew :libs:database:kaptDebugKotlin
|
||||
- git --no-pager diff --exit-code
|
||||
|
||||
|
||||
130
app/build.gradle
130
app/build.gradle
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
|
||||
id 'org.jetbrains.kotlin.plugin.compose'
|
||||
}
|
||||
|
||||
// add -Pstrict.release to the gradle command line to enable
|
||||
@@ -27,9 +27,9 @@ def privilegedExtensionApplicationId = '"org.fdroid.fdroid.privileged"'
|
||||
|
||||
android {
|
||||
namespace "org.fdroid.fdroid"
|
||||
buildToolsVersion "34.0.0"
|
||||
buildToolsVersion "35.0.0"
|
||||
|
||||
compileSdk 34
|
||||
compileSdk libs.versions.compileSdk.get().toInteger()
|
||||
|
||||
defaultConfig {
|
||||
versionCode 1021050
|
||||
@@ -112,10 +112,6 @@ android {
|
||||
aidl true
|
||||
}
|
||||
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.9"
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests {
|
||||
includeAndroidResources = true
|
||||
@@ -173,75 +169,73 @@ dependencies {
|
||||
implementation project(":libs:download")
|
||||
implementation project(":libs:index")
|
||||
implementation project(":libs:database")
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation 'androidx.cardview:cardview:1.0.0'
|
||||
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
|
||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
||||
implementation 'androidx.work:work-runtime:2.9.0'
|
||||
implementation 'com.google.guava:guava:31.0-android' // somehow needed for work-runtime to function
|
||||
implementation libs.androidx.appcompat
|
||||
implementation libs.androidx.preference.ktx
|
||||
implementation libs.androidx.gridlayout
|
||||
implementation libs.androidx.recyclerview
|
||||
implementation libs.androidx.cardview
|
||||
implementation libs.androidx.vectordrawable
|
||||
implementation libs.androidx.constraintlayout
|
||||
implementation libs.androidx.lifecycle.livedata.ktx
|
||||
implementation libs.androidx.palette.ktx
|
||||
implementation libs.androidx.work.runtime
|
||||
implementation libs.guava // somehow needed for work-runtime to function
|
||||
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
implementation libs.material
|
||||
|
||||
//noinspection UseTomlInstead
|
||||
implementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false }
|
||||
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24 or library desugering
|
||||
implementation 'info.guardianproject.netcipher:netcipher:2.2.0-alpha'
|
||||
//noinspection GradleDependency -> Commons IO > 2.5 uses java.nio.file, which requires desugaring
|
||||
implementation 'commons-io:commons-io:2.6'
|
||||
implementation 'commons-net:commons-net:3.6'
|
||||
implementation 'ch.acra:acra-mail:5.11.3'
|
||||
implementation 'ch.acra:acra-dialog:5.11.3'
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4:4.3.2'
|
||||
implementation 'org.slf4j:slf4j-api:2.0.7'
|
||||
implementation 'com.github.tony19:logback-android:3.0.0'
|
||||
implementation libs.zxing.core
|
||||
implementation libs.guardianproject.netcipher
|
||||
implementation libs.commons.io
|
||||
implementation libs.commons.net
|
||||
implementation libs.acra.mail
|
||||
implementation libs.acra.dialog
|
||||
implementation libs.adapterdelegates4
|
||||
implementation libs.slf4j.api
|
||||
implementation libs.logback.android
|
||||
|
||||
implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'
|
||||
implementation 'io.reactivex.rxjava3:rxjava:3.1.7'
|
||||
implementation libs.rxjava
|
||||
implementation libs.rxandroid
|
||||
|
||||
implementation "com.github.bumptech.glide:glide:4.16.0"
|
||||
implementation("com.github.bumptech.glide:compose:1.0.0-alpha.3") {
|
||||
exclude group: "androidx.test"
|
||||
}
|
||||
annotationProcessor "com.github.bumptech.glide:compiler:4.14.2"
|
||||
implementation libs.glide
|
||||
implementation(libs.glide.compose)
|
||||
annotationProcessor libs.glide.compiler
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
implementation 'org.bouncycastle:bcprov-jdk15to18:1.71'
|
||||
fullImplementation 'info.guardianproject.panic:panic:1.0'
|
||||
fullImplementation 'org.bouncycastle:bcpkix-jdk15to18:1.71'
|
||||
fullImplementation 'org.jmdns:jmdns:3.5.5'
|
||||
fullImplementation 'org.nanohttpd:nanohttpd:2.3.1'
|
||||
implementation libs.okhttp
|
||||
implementation libs.bcprov.jdk15to18
|
||||
fullImplementation libs.guardianproject.panic
|
||||
fullImplementation libs.bcpkix.jdk15to18
|
||||
fullImplementation libs.jmdns
|
||||
fullImplementation libs.nanohttpd
|
||||
|
||||
// newer compose-bom versions have an issue with app details repo dropdown
|
||||
implementation platform('androidx.compose:compose-bom:2023.10.01')
|
||||
implementation 'androidx.compose.material:material'
|
||||
implementation 'androidx.compose.material:material-icons-extended'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose"
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'androidx.activity:activity-compose:1.8.2'
|
||||
implementation "com.google.accompanist:accompanist-themeadapter-material:0.30.1"
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||
implementation platform(libs.androidx.compose.bom)
|
||||
implementation libs.androidx.compose.material
|
||||
implementation libs.androidx.compose.material.icons.extended
|
||||
implementation libs.androidx.lifecycle.viewmodel.compose
|
||||
implementation libs.androidx.compose.ui.tooling.preview
|
||||
implementation libs.androidx.activity.compose
|
||||
implementation libs.accompanist.themeadapter.material
|
||||
implementation libs.accompanist.drawablepainter
|
||||
debugImplementation libs.androidx.compose.ui.tooling
|
||||
|
||||
testImplementation 'androidx.test:core:1.5.0'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.robolectric:robolectric:4.11.1'
|
||||
testImplementation 'org.mockito:mockito-core:3.9.0'
|
||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||
testImplementation libs.androidx.test.core
|
||||
testImplementation libs.junit
|
||||
testImplementation libs.robolectric
|
||||
testImplementation libs.mockito.core
|
||||
testImplementation libs.hamcrest
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||
androidTestImplementation 'androidx.arch.core:core-testing:2.2.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test:monitor:1.6.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
||||
androidTestImplementation 'androidx.work:work-testing:2.9.0'
|
||||
androidTestImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
androidTestImplementation 'app.cash.turbine:turbine:1.0.0'
|
||||
androidTestImplementation libs.androidx.test.core
|
||||
androidTestImplementation libs.androidx.core.testing
|
||||
androidTestImplementation libs.androidx.test.runner
|
||||
androidTestImplementation libs.androidx.test.rules
|
||||
androidTestImplementation libs.androidx.test.ext.junit
|
||||
androidTestImplementation libs.androidx.test.monitor
|
||||
androidTestImplementation libs.androidx.espresso.core
|
||||
androidTestImplementation libs.androidx.test.uiautomator
|
||||
androidTestImplementation libs.androidx.work.testing
|
||||
androidTestImplementation libs.kotlin.test
|
||||
androidTestImplementation libs.turbine
|
||||
}
|
||||
|
||||
// org.fdroid.fdroid.updater.UpdateServiceTest needs app-full-debug.apk
|
||||
@@ -255,5 +249,3 @@ android.productFlavors.all { flavor ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "${rootProject.rootDir}/gradle/ktlint.gradle"
|
||||
|
||||
@@ -166,5 +166,4 @@ internal class RepoManagerAddAllIntegrationTest {
|
||||
log.info(" final: $item")
|
||||
return item
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.net.wifi.WifiManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.fdroid.fdroid.BuildConfig;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
@@ -41,8 +43,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* WifiApControl provides control over Wi-Fi APs using the singleton pattern.
|
||||
* Even though isSupported should be reliable, the underlying hidden APIs that
|
||||
|
||||
@@ -35,8 +35,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.fdroid.fdroid.data.Apk;
|
||||
import org.fdroid.fdroid.data.DBHelper;
|
||||
import org.fdroid.fdroid.installer.PrivilegedInstaller;
|
||||
@@ -45,6 +43,7 @@ import org.fdroid.fdroid.net.ConnectivityMonitorService;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -606,7 +605,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
|
||||
HashMap<String, List<String>> output = new HashMap<String, List<String>>();
|
||||
for (String line : string.split("\n")) {
|
||||
String[] items = line.split(" ");
|
||||
List<String> list = Lists.newArrayList(items);
|
||||
ArrayList<String> list = new ArrayList<>(Arrays.asList(items));
|
||||
String key = list.remove(0);
|
||||
output.put(key, list);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,8 @@ object ComposeUtils {
|
||||
// set caption style to match MDC
|
||||
caption = it.caption.copy(
|
||||
color = colorResource(id = R.color.fdroid_caption),
|
||||
fontSize = 12.sp)
|
||||
fontSize = 12.sp,
|
||||
)
|
||||
)
|
||||
} ?: MaterialTheme.typography,
|
||||
shapes = shapes ?: MaterialTheme.shapes
|
||||
|
||||
@@ -51,7 +51,8 @@ class IpfsGatewayAddActivity : AppCompatActivity() {
|
||||
|
||||
setContent {
|
||||
FDroidContent {
|
||||
IpfsGatewayAddScreen(onBackClicked = { onBackPressedDispatcher.onBackPressed() },
|
||||
IpfsGatewayAddScreen(
|
||||
onBackClicked = { onBackPressedDispatcher.onBackPressed() },
|
||||
onAddUserGateway = { url ->
|
||||
// don't allow adding default gateways to the user gateways list
|
||||
if (!Preferences.DEFAULT_IPFS_GATEWAYS.contains(url)) {
|
||||
@@ -63,7 +64,8 @@ class IpfsGatewayAddActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
finish()
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,24 +80,24 @@ fun IpfsGatewayAddScreen(
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
var errorMsg by remember { mutableStateOf("") }
|
||||
|
||||
Scaffold(topBar = {
|
||||
TopAppBar(
|
||||
elevation = 4.dp,
|
||||
backgroundColor = MaterialTheme.colors.primarySurface,
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClicked) {
|
||||
Icon(Icons.Filled.ArrowBack, stringResource(R.string.back))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.ipfsgw_add_title),
|
||||
modifier = Modifier.alpha(ContentAlpha.high),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
elevation = 4.dp,
|
||||
backgroundColor = MaterialTheme.colors.primarySurface,
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClicked) {
|
||||
Icon(Icons.Filled.ArrowBack, stringResource(R.string.back))
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.ipfsgw_add_title),
|
||||
modifier = Modifier.alpha(ContentAlpha.high),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
|
||||
@@ -59,8 +59,10 @@ class IpfsGatewaySettingsActivity : AppCompatActivity() {
|
||||
|
||||
setContent {
|
||||
FDroidContent {
|
||||
IpfsGatewaySettingsScreen(prefs = prefs,
|
||||
onBackClicked = { onBackPressedDispatcher.onBackPressed() })
|
||||
IpfsGatewaySettingsScreen(
|
||||
prefs = prefs,
|
||||
onBackClicked = { onBackPressedDispatcher.onBackPressed() },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,13 +97,16 @@ fun IpfsGatewaySettingsScreen(
|
||||
floatingActionButton = {
|
||||
// it doesn't seam to be supported to disable FABs, so just hide it for now.
|
||||
if (ipfsEnabled) {
|
||||
FloatingActionButton(onClick = {
|
||||
context.startActivity(Intent(context, IpfsGatewayAddActivity::class.java))
|
||||
}) {
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
context.startActivity(Intent(context, IpfsGatewayAddActivity::class.java))
|
||||
},
|
||||
) {
|
||||
Icon(Icons.Filled.Add, stringResource(id = R.string.ipfsgw_add_add))
|
||||
}
|
||||
}
|
||||
}) { paddingValues ->
|
||||
},
|
||||
) { paddingValues ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
@@ -219,7 +224,9 @@ fun UserGatewaysSettings(
|
||||
|
||||
userGateways = newGateways
|
||||
prefs.ipfsGwUserList = newGateways
|
||||
}, enabled = ipfsEnabled, modifier = Modifier.align(Alignment.CenterVertically)
|
||||
},
|
||||
enabled = ipfsEnabled,
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.DeleteForever,
|
||||
@@ -234,7 +241,6 @@ fun UserGatewaysSettings(
|
||||
@Composable
|
||||
@Preview
|
||||
fun IpfsGatewaySettingsScreenPreview() {
|
||||
|
||||
val prefs = object : IPreferencesIpfs {
|
||||
override fun isIpfsEnabled(): Boolean = true
|
||||
override fun setIpfsEnabled(enabled: Boolean) = throw NotImplementedError()
|
||||
|
||||
@@ -121,14 +121,18 @@ fun RepoPreviewHeader(
|
||||
val buttonAction: () -> Unit = when (val res = state.fetchResult) {
|
||||
is IsNewRepository, is IsNewRepoAndNewMirror, is IsNewMirror -> onAddRepo
|
||||
// unfortunately we need to duplicate these functions
|
||||
is IsExistingRepository -> { ->
|
||||
val repoId = res.existingRepoId
|
||||
RepoDetailsActivity.launch(context, repoId)
|
||||
is IsExistingRepository -> {
|
||||
{
|
||||
val repoId = res.existingRepoId
|
||||
RepoDetailsActivity.launch(context, repoId)
|
||||
}
|
||||
}
|
||||
|
||||
is IsExistingMirror -> { ->
|
||||
val repoId = res.existingRepoId
|
||||
RepoDetailsActivity.launch(context, repoId)
|
||||
is IsExistingMirror -> {
|
||||
{
|
||||
val repoId = res.existingRepoId
|
||||
RepoDetailsActivity.launch(context, repoId)
|
||||
}
|
||||
}
|
||||
|
||||
else -> error("Unexpected fetch state: ${state.fetchResult}")
|
||||
|
||||
@@ -101,7 +101,9 @@ class RepoUpdateWorker(
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
UNIQUE_WORK_NAME_AUTO_UPDATE, UPDATE, workRequest
|
||||
UNIQUE_WORK_NAME_AUTO_UPDATE,
|
||||
UPDATE,
|
||||
workRequest,
|
||||
)
|
||||
} else {
|
||||
Log.w(TAG, "Not scheduling job due to settings!")
|
||||
|
||||
@@ -34,9 +34,9 @@ public class ApkTest {
|
||||
@Before
|
||||
public final void setUp() {
|
||||
ShadowMimeTypeMap mimeTypeMap = Shadows.shadowOf(MimeTypeMap.getSingleton());
|
||||
mimeTypeMap.addExtensionMimeTypMapping("apk", "application/vnd.android.package-archive");
|
||||
mimeTypeMap.addExtensionMimeTypMapping("obf", "application/octet-stream");
|
||||
mimeTypeMap.addExtensionMimeTypMapping("zip", PublicSourceDirProvider.SHARE_APK_MIME_TYPE);
|
||||
mimeTypeMap.addExtensionMimeTypeMapping("apk", "application/vnd.android.package-archive");
|
||||
mimeTypeMap.addExtensionMimeTypeMapping("obf", "application/octet-stream");
|
||||
mimeTypeMap.addExtensionMimeTypeMapping("zip", PublicSourceDirProvider.SHARE_APK_MIME_TYPE);
|
||||
ShadowLog.stream = System.out;
|
||||
}
|
||||
|
||||
|
||||
26
build.gradle
26
build.gradle
@@ -3,12 +3,16 @@ buildscript {
|
||||
mavenCentral()
|
||||
maven { url 'https://maven.google.com/' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.2.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22"
|
||||
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.9.10"
|
||||
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.18.0'
|
||||
}
|
||||
}
|
||||
plugins {
|
||||
alias libs.plugins.android.application apply false
|
||||
alias libs.plugins.jetbrains.kotlin.android apply false
|
||||
alias libs.plugins.jetbrains.kotlin.multiplatform apply false
|
||||
alias libs.plugins.jetbrains.kotlin.plugin.serialization apply false
|
||||
alias libs.plugins.jetbrains.compose.compiler apply false
|
||||
alias libs.plugins.jetbrains.dokka apply false
|
||||
alias libs.plugins.jlleitschuh.ktlint apply false
|
||||
alias libs.plugins.vanniktech.maven.publish apply false
|
||||
}
|
||||
allprojects {
|
||||
repositories {
|
||||
@@ -22,3 +26,13 @@ allprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
subprojects {
|
||||
apply plugin: "org.jlleitschuh.gradle.ktlint"
|
||||
|
||||
ktlint {
|
||||
version = "1.3.1"
|
||||
android = true
|
||||
enableExperimentalRules = false
|
||||
verbose = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
ktlint {
|
||||
version = "0.45.2"
|
||||
android = true
|
||||
enableExperimentalRules = false
|
||||
verbose = true
|
||||
disabledRules = [
|
||||
"wrapping",
|
||||
"import-ordering",
|
||||
"no-blank-line-before-rbrace",
|
||||
]
|
||||
}
|
||||
171
gradle/libs.versions.toml
Normal file
171
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,171 @@
|
||||
[versions]
|
||||
compileSdk = "35"
|
||||
kotlin = "2.0.20"
|
||||
androidGradlePlugin = "8.6.0"
|
||||
dokka = "1.9.20"
|
||||
mavenPublish = "0.18.0"
|
||||
jlleitschuhKtlint = "12.1.1"
|
||||
|
||||
kotlinxSerializationJson = "1.4.1" # 1.4.1 because https://github.com/Kotlin/kotlinx.serialization/issues/2231
|
||||
kotlinxCoroutinesTest = "1.7.3"
|
||||
|
||||
ktor = "2.3.12"
|
||||
okhttp = "4.12.0"
|
||||
room = "2.6.1"
|
||||
glide = "4.16.0"
|
||||
glideCompose = "1.0.0-beta01"
|
||||
|
||||
androidxCoreKtx = "1.13.1"
|
||||
androidxAppcompat = "1.7.0"
|
||||
androidxPreferenceKtx = "1.2.1"
|
||||
androidxLifecycleLivedataKtx = "2.8.6"
|
||||
androidxWork = "2.9.1"
|
||||
androidxRecyclerview = "1.3.2"
|
||||
androidxConstraintlayout = "2.1.4"
|
||||
androidxCardview = "1.0.0"
|
||||
androidxPaletteKtx = "1.0.0"
|
||||
androidxVectordrawable = "1.2.0"
|
||||
androidxGridlayout = "1.0.0"
|
||||
androidxComposeBom = "2024.09.03"
|
||||
androidxActivityCompose = "1.9.2"
|
||||
accompanistThemeadapterMaterial = "0.30.1"
|
||||
accompanistDrawablepainter = "0.36.0"
|
||||
material = "1.12.0"
|
||||
|
||||
zxingCore = "3.3.3" # newer version need minSdk 24 or library desugering
|
||||
guardianprojectNetcipher = "2.2.0-alpha"
|
||||
guardianprojectPanic = "1.0"
|
||||
acra = "5.11.3"
|
||||
adapterdelegates4 = "4.3.2"
|
||||
#noinspection GradleDependency Commons IO > 2.5 uses java.nio.file, which requires desugaring
|
||||
commonsIo = "2.6"
|
||||
commonsNet = "3.6"
|
||||
bouncycastle = "1.71"
|
||||
jmdns = "3.5.5"
|
||||
nanohttpd = "2.3.1"
|
||||
guava = "32.1.3-android"
|
||||
|
||||
rxjava = "3.1.9"
|
||||
rxandroid = "3.0.2"
|
||||
|
||||
slf4jApi = "2.0.16"
|
||||
microutilsKotlinLogging = "2.1.21"
|
||||
logbackClassic = "1.5.6"
|
||||
logbackAndroid = "3.0.0"
|
||||
|
||||
junit = "4.13.2"
|
||||
mockk = "1.13.8"
|
||||
robolectric = "4.12.2"
|
||||
androidxTestCore = "1.6.1"
|
||||
androidxTestRunner = "1.6.2"
|
||||
androidxTestExtJunit = "1.2.1"
|
||||
androidxCoreTesting = "2.2.0"
|
||||
androidxTestCoreKtx = "1.6.1"
|
||||
androidxEspressoCore = "3.6.1"
|
||||
androidxTestRules = "1.6.1"
|
||||
androidxTestUiautomator = "2.3.0"
|
||||
androidxTestMonitor = "1.7.2"
|
||||
mockitoCore = "5.1.1"
|
||||
hamcrest = "2.2"
|
||||
goncalossilvaResources = "0.2.1"
|
||||
turbine = "1.0.0"
|
||||
json = "20220320"
|
||||
|
||||
[libraries]
|
||||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
|
||||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" }
|
||||
|
||||
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCoreKtx" }
|
||||
androidx-lifecycle-livedata-ktx = { module = "androidx.lifecycle:lifecycle-livedata-ktx", version.ref = "androidxLifecycleLivedataKtx" }
|
||||
androidx-cardview = { module = "androidx.cardview:cardview", version.ref = "androidxCardview" }
|
||||
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintlayout" }
|
||||
androidx-gridlayout = { module = "androidx.gridlayout:gridlayout", version.ref = "androidxGridlayout" }
|
||||
androidx-palette-ktx = { module = "androidx.palette:palette-ktx", version.ref = "androidxPaletteKtx" }
|
||||
androidx-preference-ktx = { module = "androidx.preference:preference-ktx", version.ref = "androidxPreferenceKtx" }
|
||||
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidxAppcompat" }
|
||||
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "androidxRecyclerview" }
|
||||
androidx-vectordrawable = { module = "androidx.vectordrawable:vectordrawable", version.ref = "androidxVectordrawable" }
|
||||
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "androidxWork" }
|
||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
|
||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" }
|
||||
androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
|
||||
androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
|
||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose" }
|
||||
androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
|
||||
androidx-compose-material = { module = "androidx.compose.material:material" }
|
||||
accompanist-themeadapter-material = { module = "com.google.accompanist:accompanist-themeadapter-material", version.ref = "accompanistThemeadapterMaterial" }
|
||||
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanistDrawablepainter" }
|
||||
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivityCompose" }
|
||||
|
||||
material = { module = "com.google.android.material:material", version.ref = "material" }
|
||||
|
||||
glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
|
||||
glide-compiler = { module = "com.github.bumptech.glide:compiler", version.ref = "glide" }
|
||||
glide-compose = { module = "com.github.bumptech.glide:compose", version.ref = "glideCompose" }
|
||||
glide-annotations = { module = "com.github.bumptech.glide:annotations", version.ref = "glide" }
|
||||
|
||||
guardianproject-netcipher = { module = "info.guardianproject.netcipher:netcipher", version.ref = "guardianprojectNetcipher" }
|
||||
guardianproject-panic = { module = "info.guardianproject.panic:panic", version.ref = "guardianprojectPanic" }
|
||||
nanohttpd = { module = "org.nanohttpd:nanohttpd", version.ref = "nanohttpd" }
|
||||
jmdns = { module = "org.jmdns:jmdns", version.ref = "jmdns" }
|
||||
zxing-core = { module = "com.google.zxing:core", version.ref = "zxingCore" }
|
||||
acra-mail = { module = "ch.acra:acra-mail", version.ref = "acra" }
|
||||
acra-dialog = { module = "ch.acra:acra-dialog", version.ref = "acra" }
|
||||
adapterdelegates4 = { module = "com.hannesdorfmann:adapterdelegates4", version.ref = "adapterdelegates4" }
|
||||
bcprov-jdk15to18 = { module = "org.bouncycastle:bcprov-jdk15to18", version.ref = "bouncycastle" }
|
||||
bcpkix-jdk15to18 = { module = "org.bouncycastle:bcpkix-jdk15to18", version.ref = "bouncycastle" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
|
||||
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logbackClassic" }
|
||||
logback-android = { module = "com.github.tony19:logback-android", version.ref = "logbackAndroid" }
|
||||
microutils-kotlin-logging = { module = "io.github.microutils:kotlin-logging", version.ref = "microutilsKotlinLogging" }
|
||||
|
||||
commons-io = { module = "commons-io:commons-io", version.ref = "commonsIo" }
|
||||
commons-net = { module = "commons-net:commons-net", version.ref = "commonsNet" }
|
||||
ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" }
|
||||
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
|
||||
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||
ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
|
||||
ktor-client-curl = { module = "io.ktor:ktor-client-curl", version.ref = "ktor" }
|
||||
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||
goncalossilva-resources = { module = "com.goncalossilva:resources", version.ref = "goncalossilvaResources" }
|
||||
|
||||
rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" }
|
||||
rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
|
||||
|
||||
junit = { module = "junit:junit", version.ref = "junit" }
|
||||
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
|
||||
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
|
||||
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
|
||||
androidx-test-core = { module = "androidx.test:core", version.ref = "androidxTestCore" }
|
||||
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidxTestRunner" }
|
||||
androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidxTestExtJunit" }
|
||||
androidx-test-core-ktx = { module = "androidx.test:core-ktx", version.ref = "androidxTestCoreKtx" }
|
||||
androidx-core-testing = { module = "androidx.arch.core:core-testing", version.ref = "androidxCoreTesting" }
|
||||
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidxEspressoCore" }
|
||||
androidx-test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version.ref = "androidxTestUiautomator" }
|
||||
androidx-test-monitor = { module = "androidx.test:monitor", version.ref = "androidxTestMonitor" }
|
||||
androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidxTestRules" }
|
||||
androidx-room-testing = { module = "androidx.room:room-testing", version.ref = "room" }
|
||||
androidx-work-testing = { module = "androidx.work:work-testing", version.ref = "androidxWork" }
|
||||
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4jApi" }
|
||||
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
|
||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||
mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" }
|
||||
json = { module = "org.json:json", version.ref = "json" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
|
||||
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
jetbrains-kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
jetbrains-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
jetbrains-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" }
|
||||
vanniktech-maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
|
||||
jlleitschuh-ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "jlleitschuhKtlint" }
|
||||
File diff suppressed because it is too large
Load Diff
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=c16d517b50dd28b3f5838f0e844b7520b8f1eb610f2f29de7e4e04a1b7c9c79b
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
|
||||
distributionSha256Sum=2ab88d6de2c23e6adae7363ae6e29cbdd2a709e992929b48b6530fd0c7133bd6
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -3,17 +3,16 @@ plugins {
|
||||
id 'com.android.library'
|
||||
id 'kotlin-kapt'
|
||||
id 'org.jetbrains.dokka'
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
|
||||
id 'com.vanniktech.maven.publish'
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "org.fdroid.database"
|
||||
compileSdk 34
|
||||
compileSdk libs.versions.compileSdk.get().toInteger()
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdk 33 // relevant for instrumentation tests (targetSdk 21 fails on Android 14)
|
||||
targetSdk 34 // relevant for instrumentation tests (targetSdk 21 fails on Android 14)
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
javaCompileOptions {
|
||||
@@ -57,6 +56,9 @@ android {
|
||||
freeCompilerArgs += "-Xexplicit-api=strict"
|
||||
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
|
||||
}
|
||||
kapt {
|
||||
correctErrorTypes true
|
||||
}
|
||||
aaptOptions {
|
||||
// needed only for instrumentation tests: assets.openFd()
|
||||
noCompress "json"
|
||||
@@ -73,41 +75,39 @@ dependencies {
|
||||
implementation project(":libs:download")
|
||||
implementation project(":libs:index")
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
|
||||
implementation libs.androidx.core.ktx
|
||||
implementation libs.androidx.lifecycle.livedata.ktx
|
||||
|
||||
implementation "androidx.room:room-runtime:2.6.1"
|
||||
implementation "androidx.room:room-ktx:2.6.1"
|
||||
kapt "androidx.room:room-compiler:2.6.1"
|
||||
implementation libs.androidx.room.runtime
|
||||
implementation libs.androidx.room.ktx
|
||||
kapt libs.androidx.room.compiler
|
||||
|
||||
implementation 'io.github.microutils:kotlin-logging:2.1.21'
|
||||
|
||||
// 1.4.1 because https://github.com/Kotlin/kotlinx.serialization/issues/2231
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||
implementation libs.microutils.kotlin.logging
|
||||
implementation libs.kotlinx.serialization.json
|
||||
|
||||
testImplementation project(":libs:sharedTest")
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'io.mockk:mockk:1.13.8'
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
testImplementation 'androidx.test:core:1.5.0'
|
||||
testImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
testImplementation 'androidx.arch.core:core-testing:2.2.0'
|
||||
testImplementation "androidx.room:room-testing:2.6.1"
|
||||
testImplementation 'org.robolectric:robolectric:4.11.1'
|
||||
testImplementation 'commons-io:commons-io:2.6'
|
||||
testImplementation 'ch.qos.logback:logback-classic:1.4.5'
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
|
||||
testImplementation 'app.cash.turbine:turbine:1.0.0'
|
||||
testImplementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
testImplementation libs.junit
|
||||
testImplementation libs.mockk
|
||||
testImplementation libs.kotlin.test
|
||||
testImplementation libs.androidx.test.core.ktx
|
||||
testImplementation libs.androidx.test.ext.junit
|
||||
testImplementation libs.androidx.core.testing
|
||||
testImplementation libs.androidx.room.testing
|
||||
testImplementation libs.robolectric
|
||||
testImplementation libs.commons.io
|
||||
testImplementation libs.logback.classic
|
||||
testImplementation libs.kotlinx.coroutines.test
|
||||
testImplementation libs.turbine
|
||||
testImplementation libs.okhttp
|
||||
|
||||
androidTestImplementation project(":libs:sharedTest")
|
||||
androidTestImplementation 'io.mockk:mockk-android:1.13.8'
|
||||
androidTestImplementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.arch.core:core-testing:2.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation "androidx.room:room-testing:2.6.1"
|
||||
androidTestImplementation 'commons-io:commons-io:2.6'
|
||||
androidTestImplementation libs.mockk.android
|
||||
androidTestImplementation libs.kotlin.test
|
||||
androidTestImplementation libs.androidx.test.ext.junit
|
||||
androidTestImplementation libs.androidx.core.testing
|
||||
androidTestImplementation libs.androidx.espresso.core
|
||||
androidTestImplementation libs.androidx.room.testing
|
||||
androidTestImplementation libs.commons.io
|
||||
}
|
||||
|
||||
plugins.withId("kotlin-kapt") {
|
||||
@@ -127,5 +127,3 @@ tasks.withType(DokkaTask).configureEach {
|
||||
}"""]
|
||||
)
|
||||
}
|
||||
|
||||
apply from: "${rootProject.rootDir}/gradle/ktlint.gradle"
|
||||
|
||||
@@ -14,6 +14,7 @@ internal abstract class AppTest : DbTest() {
|
||||
protected val name1 = mapOf("en-US" to "1")
|
||||
protected val name2 = mapOf("en-US" to "2")
|
||||
protected val name3 = mapOf("en-US" to "3")
|
||||
|
||||
// it is important for testing that the icons are sharing at least one locale
|
||||
protected val icons1 = mapOf("en-US" to getRandomFileV2(), "bar" to getRandomFileV2())
|
||||
protected val icons2 = mapOf("en-US" to getRandomFileV2(), "42" to getRandomFileV2())
|
||||
|
||||
@@ -32,7 +32,7 @@ internal class DbUpdateCheckerTest : AppTest() {
|
||||
private val compatChecker: (PackageVersionV2) -> Boolean = { true }
|
||||
|
||||
private val packageInfo = PackageInfo().apply {
|
||||
packageName = TestDataMinV2.packageName
|
||||
packageName = TestDataMinV2.PACKAGE_NAME
|
||||
versionCode = 0
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ internal class DbUpdateCheckerTest : AppTest() {
|
||||
val appVersions = updateChecker.getUpdatableApps()
|
||||
assertEquals(1, appVersions.size)
|
||||
assertEquals(0, appVersions[0].installedVersionCode)
|
||||
assertEquals(TestDataMinV2.packageName, appVersions[0].packageName)
|
||||
assertEquals(TestDataMinV2.PACKAGE_NAME, appVersions[0].packageName)
|
||||
assertEquals(TestDataMinV2.version.file.sha256, appVersions[0].update.version.versionId)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,10 @@ internal class FtsCaseInsensitiveMigrationTest {
|
||||
|
||||
private val repo = ContentValues().apply {
|
||||
put("repoId", 1)
|
||||
put("name", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "a", "en-US" to "b")))
|
||||
put("name", Converters.localizedTextV2toString(mapOf("de" to "a", "en-US" to "b")))
|
||||
put("address", getRandomString())
|
||||
put("certificate", "abcdef")
|
||||
put("description", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "aa", "en-US" to "bb")))
|
||||
put("description", Converters.localizedTextV2toString(mapOf("de" to "aa", "en-US" to "bb")))
|
||||
put("version", Random.nextLong())
|
||||
put("timestamp", Random.nextLong())
|
||||
}
|
||||
@@ -59,14 +57,29 @@ internal class FtsCaseInsensitiveMigrationTest {
|
||||
private val oeffiMetadata = ContentValues().apply {
|
||||
put("packageName", "de.schildbach.oeffi")
|
||||
put("repoId", 1)
|
||||
put("name", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "Öffi", "en-US" to "Offi")))
|
||||
put("description", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "Öffentlicher Nahverkehr", "en-US" to "Public Transport")))
|
||||
put(
|
||||
"name", Converters.localizedTextV2toString(
|
||||
mapOf(
|
||||
"de" to "Öffi", "en-US" to "Offi"
|
||||
)
|
||||
)
|
||||
)
|
||||
put(
|
||||
"description", Converters.localizedTextV2toString(
|
||||
mapOf(
|
||||
"de" to "Öffentlicher Nahverkehr", "en-US" to "Public Transport"
|
||||
)
|
||||
)
|
||||
)
|
||||
put("license", "GPL-3.0")
|
||||
put("summary", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "Der König des Fahrplandschungels!",
|
||||
"en-US" to " King of public transit planning!")))
|
||||
put(
|
||||
"summary", Converters.localizedTextV2toString(
|
||||
mapOf(
|
||||
"de" to "Der König des Fahrplandschungels!",
|
||||
"en-US" to " King of public transit planning!"
|
||||
)
|
||||
)
|
||||
)
|
||||
put("localizedName", "Öffi")
|
||||
put("localizedSummary", "Der König des Fahrplandschungels!")
|
||||
put("added", Random.nextLong())
|
||||
@@ -89,17 +102,34 @@ internal class FtsCaseInsensitiveMigrationTest {
|
||||
private val transportrMetadata = ContentValues().apply {
|
||||
put("packageName", "de.grobox.liberario")
|
||||
put("repoId", 1)
|
||||
put("name", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "Transportr", "en-US" to "Transportr")))
|
||||
put("description", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "Öffentlicher Nahverkehr", "en-US" to "Public Transport")))
|
||||
put(
|
||||
"name", Converters.localizedTextV2toString(
|
||||
mapOf(
|
||||
"de" to "Transportr", "en-US" to "Transportr"
|
||||
)
|
||||
)
|
||||
)
|
||||
put(
|
||||
"description", Converters.localizedTextV2toString(
|
||||
mapOf(
|
||||
"de" to "Öffentlicher Nahverkehr", "en-US" to "Public Transport"
|
||||
)
|
||||
)
|
||||
)
|
||||
put("license", "GPL-3.0")
|
||||
put("summary", Converters.localizedTextV2toString(mapOf(
|
||||
"de" to "Freier Assistent für den öffentlichen Nahverkehr ohne Werbung und Tracking",
|
||||
"en-US" to "Free Public Transport Assistant without Ads or Tracking")))
|
||||
put(
|
||||
"summary", Converters.localizedTextV2toString(
|
||||
mapOf(
|
||||
"de" to "Freier Assistent für den öffentlichen Nahverkehr ohne Werbung",
|
||||
"en-US" to "Free Public Transport Assistant without Ads or Tracking"
|
||||
)
|
||||
)
|
||||
)
|
||||
put("localizedName", "Transportr")
|
||||
put("localizedSummary",
|
||||
"Freier Assistent für den öffentlichen Nahverkehr ohne Werbung und Tracking")
|
||||
put(
|
||||
"localizedSummary",
|
||||
"Freier Assistent für den öffentlichen Nahverkehr ohne Werbung und Tracking"
|
||||
)
|
||||
put("added", Random.nextLong())
|
||||
put("lastUpdated", Random.nextLong())
|
||||
put("isCompatible", true)
|
||||
@@ -119,7 +149,6 @@ internal class FtsCaseInsensitiveMigrationTest {
|
||||
|
||||
@Test
|
||||
fun testMigration() = runBlocking {
|
||||
|
||||
helper.createDatabase(TEST_DB, 5).use { db ->
|
||||
// Database has schema version 5. Insert some data using SQL queries.
|
||||
// We can't use DAO classes because they expect the latest schema.
|
||||
|
||||
@@ -282,8 +282,8 @@ internal class IndexV2DiffTest : DbTest() {
|
||||
diff = diffRepoIdJson,
|
||||
endIndex = TestDataMidV2.index.copy(
|
||||
packages = mapOf(
|
||||
TestDataMidV2.packageName1 to TestDataMidV2.app1,
|
||||
TestDataMidV2.packageName2 to fdroidPackage,
|
||||
TestDataMidV2.PACKAGE_NAME_1 to TestDataMidV2.app1,
|
||||
TestDataMidV2.PACKAGE_NAME_2 to fdroidPackage,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@@ -41,7 +41,8 @@ internal class RepoCertNonNullMigrationTest {
|
||||
put("address", "https://example.org/repo")
|
||||
put("certificate", "0123")
|
||||
put("timestamp", -1)
|
||||
})
|
||||
},
|
||||
)
|
||||
db.insert(
|
||||
RepositoryPreferences.TABLE,
|
||||
SQLiteDatabase.CONFLICT_FAIL,
|
||||
@@ -49,7 +50,8 @@ internal class RepoCertNonNullMigrationTest {
|
||||
put("repoId", repoId1)
|
||||
put("enabled", true)
|
||||
put("weight", Long.MAX_VALUE)
|
||||
})
|
||||
},
|
||||
)
|
||||
val repoId2 = db.insert(
|
||||
CoreRepository.TABLE,
|
||||
SQLiteDatabase.CONFLICT_FAIL,
|
||||
@@ -58,7 +60,8 @@ internal class RepoCertNonNullMigrationTest {
|
||||
put("description", localizedTextV2toString(mapOf("en-US" to "no cert desc")))
|
||||
put("address", "https://example.com/repo")
|
||||
put("timestamp", -1)
|
||||
})
|
||||
},
|
||||
)
|
||||
db.insert(
|
||||
RepositoryPreferences.TABLE,
|
||||
SQLiteDatabase.CONFLICT_FAIL,
|
||||
@@ -66,7 +69,8 @@ internal class RepoCertNonNullMigrationTest {
|
||||
put("repoId", repoId2)
|
||||
put("enabled", true)
|
||||
put("weight", Long.MAX_VALUE - 2)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Re-open the database with version 2, auto-migrations are applied automatically
|
||||
|
||||
@@ -186,12 +186,14 @@ public data class App internal constructor(
|
||||
localizedFileLists?.iterator()?.forEach { file ->
|
||||
if (file.repoId != metadata.repoId || file.type != type) return@forEach
|
||||
val list = map.getOrPut(file.locale) { ArrayList() } as ArrayList
|
||||
list.add(FileV2(
|
||||
name = file.name,
|
||||
sha256 = file.sha256,
|
||||
size = file.size,
|
||||
ipfsCidV1 = file.ipfsCidV1,
|
||||
))
|
||||
list.add(
|
||||
FileV2(
|
||||
name = file.name,
|
||||
sha256 = file.sha256,
|
||||
size = file.size,
|
||||
ipfsCidV1 = file.ipfsCidV1,
|
||||
)
|
||||
)
|
||||
}
|
||||
return map.ifEmpty { null }
|
||||
}
|
||||
@@ -418,8 +420,10 @@ internal fun List<IFile>.toLocalizedFileV2(): LocalizedFileV2? = associate { fil
|
||||
// We can't restrict this query further (e.g. only from enabled repos or max weight),
|
||||
// because we are using this via @Relation on packageName for specific repos.
|
||||
// When filtering the result for only the repoId we are interested in, we'd get no icons.
|
||||
@DatabaseView(viewName = LocalizedIcon.TABLE,
|
||||
value = "SELECT * FROM ${LocalizedFile.TABLE} WHERE type='icon'")
|
||||
@DatabaseView(
|
||||
viewName = LocalizedIcon.TABLE,
|
||||
value = "SELECT * FROM ${LocalizedFile.TABLE} WHERE type='icon'",
|
||||
)
|
||||
internal data class LocalizedIcon(
|
||||
val repoId: Long,
|
||||
val packageName: String,
|
||||
|
||||
@@ -130,7 +130,8 @@ public interface AppDao {
|
||||
}
|
||||
|
||||
public enum class AppListSortOrder {
|
||||
LAST_UPDATED, NAME
|
||||
LAST_UPDATED,
|
||||
NAME,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,7 +32,7 @@ public object FDroidDatabaseHolder {
|
||||
// Singleton prevents multiple instances of database opening at the same time.
|
||||
@Volatile
|
||||
@GuardedBy("lock")
|
||||
private var INSTANCE: FDroidDatabaseInt? = null
|
||||
private var instance: FDroidDatabaseInt? = null
|
||||
private val lock = Object()
|
||||
|
||||
internal val TAG = FDroidDatabase::class.simpleName
|
||||
@@ -52,7 +52,7 @@ public object FDroidDatabaseHolder {
|
||||
): FDroidDatabase {
|
||||
// if the INSTANCE is not null, then return it,
|
||||
// if it is, then create the database
|
||||
return INSTANCE ?: synchronized(lock) {
|
||||
return instance ?: synchronized(lock) {
|
||||
val builder = Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
FDroidDatabaseInt::class.java,
|
||||
@@ -66,7 +66,7 @@ public object FDroidDatabaseHolder {
|
||||
if (fixture != null) addCallback(FixtureCallback(fixture))
|
||||
}
|
||||
val instance = builder.build()
|
||||
INSTANCE = instance
|
||||
this.instance = instance
|
||||
// return instance
|
||||
instance
|
||||
}
|
||||
@@ -79,7 +79,7 @@ public object FDroidDatabaseHolder {
|
||||
GlobalScope.launch(dispatcher) {
|
||||
val database: FDroidDatabase
|
||||
synchronized(lock) {
|
||||
database = INSTANCE ?: error("DB not yet initialized")
|
||||
database = instance ?: error("DB not yet initialized")
|
||||
}
|
||||
fixture.prePopulateDb(database)
|
||||
Log.d(TAG, "Loaded fixtures")
|
||||
|
||||
@@ -182,7 +182,8 @@ internal interface RepositoryDaoInt : RepositoryDao {
|
||||
address: String,
|
||||
username: String? = null,
|
||||
password: String? = null,
|
||||
certificate: String = "6789" // just used for testing
|
||||
// just used for testing
|
||||
certificate: String = "6789",
|
||||
): Long {
|
||||
val repo = CoreRepository(
|
||||
name = mapOf("en-US" to address),
|
||||
@@ -234,9 +235,11 @@ internal interface RepositoryDaoInt : RepositoryDao {
|
||||
* Returns a non-archive repository with the given [certificate], if it exists in the DB.
|
||||
*/
|
||||
@Transaction
|
||||
@Query("""SELECT * FROM ${CoreRepository.TABLE}
|
||||
@Query(
|
||||
"""SELECT * FROM ${CoreRepository.TABLE}
|
||||
WHERE certificate = :certificate AND address NOT LIKE "%/archive" COLLATE NOCASE
|
||||
LIMIT 1""")
|
||||
LIMIT 1"""
|
||||
)
|
||||
fun getRepository(certificate: String): Repository?
|
||||
|
||||
@Transaction
|
||||
@@ -261,9 +264,11 @@ internal interface RepositoryDaoInt : RepositoryDao {
|
||||
fun getRepositoryPreferences(repoId: Long): RepositoryPreferences?
|
||||
|
||||
@RewriteQueriesToDropUnusedColumns
|
||||
@Query("""SELECT * FROM ${Category.TABLE}
|
||||
@Query(
|
||||
"""SELECT * FROM ${Category.TABLE}
|
||||
JOIN ${RepositoryPreferences.TABLE} AS pref USING (repoId)
|
||||
WHERE pref.enabled = 1 GROUP BY id HAVING MAX(pref.weight)""")
|
||||
WHERE pref.enabled = 1 GROUP BY id HAVING MAX(pref.weight)"""
|
||||
)
|
||||
override fun getLiveCategories(): LiveData<List<Category>>
|
||||
|
||||
/**
|
||||
@@ -365,16 +370,22 @@ internal interface RepositoryDaoInt : RepositoryDao {
|
||||
@Query("UPDATE ${AppPrefs.TABLE} SET preferredRepoId = NULL WHERE preferredRepoId = :repoId")
|
||||
fun resetPreferredRepoInAppPrefs(repoId: Long)
|
||||
|
||||
@Query("""UPDATE ${RepositoryPreferences.TABLE} SET userMirrors = :mirrors
|
||||
WHERE repoId = :repoId""")
|
||||
@Query(
|
||||
"""UPDATE ${RepositoryPreferences.TABLE} SET userMirrors = :mirrors
|
||||
WHERE repoId = :repoId"""
|
||||
)
|
||||
override fun updateUserMirrors(repoId: Long, mirrors: List<String>)
|
||||
|
||||
@Query("""UPDATE ${RepositoryPreferences.TABLE} SET username = :username, password = :password
|
||||
WHERE repoId = :repoId""")
|
||||
@Query(
|
||||
"""UPDATE ${RepositoryPreferences.TABLE} SET username = :username, password = :password
|
||||
WHERE repoId = :repoId"""
|
||||
)
|
||||
override fun updateUsernameAndPassword(repoId: Long, username: String?, password: String?)
|
||||
|
||||
@Query("""UPDATE ${RepositoryPreferences.TABLE} SET disabledMirrors = :disabledMirrors
|
||||
WHERE repoId = :repoId""")
|
||||
@Query(
|
||||
"""UPDATE ${RepositoryPreferences.TABLE} SET disabledMirrors = :disabledMirrors
|
||||
WHERE repoId = :repoId"""
|
||||
)
|
||||
override fun updateDisabledMirrors(repoId: Long, disabledMirrors: List<String>)
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,9 +147,11 @@ internal fun ManifestV2.toManifest() = AppManifest(
|
||||
features = features.map { it.name },
|
||||
)
|
||||
|
||||
@DatabaseView(viewName = HighestVersion.TABLE,
|
||||
@DatabaseView(
|
||||
viewName = HighestVersion.TABLE,
|
||||
value = """SELECT repoId, packageName, antiFeatures FROM ${Version.TABLE}
|
||||
GROUP BY repoId, packageName HAVING MAX(manifest_versionCode)""")
|
||||
GROUP BY repoId, packageName HAVING MAX(manifest_versionCode)""",
|
||||
)
|
||||
internal class HighestVersion(
|
||||
val repoId: Long,
|
||||
val packageName: String,
|
||||
@@ -230,7 +232,9 @@ private fun <T> VersionedString.map(
|
||||
wantedType: VersionedStringType,
|
||||
factory: () -> T,
|
||||
): T? {
|
||||
return if (repoId != v.repoId || packageName != v.packageName || versionId != v.versionId ||
|
||||
return if (repoId != v.repoId ||
|
||||
packageName != v.packageName ||
|
||||
versionId != v.versionId ||
|
||||
type != wantedType
|
||||
) null
|
||||
else factory()
|
||||
|
||||
@@ -433,7 +433,8 @@ internal class RepoAdderTest {
|
||||
coEvery {
|
||||
httpManager.getDigestInputStream(match {
|
||||
it.indexFile.name == "../index-min-v2.json" &&
|
||||
it.mirrors.size == 1 && it.mirrors[0].baseUrl == urlTrimmed
|
||||
it.mirrors.size == 1 &&
|
||||
it.mirrors[0].baseUrl == urlTrimmed
|
||||
})
|
||||
} returns indexStream
|
||||
every {
|
||||
@@ -550,7 +551,8 @@ internal class RepoAdderTest {
|
||||
coEvery {
|
||||
httpManager.getDigestInputStream(match {
|
||||
it.indexFile.name == "../index-min-v2.json" &&
|
||||
it.mirrors.size == 1 && it.mirrors[0].baseUrl == repoAddress
|
||||
it.mirrors.size == 1 &&
|
||||
it.mirrors[0].baseUrl == repoAddress
|
||||
})
|
||||
} returns indexStream
|
||||
every {
|
||||
@@ -610,7 +612,8 @@ internal class RepoAdderTest {
|
||||
coEvery {
|
||||
httpManager.getDigestInputStream(match {
|
||||
it.indexFile.name == "/index-v2.json" &&
|
||||
it.mirrors.size == 1 && it.mirrors[0].baseUrl == "https://example.org/repo"
|
||||
it.mirrors.size == 1 &&
|
||||
it.mirrors[0].baseUrl == "https://example.org/repo"
|
||||
})
|
||||
} returns indexStream
|
||||
every {
|
||||
@@ -776,7 +779,8 @@ internal class RepoAdderTest {
|
||||
coEvery {
|
||||
httpManager.getDigestInputStream(match {
|
||||
it.indexFile.name == "../index-min-v2.json" &&
|
||||
it.mirrors.size == 1 && it.mirrors[0].baseUrl == urlTrimmed
|
||||
it.mirrors.size == 1 &&
|
||||
it.mirrors[0].baseUrl == urlTrimmed
|
||||
})
|
||||
} returns indexStream
|
||||
every {
|
||||
@@ -803,7 +807,8 @@ internal class RepoAdderTest {
|
||||
it.address == TestDataMinV2.repo.address &&
|
||||
it.formatVersion == IndexFormatVersion.TWO &&
|
||||
it.name.getBestLocale(localeList) == repoName &&
|
||||
it.username == username && it.password == password // this is the important bit
|
||||
it.username == username &&
|
||||
it.password == password // this is the important bit
|
||||
})
|
||||
} returns 42L
|
||||
every { repoDao.updateUserMirrors(42L, listOf(urlTrimmed)) } just Runs
|
||||
@@ -850,7 +855,8 @@ internal class RepoAdderTest {
|
||||
coEvery {
|
||||
httpManager.getDigestInputStream(match {
|
||||
it.indexFile.name == "../index-min-v2.json" &&
|
||||
it.mirrors.size == 1 && it.mirrors[0].baseUrl == urlTrimmed
|
||||
it.mirrors.size == 1 &&
|
||||
it.mirrors[0].baseUrl == urlTrimmed
|
||||
})
|
||||
} returns indexStream
|
||||
every {
|
||||
@@ -895,7 +901,7 @@ internal class RepoAdderTest {
|
||||
val state3 = awaitItem()
|
||||
assertIs<Fetching>(state3)
|
||||
assertEquals(TestDataMinV2.packages.size, state3.apps.size)
|
||||
assertEquals(TestDataMinV2.packageName, state3.apps[0].packageName)
|
||||
assertEquals(TestDataMinV2.PACKAGE_NAME, state3.apps[0].packageName)
|
||||
assertFalse(state3.done)
|
||||
|
||||
// final result
|
||||
|
||||
@@ -2,7 +2,6 @@ plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform'
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.dokka'
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
|
||||
id 'com.vanniktech.maven.publish'
|
||||
}
|
||||
|
||||
@@ -30,57 +29,56 @@ kotlin {
|
||||
}
|
||||
commonMain {
|
||||
dependencies {
|
||||
api "io.ktor:ktor-client-core:2.3.8"
|
||||
implementation 'io.github.microutils:kotlin-logging:2.1.21'
|
||||
api libs.ktor.client.core
|
||||
implementation libs.microutils.kotlin.logging
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation kotlin('test')
|
||||
implementation "io.ktor:ktor-client-mock:2.3.8"
|
||||
implementation libs.ktor.client.mock
|
||||
}
|
||||
}
|
||||
// JVM is disabled for now, because Android app is including it instead of Android library
|
||||
jvmMain {
|
||||
dependencies {
|
||||
implementation "io.ktor:ktor-client-cio:2.3.8"
|
||||
implementation libs.ktor.client.cio
|
||||
}
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation 'junit:junit:4.13.2'
|
||||
implementation libs.junit
|
||||
}
|
||||
}
|
||||
androidMain {
|
||||
// needed because of https://issuetracker.google.com/issues/231701341
|
||||
kotlin.srcDir("src/commonMain/kotlin")
|
||||
dependencies {
|
||||
implementation "io.ktor:ktor-client-okhttp:2.3.8"
|
||||
implementation libs.ktor.client.okhttp
|
||||
//noinspection UseTomlInstead
|
||||
implementation("com.github.bumptech.glide:glide:4.16.0") {
|
||||
transitive = false // we don't need all that it pulls in, just the basics
|
||||
}
|
||||
implementation "com.github.bumptech.glide:annotations:4.16.0"
|
||||
implementation libs.glide.annotations
|
||||
}
|
||||
}
|
||||
androidUnitTest {
|
||||
dependencies {
|
||||
implementation kotlin('test')
|
||||
implementation 'org.json:json:20220320'
|
||||
implementation 'junit:junit:4.13.2'
|
||||
implementation 'ch.qos.logback:logback-classic:1.4.5'
|
||||
implementation libs.json
|
||||
implementation libs.junit
|
||||
implementation libs.logback.classic
|
||||
}
|
||||
}
|
||||
androidInstrumentedTest {
|
||||
dependsOn(commonTest)
|
||||
dependencies {
|
||||
implementation project(":libs:sharedTest")
|
||||
implementation 'androidx.test:runner:1.5.2'
|
||||
implementation 'androidx.test.ext:junit:1.1.5'
|
||||
implementation libs.androidx.test.runner
|
||||
implementation libs.androidx.test.ext.junit
|
||||
}
|
||||
}
|
||||
nativeMain {
|
||||
dependencies {
|
||||
implementation "io.ktor:ktor-client-curl:2.3.8"
|
||||
implementation libs.ktor.client.curl
|
||||
}
|
||||
}
|
||||
nativeTest {
|
||||
@@ -91,7 +89,7 @@ kotlin {
|
||||
|
||||
android {
|
||||
namespace "org.fdroid.download"
|
||||
compileSdk 34
|
||||
compileSdk libs.versions.compileSdk.get().toInteger()
|
||||
sourceSets {
|
||||
main.manifest.srcFile('src/androidMain/AndroidManifest.xml')
|
||||
getByName("androidTest").java.srcDir(file("src/androidAndroidTest/kotlin"))
|
||||
@@ -131,5 +129,3 @@ tasks.withType(DokkaTask).configureEach {
|
||||
}"""]
|
||||
)
|
||||
}
|
||||
|
||||
apply from: "${rootProject.rootDir}/gradle/ktlint.gradle"
|
||||
|
||||
@@ -226,7 +226,3 @@ public abstract class Downloader constructor(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public fun interface BytesReceiver {
|
||||
public suspend fun receive(bytes: ByteArray, numTotalBytes: Long?)
|
||||
}
|
||||
|
||||
@@ -246,6 +246,10 @@ public open class HttpManager @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
public fun interface BytesReceiver {
|
||||
public suspend fun receive(bytes: ByteArray, numTotalBytes: Long?)
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown if we tried to resume a download, but the current mirror server does not offer resuming.
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization'
|
||||
id 'com.android.library'
|
||||
id 'org.jetbrains.dokka'
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
|
||||
id 'com.vanniktech.maven.publish'
|
||||
}
|
||||
|
||||
kotlin {
|
||||
android {
|
||||
androidTarget {
|
||||
compilations.configureEach {
|
||||
kotlinOptions.jvmTarget = '17'
|
||||
}
|
||||
@@ -33,19 +32,18 @@ kotlin {
|
||||
}
|
||||
commonMain {
|
||||
dependencies {
|
||||
// 1.4.1 because https://github.com/Kotlin/kotlinx.serialization/issues/2231
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||
implementation 'io.github.microutils:kotlin-logging:2.1.21'
|
||||
implementation libs.kotlinx.serialization.json
|
||||
implementation libs.microutils.kotlin.logging
|
||||
|
||||
implementation project(":libs:download")
|
||||
implementation "io.ktor:ktor-io:2.3.8"
|
||||
implementation libs.ktor.io
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation project(":libs:sharedTest")
|
||||
implementation kotlin('test')
|
||||
implementation "com.goncalossilva:resources:0.2.1"
|
||||
implementation libs.goncalossilva.resources
|
||||
}
|
||||
}
|
||||
// JVM is disabled for now, because Android app is including it instead of Android library
|
||||
@@ -55,27 +53,27 @@ kotlin {
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation 'junit:junit:4.13.2'
|
||||
implementation libs.junit
|
||||
}
|
||||
}
|
||||
androidMain {
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:1.9.22"
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation libs.kotlin.reflect
|
||||
implementation libs.androidx.core.ktx
|
||||
}
|
||||
}
|
||||
androidUnitTest {
|
||||
dependencies {
|
||||
implementation 'junit:junit:4.13.2'
|
||||
implementation 'io.mockk:mockk:1.13.8'
|
||||
implementation libs.junit
|
||||
implementation libs.mockk
|
||||
}
|
||||
}
|
||||
androidInstrumentedTest {
|
||||
dependencies {
|
||||
implementation project(":libs:sharedTest")
|
||||
implementation kotlin('test')
|
||||
implementation 'androidx.test:runner:1.5.2'
|
||||
implementation 'androidx.test.ext:junit:1.1.5'
|
||||
implementation libs.androidx.test.runner
|
||||
implementation libs.androidx.test.ext.junit
|
||||
}
|
||||
}
|
||||
nativeMain {
|
||||
@@ -90,7 +88,7 @@ kotlin {
|
||||
|
||||
android {
|
||||
namespace "org.fdroid.index"
|
||||
compileSdk 34
|
||||
compileSdk libs.versions.compileSdk.get().toInteger()
|
||||
sourceSets {
|
||||
main.manifest.srcFile('src/androidMain/AndroidManifest.xml')
|
||||
getByName("androidTest").java.srcDir(file("src/androidAndroidTest/kotlin"))
|
||||
@@ -124,5 +122,3 @@ tasks.withType(DokkaTask).configureEach {
|
||||
}"""]
|
||||
)
|
||||
}
|
||||
|
||||
apply from: "${rootProject.rootDir}/gradle/ktlint.gradle"
|
||||
|
||||
@@ -74,10 +74,12 @@ internal class BestLocaleTest {
|
||||
// handle stripped script (Hans/Hant)
|
||||
assertEquals(
|
||||
"zh-TW",
|
||||
getMap("en-US",
|
||||
getMap(
|
||||
"en-US",
|
||||
"zh-CN",
|
||||
"zh-HK",
|
||||
"zh-TW").getBestLocale(getLocaleList("zh-Hant-TW,zh-Hans-CN")),
|
||||
"zh-TW",
|
||||
).getBestLocale(getLocaleList("zh-Hant-TW,zh-Hans-CN")),
|
||||
)
|
||||
assertEquals(
|
||||
"zh-CN",
|
||||
|
||||
@@ -99,7 +99,8 @@ public class UpdateChecker(
|
||||
versions.iterator().forEach versions@{ version ->
|
||||
// if the installed version has a known vulnerability, we return it as well
|
||||
if (includeKnownVulnerabilities &&
|
||||
version.versionCode == installedVersionCode && version.hasKnownVulnerability
|
||||
version.versionCode == installedVersionCode &&
|
||||
version.hasKnownVulnerability
|
||||
) return version
|
||||
// if version code is not higher than installed skip package as list is sorted
|
||||
if (version.versionCode <= installedVersionCode) return null
|
||||
|
||||
@@ -87,10 +87,10 @@ public class IndexV1StreamProcessor(
|
||||
private fun deserializeRepo(decoder: JsonDecoder, index: Int) {
|
||||
require(index == descriptor.getElementIndex("repo"))
|
||||
val repo = decoder.decodeSerializableValue(RepoV1.serializer())
|
||||
if (lastTimestamp >= repo.timestamp) {
|
||||
throw OldIndexException(lastTimestamp == repo.timestamp,
|
||||
"Old repo ${repo.address} ${repo.timestamp}")
|
||||
}
|
||||
if (lastTimestamp >= repo.timestamp) throw OldIndexException(
|
||||
isSameTimestamp = lastTimestamp == repo.timestamp,
|
||||
msg = "Old repo ${repo.address} ${repo.timestamp}",
|
||||
)
|
||||
val repoV2 = repo.toRepoV2(
|
||||
locale = DEFAULT_LOCALE,
|
||||
antiFeatures = emptyMap(),
|
||||
@@ -165,7 +165,9 @@ public class IndexV1StreamProcessor(
|
||||
val packageIndex = compositeDecoder.decodeElementIndex(descriptor)
|
||||
if (packageIndex == DECODE_DONE) break
|
||||
val packageVersionV1 = decoder.decodeSerializableElement(
|
||||
descriptor, index + 1, PackageV1.serializer()
|
||||
descriptor = descriptor,
|
||||
index = index + 1,
|
||||
deserializer = PackageV1.serializer(),
|
||||
)
|
||||
val versionCode = packageVersionV1.versionCode ?: 0
|
||||
val suggestedVersionCode =
|
||||
|
||||
@@ -49,11 +49,13 @@ public class IndexV2DiffStreamProcessor(
|
||||
val index = decoder.decodeElementIndex(descriptor)
|
||||
if (index == packagesIndex) diffPackages(decoder, index)
|
||||
}
|
||||
|
||||
packagesIndex -> {
|
||||
diffPackages(decoder, startIndex)
|
||||
val index = decoder.decodeElementIndex(descriptor)
|
||||
if (index == repoIndex) diffRepo(version, decoder, index)
|
||||
}
|
||||
|
||||
else -> error("Unexpected startIndex: $startIndex")
|
||||
}
|
||||
var currentIndex = 0
|
||||
@@ -89,7 +91,9 @@ public class IndexV2DiffStreamProcessor(
|
||||
val packageName = decoder.decodeStringElement(descriptor, index)
|
||||
decoder.decodeElementIndex(descriptor)
|
||||
val packageV2 = decoder.decodeSerializableElement(
|
||||
descriptor, index + 1, JsonElement.serializer()
|
||||
descriptor = descriptor,
|
||||
index = index + 1,
|
||||
deserializer = JsonElement.serializer(),
|
||||
)
|
||||
if (packageV2 is JsonNull) {
|
||||
// delete app and existing metadata
|
||||
|
||||
@@ -88,7 +88,9 @@ public class IndexV2FullStreamProcessor(
|
||||
val packageName = decoder.decodeStringElement(descriptor, index)
|
||||
decoder.decodeElementIndex(descriptor)
|
||||
val packageV2 = decoder.decodeSerializableElement(
|
||||
descriptor, index + 1, PackageV2.serializer()
|
||||
descriptor = descriptor,
|
||||
index = index + 1,
|
||||
deserializer = PackageV2.serializer(),
|
||||
)
|
||||
indexStreamReceiver.receive(packageName, packageV2)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.fdroid.index.v1
|
||||
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.fdroid.index.v2.AntiFeatureV2
|
||||
import org.fdroid.index.v2.CategoryV2
|
||||
import org.fdroid.index.v2.IndexV2
|
||||
@@ -29,34 +29,43 @@ internal class IndexV1StreamProcessorTest {
|
||||
|
||||
@Test
|
||||
fun testEmpty() {
|
||||
testStreamProcessing("$assetPath/index-empty-v1.json", TestDataEmptyV2.index.v1compat())
|
||||
testStreamProcessing("$ASSET_PATH/index-empty-v1.json", TestDataEmptyV2.index.v1compat())
|
||||
}
|
||||
|
||||
@Test(expected = OldIndexException::class)
|
||||
fun testEmptyEqualTimestamp() {
|
||||
testStreamProcessing("$assetPath/index-empty-v1.json",
|
||||
TestDataEmptyV2.index.v1compat(), TestDataEmptyV2.index.repo.timestamp)
|
||||
testStreamProcessing(
|
||||
"$ASSET_PATH/index-empty-v1.json",
|
||||
TestDataEmptyV2.index.v1compat(),
|
||||
TestDataEmptyV2.index.repo.timestamp,
|
||||
)
|
||||
}
|
||||
|
||||
@Test(expected = OldIndexException::class)
|
||||
fun testEmptyHigherTimestamp() {
|
||||
testStreamProcessing("$assetPath/index-empty-v1.json",
|
||||
TestDataEmptyV2.index.v1compat(), TestDataEmptyV2.index.repo.timestamp + 1)
|
||||
testStreamProcessing(
|
||||
"$ASSET_PATH/index-empty-v1.json",
|
||||
TestDataEmptyV2.index.v1compat(),
|
||||
TestDataEmptyV2.index.repo.timestamp + 1,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMin() {
|
||||
testStreamProcessing("$assetPath/index-min-v1.json", TestDataMinV2.index.v1compat())
|
||||
testStreamProcessing(
|
||||
"$ASSET_PATH/index-min-v1.json",
|
||||
TestDataMinV2.index.v1compat(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMid() {
|
||||
testStreamProcessing("$assetPath/index-mid-v1.json", TestDataMidV2.indexCompat)
|
||||
testStreamProcessing("$ASSET_PATH/index-mid-v1.json", TestDataMidV2.indexCompat)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMax() {
|
||||
testStreamProcessing("$assetPath/index-max-v1.json", TestDataMaxV2.indexCompat)
|
||||
testStreamProcessing("$ASSET_PATH/index-max-v1.json", TestDataMaxV2.indexCompat)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,16 +82,20 @@ internal class IndexV1StreamProcessorTest {
|
||||
|
||||
// empty repo dict
|
||||
assertFailsWith<SerializationException> {
|
||||
testStreamError("""{
|
||||
testStreamError(
|
||||
"""{
|
||||
"repo": {}
|
||||
}""".trimIndent())
|
||||
}""".trimIndent()
|
||||
)
|
||||
}.also { assertContains(it.message!!, "timestamp") }
|
||||
|
||||
// timestamp not a number
|
||||
assertFailsWith<SerializationException> {
|
||||
testStreamError("""{
|
||||
testStreamError(
|
||||
"""{
|
||||
"repo": { "timestamp": "string" }
|
||||
}""".trimIndent())
|
||||
}""".trimIndent()
|
||||
)
|
||||
}.also { assertContains(it.message!!, "numeric literal") }
|
||||
|
||||
// remember valid repo for further tests
|
||||
@@ -99,21 +112,25 @@ internal class IndexV1StreamProcessorTest {
|
||||
|
||||
// apps is dict
|
||||
assertFailsWith<SerializationException> {
|
||||
testStreamError("""{
|
||||
testStreamError(
|
||||
"""{
|
||||
$validRepo,
|
||||
"requests": {"install": [], "uninstall": []},
|
||||
"apps": {}
|
||||
}""".trimIndent())
|
||||
}""".trimIndent()
|
||||
)
|
||||
}.also { assertContains(it.message!!, "apps") }
|
||||
|
||||
// packages is list
|
||||
assertFailsWith<SerializationException> {
|
||||
testStreamError("""{
|
||||
testStreamError(
|
||||
"""{
|
||||
$validRepo,
|
||||
"requests": {"install": [], "uninstall": []},
|
||||
"apps": [],
|
||||
"packages": []
|
||||
}""".trimIndent())
|
||||
}""".trimIndent()
|
||||
)
|
||||
}.also { assertContains(it.message!!, "packages") }
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.fdroid.index.v2
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.fdroid.test.TestDataEmptyV2
|
||||
import org.fdroid.test.TestDataMaxV2
|
||||
import org.fdroid.test.TestDataMidV2
|
||||
@@ -27,27 +27,27 @@ internal class IndexV2FullStreamProcessorTest {
|
||||
|
||||
@Test
|
||||
fun testEmpty() {
|
||||
testStreamProcessing("$assetPath/index-empty-v2.json", TestDataEmptyV2.index, 0)
|
||||
testStreamProcessing("$ASSET_PATH/index-empty-v2.json", TestDataEmptyV2.index, 0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMin() {
|
||||
testStreamProcessing("$assetPath/index-min-v2.json", TestDataMinV2.index, 1)
|
||||
testStreamProcessing("$ASSET_PATH/index-min-v2.json", TestDataMinV2.index, 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMinReordered() {
|
||||
testStreamProcessing("$assetPath/index-min-reordered-v2.json", TestDataMinV2.index, 1)
|
||||
testStreamProcessing("$ASSET_PATH/index-min-reordered-v2.json", TestDataMinV2.index, 1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMid() {
|
||||
testStreamProcessing("$assetPath/index-mid-v2.json", TestDataMidV2.index, 2)
|
||||
testStreamProcessing("$ASSET_PATH/index-mid-v2.json", TestDataMidV2.index, 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMax() {
|
||||
testStreamProcessing("$assetPath/index-max-v2.json", TestDataMaxV2.index, 3)
|
||||
testStreamProcessing("$ASSET_PATH/index-max-v2.json", TestDataMaxV2.index, 3)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -8,7 +8,7 @@ import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import org.fdroid.index.IndexParser
|
||||
import org.fdroid.index.IndexParser.json
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.fdroid.index.parseV2
|
||||
import org.fdroid.test.DiffUtils.clean
|
||||
import org.fdroid.test.DiffUtils.cleanMetadata
|
||||
@@ -27,44 +27,44 @@ internal class ReflectionDifferTest {
|
||||
|
||||
@Test
|
||||
fun testEmptyToMin() = testDiff(
|
||||
diffPath = "$assetPath/diff-empty-min/23.json",
|
||||
startPath = "$assetPath/index-empty-v2.json",
|
||||
endPath = "$assetPath/index-min-v2.json",
|
||||
diffPath = "$ASSET_PATH/diff-empty-min/23.json",
|
||||
startPath = "$ASSET_PATH/index-empty-v2.json",
|
||||
endPath = "$ASSET_PATH/index-min-v2.json",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testEmptyToMid() = testDiff(
|
||||
diffPath = "$assetPath/diff-empty-mid/23.json",
|
||||
startPath = "$assetPath/index-empty-v2.json",
|
||||
endPath = "$assetPath/index-mid-v2.json",
|
||||
diffPath = "$ASSET_PATH/diff-empty-mid/23.json",
|
||||
startPath = "$ASSET_PATH/index-empty-v2.json",
|
||||
endPath = "$ASSET_PATH/index-mid-v2.json",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testEmptyToMax() = testDiff(
|
||||
diffPath = "$assetPath/diff-empty-max/23.json",
|
||||
startPath = "$assetPath/index-empty-v2.json",
|
||||
endPath = "$assetPath/index-max-v2.json",
|
||||
diffPath = "$ASSET_PATH/diff-empty-max/23.json",
|
||||
startPath = "$ASSET_PATH/index-empty-v2.json",
|
||||
endPath = "$ASSET_PATH/index-max-v2.json",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testMinToMid() = testDiff(
|
||||
diffPath = "$assetPath/diff-empty-mid/42.json",
|
||||
startPath = "$assetPath/index-min-v2.json",
|
||||
endPath = "$assetPath/index-mid-v2.json",
|
||||
diffPath = "$ASSET_PATH/diff-empty-mid/42.json",
|
||||
startPath = "$ASSET_PATH/index-min-v2.json",
|
||||
endPath = "$ASSET_PATH/index-mid-v2.json",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testMinToMax() = testDiff(
|
||||
diffPath = "$assetPath/diff-empty-max/42.json",
|
||||
startPath = "$assetPath/index-min-v2.json",
|
||||
endPath = "$assetPath/index-max-v2.json",
|
||||
diffPath = "$ASSET_PATH/diff-empty-max/42.json",
|
||||
startPath = "$ASSET_PATH/index-min-v2.json",
|
||||
endPath = "$ASSET_PATH/index-max-v2.json",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testMidToMax() = testDiff(
|
||||
diffPath = "$assetPath/diff-empty-max/1337.json",
|
||||
startPath = "$assetPath/index-mid-v2.json",
|
||||
endPath = "$assetPath/index-max-v2.json",
|
||||
diffPath = "$ASSET_PATH/diff-empty-max/1337.json",
|
||||
startPath = "$ASSET_PATH/index-mid-v2.json",
|
||||
endPath = "$ASSET_PATH/index-max-v2.json",
|
||||
)
|
||||
|
||||
@Test
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.fdroid.index.v2.IndexV2
|
||||
public object IndexParser {
|
||||
|
||||
@Volatile
|
||||
private var JSON: Json? = null
|
||||
private var jsonInstance: Json? = null
|
||||
|
||||
/**
|
||||
* Initializing [Json] is expensive, so using this method is preferable as it keeps returning
|
||||
@@ -18,7 +18,7 @@ public object IndexParser {
|
||||
public val json: Json
|
||||
@JvmStatic
|
||||
get() {
|
||||
return JSON ?: synchronized(this) {
|
||||
return jsonInstance ?: synchronized(this) {
|
||||
Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
@@ -12,28 +12,28 @@ import org.fdroid.test.v1compat
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal const val assetPath = "../sharedTest/src/main/assets"
|
||||
internal const val ASSET_PATH = "../sharedTest/src/main/assets"
|
||||
|
||||
internal class IndexConverterTest {
|
||||
|
||||
@Test
|
||||
fun testEmpty() {
|
||||
testConversation("$assetPath/index-empty-v1.json", TestDataEmptyV2.index.v1compat())
|
||||
testConversation("$ASSET_PATH/index-empty-v1.json", TestDataEmptyV2.index.v1compat())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMin() {
|
||||
testConversation("$assetPath/index-min-v1.json", TestDataMinV2.index.v1compat())
|
||||
testConversation("$ASSET_PATH/index-min-v1.json", TestDataMinV2.index.v1compat())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMid() {
|
||||
testConversation("$assetPath/index-mid-v1.json", TestDataMidV2.indexCompat)
|
||||
testConversation("$ASSET_PATH/index-mid-v1.json", TestDataMidV2.indexCompat)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMax() {
|
||||
testConversation("$assetPath/index-max-v1.json", TestDataMaxV2.indexCompat)
|
||||
testConversation("$ASSET_PATH/index-max-v1.json", TestDataMaxV2.indexCompat)
|
||||
}
|
||||
|
||||
private fun testConversation(file: String, expectedIndex: IndexV2) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.fdroid.index.v1
|
||||
import com.goncalossilva.resources.Resource
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.fdroid.index.IndexParser.parseV1
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.fdroid.test.TestDataEmptyV1
|
||||
import org.fdroid.test.TestDataMaxV1
|
||||
import org.fdroid.test.TestDataMidV1
|
||||
@@ -17,7 +17,7 @@ internal class IndexV1Test {
|
||||
|
||||
@Test
|
||||
fun testIndexEmptyV1() {
|
||||
val indexRes = Resource("$assetPath/index-empty-v1.json")
|
||||
val indexRes = Resource("$ASSET_PATH/index-empty-v1.json")
|
||||
val indexStr = indexRes.readText()
|
||||
val index = parseV1(indexStr)
|
||||
assertEquals(TestDataEmptyV1.index, index)
|
||||
@@ -25,7 +25,7 @@ internal class IndexV1Test {
|
||||
|
||||
@Test
|
||||
fun testIndexMinV1() {
|
||||
val indexRes = Resource("$assetPath/index-min-v1.json")
|
||||
val indexRes = Resource("$ASSET_PATH/index-min-v1.json")
|
||||
val indexStr = indexRes.readText()
|
||||
val index = parseV1(indexStr)
|
||||
assertEquals(TestDataMinV1.index, index)
|
||||
@@ -33,7 +33,7 @@ internal class IndexV1Test {
|
||||
|
||||
@Test
|
||||
fun testIndexMidV1() {
|
||||
val indexRes = Resource("$assetPath/index-mid-v1.json")
|
||||
val indexRes = Resource("$ASSET_PATH/index-mid-v1.json")
|
||||
val indexStr = indexRes.readText()
|
||||
val index = parseV1(indexStr)
|
||||
assertEquals(TestDataMidV1.index, index)
|
||||
@@ -41,7 +41,7 @@ internal class IndexV1Test {
|
||||
|
||||
@Test
|
||||
fun testIndexMaxV1() {
|
||||
val indexRes = Resource("$assetPath/index-max-v1.json")
|
||||
val indexRes = Resource("$ASSET_PATH/index-max-v1.json")
|
||||
val indexStr = indexRes.readText()
|
||||
val index = parseV1(indexStr)
|
||||
assertEquals(TestDataMaxV1.index, index)
|
||||
@@ -108,14 +108,14 @@ internal class IndexV1Test {
|
||||
|
||||
@Test
|
||||
fun testGuardianProjectV1() {
|
||||
val indexRes = Resource("$assetPath/guardianproject_index-v1.json")
|
||||
val indexRes = Resource("$ASSET_PATH/guardianproject_index-v1.json")
|
||||
val indexStr = indexRes.readText()
|
||||
parseV1(indexStr)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLocalizedV1() {
|
||||
val indexRes = Resource("$assetPath/localized.json")
|
||||
val indexRes = Resource("$ASSET_PATH/localized.json")
|
||||
val indexStr = indexRes.readText()
|
||||
parseV1(indexStr)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.fdroid.index.v2
|
||||
import com.goncalossilva.resources.Resource
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.fdroid.index.IndexParser
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.fdroid.test.TestDataEntry
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertContains
|
||||
@@ -14,22 +14,22 @@ internal class EntryTest {
|
||||
|
||||
@Test
|
||||
fun testEmpty() {
|
||||
testEntryEquality("$assetPath/entry-empty-v2.json", TestDataEntry.empty)
|
||||
testEntryEquality("$ASSET_PATH/entry-empty-v2.json", TestDataEntry.empty)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyToMin() {
|
||||
testEntryEquality("$assetPath/diff-empty-min/$DATA_FILE_NAME", TestDataEntry.emptyToMin)
|
||||
testEntryEquality("$ASSET_PATH/diff-empty-min/$DATA_FILE_NAME", TestDataEntry.emptyToMin)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyToMid() {
|
||||
testEntryEquality("$assetPath/diff-empty-mid/$DATA_FILE_NAME", TestDataEntry.emptyToMid)
|
||||
testEntryEquality("$ASSET_PATH/diff-empty-mid/$DATA_FILE_NAME", TestDataEntry.emptyToMid)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEmptyToMax() {
|
||||
testEntryEquality("$assetPath/diff-empty-max/$DATA_FILE_NAME", TestDataEntry.emptyToMax)
|
||||
testEntryEquality("$ASSET_PATH/diff-empty-max/$DATA_FILE_NAME", TestDataEntry.emptyToMax)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,7 +5,7 @@ import kotlinx.serialization.json.JsonNull
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import org.fdroid.index.IndexParser
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
@@ -17,22 +17,22 @@ import kotlin.test.fail
|
||||
internal class IndexV2DiffStreamProcessorTest {
|
||||
|
||||
@Test
|
||||
fun testEmptyToMin() = testDiff("$assetPath/diff-empty-min/23.json", 1)
|
||||
fun testEmptyToMin() = testDiff("$ASSET_PATH/diff-empty-min/23.json", 1)
|
||||
|
||||
@Test
|
||||
fun testEmptyToMid() = testDiff("$assetPath/diff-empty-mid/23.json", 2)
|
||||
fun testEmptyToMid() = testDiff("$ASSET_PATH/diff-empty-mid/23.json", 2)
|
||||
|
||||
@Test
|
||||
fun testEmptyToMax() = testDiff("$assetPath/diff-empty-max/23.json", 3)
|
||||
fun testEmptyToMax() = testDiff("$ASSET_PATH/diff-empty-max/23.json", 3)
|
||||
|
||||
@Test
|
||||
fun testMinToMid() = testDiff("$assetPath/diff-empty-mid/42.json", 2)
|
||||
fun testMinToMid() = testDiff("$ASSET_PATH/diff-empty-mid/42.json", 2)
|
||||
|
||||
@Test
|
||||
fun testMinToMax() = testDiff("$assetPath/diff-empty-max/42.json", 3)
|
||||
fun testMinToMax() = testDiff("$ASSET_PATH/diff-empty-max/42.json", 3)
|
||||
|
||||
@Test
|
||||
fun testMidToMax() = testDiff("$assetPath/diff-empty-max/1337.json", 2)
|
||||
fun testMidToMax() = testDiff("$ASSET_PATH/diff-empty-max/1337.json", 2)
|
||||
|
||||
@Test
|
||||
fun testRemovePackage() {
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.fdroid.index.v2
|
||||
import com.goncalossilva.resources.Resource
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.fdroid.index.IndexParser.parseV2
|
||||
import org.fdroid.index.assetPath
|
||||
import org.fdroid.index.ASSET_PATH
|
||||
import org.fdroid.test.TestDataEmptyV2
|
||||
import org.fdroid.test.TestDataMaxV2
|
||||
import org.fdroid.test.TestDataMidV2
|
||||
@@ -17,27 +17,27 @@ internal class IndexV2Test {
|
||||
|
||||
@Test
|
||||
fun testEmpty() {
|
||||
testIndexEquality("$assetPath/index-empty-v2.json", TestDataEmptyV2.index)
|
||||
testIndexEquality("$ASSET_PATH/index-empty-v2.json", TestDataEmptyV2.index)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMin() {
|
||||
testIndexEquality("$assetPath/index-min-v2.json", TestDataMinV2.index)
|
||||
testIndexEquality("$ASSET_PATH/index-min-v2.json", TestDataMinV2.index)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMinReordered() {
|
||||
testIndexEquality("$assetPath/index-min-reordered-v2.json", TestDataMinV2.index)
|
||||
testIndexEquality("$ASSET_PATH/index-min-reordered-v2.json", TestDataMinV2.index)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMid() {
|
||||
testIndexEquality("$assetPath/index-mid-v2.json", TestDataMidV2.index)
|
||||
testIndexEquality("$ASSET_PATH/index-mid-v2.json", TestDataMidV2.index)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMax() {
|
||||
testIndexEquality("$assetPath/index-max-v2.json", TestDataMaxV2.index)
|
||||
testIndexEquality("$ASSET_PATH/index-max-v2.json", TestDataMaxV2.index)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
plugins {
|
||||
id 'kotlin-android'
|
||||
id 'com.android.library'
|
||||
id "org.jlleitschuh.gradle.ktlint" version "10.2.1"
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -15,7 +14,7 @@ java {
|
||||
// not really an Android library, but index is not publishing for JVM at the moment
|
||||
android {
|
||||
namespace 'org.fdroid.test'
|
||||
compileSdk 34
|
||||
compileSdk libs.versions.compileSdk.get().toInteger()
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
}
|
||||
@@ -25,9 +24,6 @@ dependencies {
|
||||
implementation project(":libs:download")
|
||||
implementation project(":libs:index")
|
||||
|
||||
implementation 'org.jetbrains.kotlin:kotlin-test'
|
||||
// 1.4.1 because https://github.com/Kotlin/kotlinx.serialization/issues/2231
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
|
||||
implementation libs.kotlin.test
|
||||
implementation libs.kotlinx.serialization.json
|
||||
}
|
||||
|
||||
apply from: "${rootProject.rootDir}/gradle/ktlint.gradle"
|
||||
|
||||
@@ -42,8 +42,9 @@ object DiffUtils {
|
||||
* Removes keys from a JSON object representing a [MetadataV2] which need special handling.
|
||||
*/
|
||||
fun JsonObject.cleanMetadata(): JsonObject {
|
||||
val keysToFilter = listOf("icon", "featureGraphic", "promoGraphic", "tvBanner",
|
||||
"screenshots")
|
||||
val keysToFilter = listOf(
|
||||
"icon", "featureGraphic", "promoGraphic", "tvBanner", "screenshots",
|
||||
)
|
||||
val newMap = filterKeys { it !in keysToFilter }
|
||||
return JsonObject(newMap)
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ object TestDataMinV1 {
|
||||
description = "This is a repo with minimal data.",
|
||||
)
|
||||
|
||||
const val packageName = "org.fdroid.min1"
|
||||
const val PACKAGE_NAME = "org.fdroid.min1"
|
||||
val app = AppV1(
|
||||
packageName = packageName,
|
||||
packageName = PACKAGE_NAME,
|
||||
categories = emptyList(),
|
||||
antiFeatures = emptyList(),
|
||||
license = "",
|
||||
@@ -43,15 +43,15 @@ object TestDataMinV1 {
|
||||
val apps = listOf(app)
|
||||
|
||||
val version = PackageV1(
|
||||
packageName = packageName,
|
||||
apkName = "${packageName}_23.apk",
|
||||
packageName = PACKAGE_NAME,
|
||||
apkName = "${PACKAGE_NAME}_23.apk",
|
||||
hash = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
hashType = "sha256",
|
||||
size = 1337,
|
||||
versionName = "0",
|
||||
)
|
||||
val versions = listOf(version)
|
||||
val packages = mapOf(packageName to versions)
|
||||
val packages = mapOf(PACKAGE_NAME to versions)
|
||||
|
||||
val index = IndexV1(
|
||||
repo = repo,
|
||||
@@ -74,11 +74,11 @@ object TestDataMidV1 {
|
||||
mirrors = listOf("https://mid-v1.com"),
|
||||
)
|
||||
|
||||
const val packageName1 = TestDataMinV1.packageName
|
||||
const val packageName2 = "org.fdroid.fdroid"
|
||||
const val PACKAGE_NAME_1 = TestDataMinV1.PACKAGE_NAME
|
||||
const val PACKAGE_NAME_2 = "org.fdroid.fdroid"
|
||||
val categories = listOf("Cat1", "Cat2", "Cat3")
|
||||
val app1 = TestDataMinV1.app.copy(
|
||||
packageName = packageName1,
|
||||
packageName = PACKAGE_NAME_1,
|
||||
categories = listOf(categories[0]),
|
||||
antiFeatures = listOf("AntiFeature"),
|
||||
summary = "App1 summary",
|
||||
@@ -192,9 +192,9 @@ object TestDataMidV1 {
|
||||
|
||||
val version1_1 = TestDataMinV1.version.copy(
|
||||
added = 2342,
|
||||
apkName = "${packageName1}_23_2.apk",
|
||||
apkName = "${PACKAGE_NAME_1}_23_2.apk",
|
||||
size = 1338,
|
||||
srcName = "${packageName1}_23_2.zip",
|
||||
srcName = "${PACKAGE_NAME_1}_23_2.zip",
|
||||
usesPermission = listOf(PermissionV1("perm")),
|
||||
usesPermission23 = emptyList(),
|
||||
versionCode = 1,
|
||||
@@ -204,8 +204,8 @@ object TestDataMidV1 {
|
||||
antiFeatures = listOf("anti-feature"),
|
||||
)
|
||||
val version1_2 = PackageV1(
|
||||
packageName = packageName1,
|
||||
apkName = "${packageName1}_42.apk",
|
||||
packageName = PACKAGE_NAME_1,
|
||||
apkName = "${PACKAGE_NAME_1}_42.apk",
|
||||
hash = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
hashType = "sha256",
|
||||
minSdkVersion = 21,
|
||||
@@ -215,7 +215,7 @@ object TestDataMidV1 {
|
||||
signer = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
features = listOf("new feature"),
|
||||
size = 1337,
|
||||
srcName = "${packageName1}_42.zip",
|
||||
srcName = "${PACKAGE_NAME_1}_42.zip",
|
||||
versionCode = 24,
|
||||
versionName = "24",
|
||||
)
|
||||
@@ -396,8 +396,8 @@ object TestDataMidV1 {
|
||||
val versions1 = listOf(version1_1, version1_2)
|
||||
val versions2 = listOf(version2_1, version2_2, version2_3, version2_4, version2_5)
|
||||
val packages = mapOf(
|
||||
packageName1 to versions1,
|
||||
packageName2 to versions2,
|
||||
PACKAGE_NAME_1 to versions1,
|
||||
PACKAGE_NAME_2 to versions2,
|
||||
)
|
||||
|
||||
val index = IndexV1(
|
||||
@@ -422,9 +422,9 @@ object TestDataMaxV1 {
|
||||
mirrors = listOf("https://max-v1.com", "https://max-v1.org"),
|
||||
)
|
||||
|
||||
const val packageName1 = TestDataMidV1.packageName1
|
||||
const val packageName2 = TestDataMidV1.packageName2
|
||||
const val packageName3 = "Haoheiseeshai2que2Che0ooSa6aikeemoo2ap9Aequoh4ju5chooYuPhiev8moo" +
|
||||
const val PACKAGE_NAME_1 = TestDataMidV1.PACKAGE_NAME_1
|
||||
const val PACKAGE_NAME_2 = TestDataMidV1.PACKAGE_NAME_2
|
||||
const val PACKAGE_NAME_3 = "Haoheiseeshai2que2Che0ooSa6aikeemoo2ap9Aequoh4ju5chooYuPhiev8moo" +
|
||||
"dahlonu2oht5Eikahvushapeum5aefo6xig4aghahyaaNuezoo4eexee1Goo5Ung" +
|
||||
"ohGha6quaeghe8uCh9iex9Oowa9aiyohzoo2ij5miifiegaeth8nie9jae6raeph" +
|
||||
"oowishoor1Ien5vahGhahm7eidaiy2AeCaej9iexahyooshu2ic9tea1ool8tu4Y"
|
||||
@@ -468,7 +468,7 @@ object TestDataMaxV1 {
|
||||
),
|
||||
)
|
||||
val app3 = AppV1(
|
||||
packageName = packageName3,
|
||||
packageName = PACKAGE_NAME_3,
|
||||
categories = categories,
|
||||
antiFeatures = listOf("AntiFeature", "NonFreeNet", "NotNice", "VeryBad", "Dont,Show,This"),
|
||||
summary = "App3 summary",
|
||||
@@ -510,8 +510,10 @@ object TestDataMaxV1 {
|
||||
icon = "en ",
|
||||
video = "en ",
|
||||
phoneScreenshots = listOf("en phoneScreenshots", "en phoneScreenshots2"),
|
||||
sevenInchScreenshots = listOf("en sevenInchScreenshots",
|
||||
"en sevenInchScreenshots2"),
|
||||
sevenInchScreenshots = listOf(
|
||||
"en sevenInchScreenshots",
|
||||
"en sevenInchScreenshots2",
|
||||
),
|
||||
tenInchScreenshots = listOf("en tenInchScreenshots", "en tenInchScreenshots2"),
|
||||
wearScreenshots = listOf("en wearScreenshots", "en wearScreenshots2"),
|
||||
tvScreenshots = listOf("en tvScreenshots", "en tvScreenshots2"),
|
||||
@@ -605,9 +607,9 @@ object TestDataMaxV1 {
|
||||
)
|
||||
val versions3 = listOf(version3_1)
|
||||
val packages = mapOf(
|
||||
packageName1 to versions1,
|
||||
packageName2 to versions2,
|
||||
packageName3 to versions3,
|
||||
PACKAGE_NAME_1 to versions1,
|
||||
PACKAGE_NAME_2 to versions2,
|
||||
PACKAGE_NAME_3 to versions3,
|
||||
)
|
||||
|
||||
val index = IndexV1(
|
||||
|
||||
@@ -127,11 +127,11 @@ object TestDataMinV2 {
|
||||
description = mapOf(LOCALE to "This is a repo with minimal data."),
|
||||
)
|
||||
|
||||
const val packageName = "org.fdroid.min1"
|
||||
const val PACKAGE_NAME = "org.fdroid.min1"
|
||||
val version = PackageVersionV2(
|
||||
added = 0,
|
||||
file = FileV1(
|
||||
name = "/${packageName}_23.apk",
|
||||
name = "/${PACKAGE_NAME}_23.apk",
|
||||
sha256 = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
size = 1337,
|
||||
),
|
||||
@@ -150,7 +150,7 @@ object TestDataMinV2 {
|
||||
version.file.sha256 to version,
|
||||
),
|
||||
)
|
||||
val packages = mapOf(packageName to app)
|
||||
val packages = mapOf(PACKAGE_NAME to app)
|
||||
|
||||
val index = IndexV2(
|
||||
repo = repo,
|
||||
@@ -206,17 +206,17 @@ object TestDataMidV2 {
|
||||
)
|
||||
val repoCompat = repo.v1compat()
|
||||
|
||||
const val packageName1 = TestDataMinV1.packageName
|
||||
const val packageName2 = "org.fdroid.fdroid"
|
||||
const val PACKAGE_NAME_1 = TestDataMinV1.PACKAGE_NAME
|
||||
const val PACKAGE_NAME_2 = "org.fdroid.fdroid"
|
||||
val version1_1 = PackageVersionV2(
|
||||
added = 2342,
|
||||
file = FileV1(
|
||||
name = "/${packageName1}_23_2.apk",
|
||||
name = "/${PACKAGE_NAME_1}_23_2.apk",
|
||||
sha256 = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
size = 1338,
|
||||
),
|
||||
src = FileV2(
|
||||
name = "/${packageName1}_23_2.zip",
|
||||
name = "/${PACKAGE_NAME_1}_23_2.zip",
|
||||
sha256 = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
size = 1338,
|
||||
),
|
||||
@@ -233,7 +233,7 @@ object TestDataMidV2 {
|
||||
val version1_2 = PackageVersionV2(
|
||||
added = 0,
|
||||
file = FileV1(
|
||||
name = "/${packageName1}_42.apk",
|
||||
name = "/${PACKAGE_NAME_1}_42.apk",
|
||||
sha256 = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
size = 1337,
|
||||
),
|
||||
@@ -253,7 +253,7 @@ object TestDataMidV2 {
|
||||
),
|
||||
),
|
||||
src = FileV2(
|
||||
name = "/${packageName1}_42.zip",
|
||||
name = "/${PACKAGE_NAME_1}_42.zip",
|
||||
sha256 = "824a109b2352138c3699760e1683385d0ed50ce526fc7982f8d65757743374bf",
|
||||
size = 1338,
|
||||
),
|
||||
@@ -687,7 +687,7 @@ object TestDataMidV2 {
|
||||
version2_5.file.sha256 to version2_5Compat,
|
||||
),
|
||||
)
|
||||
val packages = mapOf(packageName1 to app1, packageName2 to app2)
|
||||
val packages = mapOf(PACKAGE_NAME_1 to app1, PACKAGE_NAME_2 to app2)
|
||||
|
||||
val index = IndexV2(
|
||||
repo = repo,
|
||||
@@ -696,8 +696,8 @@ object TestDataMidV2 {
|
||||
val indexCompat = index.copy(
|
||||
repo = repoCompat,
|
||||
packages = mapOf(
|
||||
packageName1 to app1Compat,
|
||||
packageName2 to app2Compat,
|
||||
PACKAGE_NAME_1 to app1Compat,
|
||||
PACKAGE_NAME_2 to app2Compat,
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -813,9 +813,9 @@ object TestDataMaxV2 {
|
||||
),
|
||||
)
|
||||
|
||||
const val packageName1 = TestDataMidV2.packageName1
|
||||
const val packageName2 = TestDataMidV2.packageName2
|
||||
const val packageName3 = "Haoheiseeshai2que2Che0ooSa6aikeemoo2ap9Aequoh4ju5chooYuPhiev8moo" +
|
||||
const val PACKAGE_NAME_1 = TestDataMidV2.PACKAGE_NAME_1
|
||||
const val PACKAGE_NAME_2 = TestDataMidV2.PACKAGE_NAME_2
|
||||
const val PACKAGE_NAME_3 = "Haoheiseeshai2que2Che0ooSa6aikeemoo2ap9Aequoh4ju5chooYuPhiev8moo" +
|
||||
"dahlonu2oht5Eikahvushapeum5aefo6xig4aghahyaaNuezoo4eexee1Goo5Ung" +
|
||||
"ohGha6quaeghe8uCh9iex9Oowa9aiyohzoo2ij5miifiegaeth8nie9jae6raeph" +
|
||||
"oowishoor1Ien5vahGhahm7eidaiy2AeCaej9iexahyooshu2ic9tea1ool8tu4Y"
|
||||
@@ -1262,16 +1262,16 @@ object TestDataMaxV2 {
|
||||
val index = IndexV2(
|
||||
repo = repo,
|
||||
packages = mapOf(
|
||||
TestDataMidV2.packageName1 to TestDataMidV2.app1,
|
||||
TestDataMidV2.packageName2 to app2,
|
||||
packageName3 to app3,
|
||||
TestDataMidV2.PACKAGE_NAME_1 to TestDataMidV2.app1,
|
||||
TestDataMidV2.PACKAGE_NAME_2 to app2,
|
||||
PACKAGE_NAME_3 to app3,
|
||||
),
|
||||
)
|
||||
val indexCompat = index.v1compat().copy(
|
||||
packages = mapOf(
|
||||
TestDataMidV2.packageName1 to TestDataMidV2.app1Compat,
|
||||
TestDataMidV2.packageName2 to app2Compat,
|
||||
packageName3 to app3Compat,
|
||||
TestDataMidV2.PACKAGE_NAME_1 to TestDataMidV2.app1Compat,
|
||||
TestDataMidV2.PACKAGE_NAME_2 to app2Compat,
|
||||
PACKAGE_NAME_3 to app3Compat,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
include ':app'
|
||||
include ':libs:sharedTest'
|
||||
include ':libs:download'
|
||||
|
||||
Reference in New Issue
Block a user