Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for f-droids new repository format #287

Open
bt90 opened this issue Mar 3, 2023 · 50 comments · Fixed by #309
Open

Add support for f-droids new repository format #287

bt90 opened this issue Mar 3, 2023 · 50 comments · Fixed by #309
Labels
enhancement New feature or request help wanted Extra attention is needed

Comments

@bt90
Copy link

bt90 commented Mar 3, 2023

The official F-Droid client recently gained support for delta repository index updates:

https://f-droid.org/2023/03/01/new-repo-format-faster-smaller-updates.html

It would be nice if Droid-ify could also use the entry.json file to speedup index updates.

@bt90 bt90 added the enhancement New feature or request label Mar 3, 2023
@Iamlooker
Copy link
Member

I will start working on this tomorrow.

@Iamlooker Iamlooker pinned this issue Mar 16, 2023
Iamlooker added a commit that referenced this issue Mar 19, 2023
Signed-off-by: LooKeR <mohit2002ss@gmail.com>
@Iamlooker
Copy link
Member

This is now the first priority. I will work on other topics once this is over

@grote
Copy link

grote commented May 1, 2023

Have you considered making use of the official F-Droid libraries for this? At least the minimal index library should be helpful.

@Iamlooker
Copy link
Member

I looked into it but thought I will write my own implementation

@grote
Copy link

grote commented May 1, 2023

Here's an early draft of a blog post about those libraries. Maybe it can convince you to not re-invent the wheel:
https://gitlab.com/fdroid/fdroid-website/-/blob/80143c506cc4ea2f0a1cd10791760fd8457c7a2a/_posts/2023-05-15-three-client-libraries.md

@Iamlooker
Copy link
Member

Iamlooker commented May 1, 2023

Cool I will read it and get back. But I just wanted to tell you something, your best locale selector is probably not working as intended. I wrote my own and I think it is passing all the tests that I wrote which are inspired by your tests. Maybe you can run those test once and check? I could be very wrong.

@Iamlooker
Copy link
Member

@grote this seems to be the solution for the locale selector imo.

Also on FDroid libraries, I want to use them but they seem a little confusing and hard to integrate with my current structure but I will try it, it is harder because I will be adding support for GitHub Release soon

@grote
Copy link

grote commented May 2, 2023

@grote this seems to be the solution for the locale selector imo.

Can you please point me to the test case(s) that fail for our implementation? Due to the differences in your code, it isn't obvious to spot.

Also on FDroid libraries, I want to use them but they seem a little confusing and hard to integrate with my current structure

Even just the index library? It should e the easiest to integrate into an existing project that doesn't use v2, yet.

Also remember that code is copyrighted and if you copy it into your project, there are certain conditions that apply such as crediting the original auther, etc. This is also true for test code.

@Iamlooker
Copy link
Member

Can you please point me to the test case(s) that fail for our implementation? Due to the differences in your code, it isn't obvious to spot.

This is one of the test which fails with the current implementation of LocaleChooser

Even just the index library? It should be the easiest to integrate into an existing project that doesn't use v2, yet.

Yes, I haven't looked into it thoroughly but I will reach back if I fail to implement it this time.

Also remember that code is copyrighted and if you copy it into your project, there are certain conditions that apply such as crediting the original auther, etc. This is also true for test code.

I have actually not copied it, I just took inspiration, you can see my Tests and Localization code here. I will give credits anyways. Thanks for notifying because I am not that knowledgeable about copyright rules.

@grote
Copy link

grote commented May 15, 2023

This is one of the test which fails with the current implementation of LocaleChooser

This test seems to be copied one-to-one from F-Droid's source code (again without attribution and license declaration):
https://gitlab.com/fdroid/fdroidclient/-/blob/680a1154cf3806390c2e4a9e95a7c6d6107b470f/libs/index/src/androidAndroidTest/kotlin/org/fdroid/BestLocaleTest.kt#L43

It is passing just fine. So maybe you were thinking of other tests that don't pass for F-Droid's locale chooser implementation?

@Iamlooker
Copy link
Member

@grote I added credits to FDroid in latest commit 3ee1064c40d8caeac5ba5719bbd86ea6d5cd8fe0. You can tell me if anything needs to be changed

@Iamlooker
Copy link
Member

It is passing just fine. So maybe you were thinking of other tests that don't pass for F-Droid's locale chooser implementation?

Idk it was failing for me. I will try to replicate it can share a ss to you here

@grote
Copy link

grote commented May 28, 2023

What device do you run it on? Which Android version? Maybe there's differences on older versions.

@Iamlooker
Copy link
Member

What device do you run it on? Which Android version? Maybe there's differences on older versions.

I am not using any devices, I am just running the test on Android Studio

@grote
Copy link

grote commented May 30, 2023

This is an instrumentation test. It won't work as a unit test which probably explains the failure you've been seeing. Roboelectric most likely doesn't provide the real implementation and just mocks those classes.

@Iamlooker
Copy link
Member

This is an instrumentation test. It won't work as a unit test which probably explains the failure you've been seeing. Roboelectric most likely doesn't provide the real implementation and just mocks those classes.

😵😵 My bad

@BholeyKaBhakt
Copy link

It still downloads whole 1.2 mb of index from izzyondroid's repo. I wonder if it's a bug or sth else.

@Idesmi
Copy link

Idesmi commented Sep 8, 2023

It still downloads whole 1.2 mb of index from izzyondroid's repo. I wonder if it's a bug or sth else.

Index v2 implementation has not been completed

@Iamlooker
Copy link
Member

@grote There seems to be a reflection error due to mismatch in ktor library version in the fdroid-download and my versions.

Maybe this can be fixed on the library side? I am encountring this issue when I add :core:data library to my :app module. The full error is:

Logs
                                                                Process: com.looker.droidify.debug, PID: 14559
                                                                java.lang.ExceptionInInitializerError
                                                                	at kotlin.reflect.jvm.internal.impl.types.error.ErrorModuleDescriptor.<clinit>(ErrorModuleDescriptor.kt:23)
                                                                	at kotlin.reflect.jvm.internal.impl.types.error.ErrorUtils.<clinit>(ErrorUtils.kt:14)
                                                                	at kotlin.reflect.jvm.internal.impl.types.error.ErrorUtils.createErrorType(Unknown Source:0)
                                                                	at kotlin.reflect.jvm.internal.impl.types.TypeUtils.<clinit>(TypeUtils.java:36)
                                                                	at kotlin.reflect.jvm.internal.impl.types.TypeUtils.makeUnsubstitutedType(TypeUtils.java:213)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.impl.AbstractClassDescriptor$1.invoke(AbstractClassDescriptor.java:49)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.impl.AbstractClassDescriptor$1.invoke(AbstractClassDescriptor.java:46)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.impl.AbstractClassDescriptor.getDefaultType(AbstractClassDescriptor.java:175)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInsCustomizer.createMockJavaIoSerializableType(JvmBuiltInsCustomizer.kt:91)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInsCustomizer.<init>(JvmBuiltInsCustomizer.kt:59)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns$customizer$2.invoke(JvmBuiltIns.kt:76)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns$customizer$2.invoke(JvmBuiltIns.kt:75)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns.getCustomizer(JvmBuiltIns.kt:75)
                                                                	at kotlin.reflect.jvm.internal.impl.load.kotlin.DeserializationComponentsForJava.<init>(DeserializationComponentsForJava.kt:80)
                                                                	at kotlin.reflect.jvm.internal.impl.load.kotlin.DeserializationComponentsForJavaKt.makeDeserializationComponentsForJava(DeserializationComponentsForJava.kt:192)
                                                                	at kotlin.reflect.jvm.internal.impl.load.kotlin.DeserializationComponentsForJava$Companion.createModuleData(DeserializationComponentsForJava.kt:123)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.runtime.components.RuntimeModuleData$Companion.create(RuntimeModuleData.kt:32)
                                                                	at kotlin.reflect.jvm.internal.ModuleByClassLoaderKt.getOrCreateModule(moduleByClassLoader.kt:58)
                                                                	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:36)
                                                                	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:35)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
                                                                	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data.getModuleData(KDeclarationContainerImpl.kt:35)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:50)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:48)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data.getDescriptor(KClassImpl.kt:48)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:182)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:44)
                                                                	at kotlin.reflect.full.KClassifiers.createType(KClassifiers.kt:48)
AndroidRuntime          com.looker.droidify.debug            E  	at kotlin.reflect.jvm.internal.ReflectionFactoryImpl.typeOf(ReflectionFactoryImpl.java:124)
                                                                	at kotlin.jvm.internal.Reflection.typeOf(Reflection.java:128)
                                                                	at io.ktor.client.statement.HttpResponseKt.bodyAsChannel(HttpResponse.kt:101)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invokeSuspend(KtorDownloader.kt:76)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invoke(Unknown Source:8)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invoke(Unknown Source:4)
                                                                	at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:50)
                                                                	at io.ktor.client.statement.HttpStatement$execute$1.invokeSuspend(Unknown Source:15)
                                                                	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
                                                                	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
                                                                	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
                                                                	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@4dcca43, Dispatchers.Main]
                                                                Caused by: java.lang.IllegalStateException: Resource not found in classpath: kotlin/kotlin.kotlin_builtins
                                                                	at kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl.createBuiltInPackageFragmentProvider(BuiltInsLoaderImpl.kt:59)
                                                                	at kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl.createPackageFragmentProvider(BuiltInsLoaderImpl.kt:35)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.createBuiltInsModule(KotlinBuiltIns.java:105)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.DefaultBuiltIns.<init>(DefaultBuiltIns.kt:24)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.DefaultBuiltIns.<init>(DefaultBuiltIns.kt:21)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.DefaultBuiltIns.<clinit>(DefaultBuiltIns.kt:31)```

</details>

@Iamlooker
Copy link
Member

Also I am getting the following error when I have both my downloader and :core:data in the main :app module.

This occurs when I try to download a file. It works fine if I dont have data module(Which contains fdroid-download) in my app module

Logs
                                                                Process: com.looker.droidify.debug, PID: 14559
                                                                java.lang.ExceptionInInitializerError
                                                                	at kotlin.reflect.jvm.internal.impl.types.error.ErrorModuleDescriptor.<clinit>(ErrorModuleDescriptor.kt:23)
                                                                	at kotlin.reflect.jvm.internal.impl.types.error.ErrorUtils.<clinit>(ErrorUtils.kt:14)
                                                                	at kotlin.reflect.jvm.internal.impl.types.error.ErrorUtils.createErrorType(Unknown Source:0)
                                                                	at kotlin.reflect.jvm.internal.impl.types.TypeUtils.<clinit>(TypeUtils.java:36)
                                                                	at kotlin.reflect.jvm.internal.impl.types.TypeUtils.makeUnsubstitutedType(TypeUtils.java:213)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.impl.AbstractClassDescriptor$1.invoke(AbstractClassDescriptor.java:49)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.impl.AbstractClassDescriptor$1.invoke(AbstractClassDescriptor.java:46)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.impl.AbstractClassDescriptor.getDefaultType(AbstractClassDescriptor.java:175)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInsCustomizer.createMockJavaIoSerializableType(JvmBuiltInsCustomizer.kt:91)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltInsCustomizer.<init>(JvmBuiltInsCustomizer.kt:59)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns$customizer$2.invoke(JvmBuiltIns.kt:76)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns$customizer$2.invoke(JvmBuiltIns.kt:75)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527)
                                                                	at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns.getCustomizer(JvmBuiltIns.kt:75)
                                                                	at kotlin.reflect.jvm.internal.impl.load.kotlin.DeserializationComponentsForJava.<init>(DeserializationComponentsForJava.kt:80)
                                                                	at kotlin.reflect.jvm.internal.impl.load.kotlin.DeserializationComponentsForJavaKt.makeDeserializationComponentsForJava(DeserializationComponentsForJava.kt:192)
                                                                	at kotlin.reflect.jvm.internal.impl.load.kotlin.DeserializationComponentsForJava$Companion.createModuleData(DeserializationComponentsForJava.kt:123)
                                                                	at kotlin.reflect.jvm.internal.impl.descriptors.runtime.components.RuntimeModuleData$Companion.create(RuntimeModuleData.kt:32)
                                                                	at kotlin.reflect.jvm.internal.ModuleByClassLoaderKt.getOrCreateModule(moduleByClassLoader.kt:58)
                                                                	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:36)
                                                                	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:35)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
                                                                	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data.getModuleData(KDeclarationContainerImpl.kt:35)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:50)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:48)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data.getDescriptor(KClassImpl.kt:48)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:182)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:44)
                                                                	at kotlin.reflect.full.KClassifiers.createType(KClassifiers.kt:48)
AndroidRuntime          com.looker.droidify.debug            E  	at kotlin.reflect.jvm.internal.ReflectionFactoryImpl.typeOf(ReflectionFactoryImpl.java:124)
                                                                	at kotlin.jvm.internal.Reflection.typeOf(Reflection.java:128)
                                                                	at io.ktor.client.statement.HttpResponseKt.bodyAsChannel(HttpResponse.kt:101)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invokeSuspend(KtorDownloader.kt:76)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invoke(Unknown Source:8)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invoke(Unknown Source:4)
                                                                	at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:50)
                                                                	at io.ktor.client.statement.HttpStatement$execute$1.invokeSuspend(Unknown Source:15)
                                                                	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
                                                                	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
                                                                	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
                                                                	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@4dcca43, Dispatchers.Main]
                                                                Caused by: java.lang.IllegalStateException: Resource not found in classpath: kotlin/kotlin.kotlin_builtins
                                                                	at kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl.createBuiltInPackageFragmentProvider(BuiltInsLoaderImpl.kt:59)
                                                                	at kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl.createPackageFragmentProvider(BuiltInsLoaderImpl.kt:35)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.createBuiltInsModule(KotlinBuiltIns.java:105)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.DefaultBuiltIns.<init>(DefaultBuiltIns.kt:24)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.DefaultBuiltIns.<init>(DefaultBuiltIns.kt:21)
                                                                	at kotlin.reflect.jvm.internal.impl.builtins.DefaultBuiltIns.<clinit>(DefaultBuiltIns.kt:31)```

</details>

@grote
Copy link

grote commented Sep 28, 2023

does it work when using the same ktor library version?

@Iamlooker
Copy link
Member

does it work when using the same ktor library version?

Nope it still fails

@grote
Copy link

grote commented Sep 28, 2023

Does it work when excluding the ktor dependency from fdroid's downloader lib? is your kotlin-reflect dependency on the same kotlin version as your project?

@Iamlooker
Copy link
Member

I am not using kotlin-relfect. And exclusion was not possible working (Maybe I was doing something wrong), I tried this:

implementation(FDroid.download) { exclude("io.ktor") }

but it was still available to use

@grote
Copy link

grote commented Sep 28, 2023

Your stacktrace shows that you do use kotlin-reflect, so please as a first thing add it as an explicit dependency and ensure that it uses the same kotlin version as your project does. This is a common source of such errors.

@Iamlooker
Copy link
Member

Iamlooker commented Sep 28, 2023

Your stacktrace shows that you do use kotlin-reflect, so please as a first thing add it as an explicit dependency and ensure that it uses the same kotlin version as your project does. This is a common source of such errors.

That seems to have fixed it. I digged a little deeper and it seemed like Android Studio G comes with kotlin version 1.8.20 and the reflect was also using the same.

Either-way thanks 😄

@Iamlooker
Copy link
Member

Iamlooker commented Oct 16, 2023

I am facing a similar issue again, this time I have the latest reflect and kotlin version but still.

Details
                                                                java.util.concurrent.ExecutionException: kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Unresolved class: class java.lang.String
                                                                	at androidx.work.impl.utils.futures.AbstractFuture.getDoneValue(AbstractFuture.java:516)
                                                                	at androidx.work.impl.utils.futures.AbstractFuture.get(AbstractFuture.java:475)
                                                                	at androidx.work.impl.WorkerWrapper$2.run(WorkerWrapper.java:317)
                                                                	at androidx.work.impl.utils.SerialExecutorImpl$Task.run(SerialExecutorImpl.java:96)
                                                                	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
                                                                	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
                                                                	at java.lang.Thread.run(Thread.java:1012)
                                                                Caused by: kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Unresolved class: class java.lang.String
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.reportUnresolvedClass(KClassImpl.kt:328)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.access$reportUnresolvedClass(KClassImpl.kt:44)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:56)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:48)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
                                                                	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl$Data.getDescriptor(KClassImpl.kt:48)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:182)
                                                                	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:44)
                                                                	at kotlin.reflect.full.KClassifiers.createType(KClassifiers.kt:47)
                                                                	at kotlin.reflect.jvm.internal.CachesKt$CACHE_FOR_BASE_CLASSIFIERS$1.invoke(caches.kt:37)
                                                                	at kotlin.reflect.jvm.internal.CachesKt$CACHE_FOR_BASE_CLASSIFIERS$1.invoke(caches.kt:36)
                                                                	at kotlin.reflect.jvm.internal.ComputableClassValue.computeValue(CacheByClass.kt:48)
                                                                	at kotlin.reflect.jvm.internal.ComputableClassValue.computeValue(CacheByClass.kt:46)
                                                                	at java.lang.ClassValue.getFromHashMap(ClassValue.java:230)
                                                                	at java.lang.ClassValue.getFromBackup(ClassValue.java:212)
                                                                	at java.lang.ClassValue.get(ClassValue.java:116)
                                                                	at kotlin.reflect.jvm.internal.ClassValueCache.get(CacheByClass.kt:61)
                                                                	at kotlin.reflect.jvm.internal.CachesKt.getOrCreateKType(caches.kt:55)
                                                                	at kotlin.reflect.jvm.internal.ReflectionFactoryImpl.typeOf(ReflectionFactoryImpl.java:123)
                                                                	at kotlin.jvm.internal.Reflection.typeOf(Reflection.java:128)
                                                                	at org.fdroid.index.v1.PermissionV1Serializer$descriptor$1.invoke(PackageV1.kt:124)
                                                                	at org.fdroid.index.v1.PermissionV1Serializer$descriptor$1.invoke(PackageV1.kt:95)
                                                                	at kotlinx.serialization.descriptors.SerialDescriptorsKt.buildClassSerialDescriptor(SerialDescriptors.kt:58)
                                                                	at org.fdroid.index.v1.PermissionV1Serializer.<init>(PackageV1.kt:95)
                                                                	at org.fdroid.index.v1.PermissionV1$Companion.serializer(PackageV1.kt:88)
                                                                	at org.fdroid.index.v1.PackageV1$$serializer.deserialize(PackageV1.kt:28)
                                                                	at org.fdroid.index.v1.PackageV1$$serializer.deserialize(PackageV1.kt:28)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:70)
                                                                	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
                                                                	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:162)
                                                                	at kotlinx.serialization.encoding.CompositeDecoder$DefaultImpls.decodeSerializableElement$default(Decoding.kt:533)
                                                                	at kotlinx.serialization.internal.CollectionLikeSerializer.readElement(CollectionSerializers.kt:80)
                                                                	at kotlinx.serialization.internal.AbstractCollectionSerializer.readElement$default(CollectionSerializers.kt:51)
WM-WorkerWrapper        com.looker.droidify.debug            E  	at kotlinx.serialization.internal.AbstractCollectionSerializer.merge(CollectionSerializers.kt:36)
                                                                	at kotlinx.serialization.internal.AbstractCollectionSerializer.deserialize(CollectionSerializers.kt:43)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:70)
                                                                	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
                                                                	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:162)
                                                                	at kotlinx.serialization.encoding.CompositeDecoder$DefaultImpls.decodeSerializableElement$default(Decoding.kt:533)
                                                                	at kotlinx.serialization.internal.MapLikeSerializer.readElement(CollectionSerializers.kt:111)
                                                                	at kotlinx.serialization.internal.MapLikeSerializer.readElement(CollectionSerializers.kt:84)
                                                                	at kotlinx.serialization.internal.AbstractCollectionSerializer.readElement$default(CollectionSerializers.kt:51)
                                                                	at kotlinx.serialization.internal.AbstractCollectionSerializer.merge(CollectionSerializers.kt:36)
                                                                	at kotlinx.serialization.internal.AbstractCollectionSerializer.deserialize(CollectionSerializers.kt:43)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:70)
                                                                	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
                                                                	at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:162)
                                                                	at org.fdroid.index.v1.IndexV1$$serializer.deserialize(IndexV1.kt:13)
                                                                	at org.fdroid.index.v1.IndexV1$$serializer.deserialize(IndexV1.kt:13)
                                                                	at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:70)
                                                                	at kotlinx.serialization.json.internal.JsonStreamsKt.decodeByReader(JsonStreams.kt:38)
                                                                	at kotlinx.serialization.json.JvmStreamsKt.decodeFromStream(JvmStreams.kt:59)
                                                                	at org.fdroid.index.IndexParserKt.parseV1(IndexParser.kt:25)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator$getIndexAndFingerprint$2.invokeSuspend(IndexValidator.kt:44)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator$getIndexAndFingerprint$2.invoke(Unknown Source:8)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator$getIndexAndFingerprint$2.invoke(Unknown Source:4)
                                                                	at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78)
                                                                	at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:167)
                                                                	at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator.getIndexAndFingerprint(IndexValidator.kt:37)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator.access$getIndexAndFingerprint(IndexValidator.kt:17)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator$validate$2.invokeSuspend(IndexValidator.kt:22)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator$validate$2.invoke(Unknown Source:8)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator$validate$2.invoke(Unknown Source:4)
                                                                	at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:78)
                                                                	at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:167)
                                                                	at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
                                                                	at com.looker.core.data.fdroid.sync.signature.IndexValidator.validate(IndexValidator.kt:21)
                                                                	at com.looker.network.KtorDownloader$downloadToFile$2.invokeSuspend(KtorDownloader.kt:77)
                                                                	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:270)
                                                                	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
WM-WorkerWrapper        com.looker.droidify.debug            E  	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
                                                                	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
                                                                	at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
                                                                	at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
                                                                	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)```

@bt90
Copy link
Author

bt90 commented Oct 16, 2023

ProGuard 🤔

@Iamlooker
Copy link
Member

ProGuard 🤔

In debug builds tho

@Ght55
Copy link

Ght55 commented Oct 28, 2023

Not select text for copy

@Iamlooker
Copy link
Member

I am facing a similar issue again, this time I have the latest reflect and kotlin version but still.

Btw this happens when I am downloading a v1 format index. Sorry @grote for tagging again

@grote
Copy link

grote commented Oct 31, 2023

Unresolved class: class java.lang.String looks very strange. Does it happen for all v1 indexes or only a specific one? Have you tried manually parsing the affected index and reducing it down to find the part of it causing the error?

@Iamlooker
Copy link
Member

Unresolved class: class java.lang.String looks very strange. Does it happen for all v1 indexes or only a specific one?

Yes, it happens with Izzy and FDroid index v1.

Have you tried manually parsing the affected index and reducing it down to find the part of it causing the error?

Removing packages map fixes the issue

@Iamlooker
Copy link
Member

You can test it in this branch, you just need to build and click sync button on main screen

@chimbori
Copy link

chimbori commented Nov 5, 2023

Tested with repo https://apps.chimbori.com/fdroid/repo, but it still fails to validate the index with the Droid-ify APK built from source from test-index-v1.

The repo was generated by fdroidserver: stable 2.2.1 on macOS, and can be correctly parsed by the official F-Droid app.

Thank you for looking into this!

@spider1163 spider1163 mentioned this issue Mar 31, 2024
@mecuricat
Copy link

Any update on this?

@Iamlooker Iamlooker added the help wanted Extra attention is needed label Apr 30, 2024
@Iamlooker
Copy link
Member

@machiav3lli As we are both working with same stuff here, should we both work on it here? Or are you planning to use FDroid's library completely through-and-through?

@Iamlooker
Copy link
Member

Only support for diff parsing is missing

Otherwise everything seems to be working the way I intend 🤔

I will add license and other references in few day to code similar to FDroid Library.

@Iamlooker
Copy link
Member

I would like some help with Diff Parsing to speed up the process, I won't be able to allocate any time to this for some time now.

@machiav3lli
Copy link
Contributor

Started checking & navigating the codes & docs.

@machiav3lli
Copy link
Contributor

@Iamlooker So, just reporting: the most complexity seems in keeping the automated parsing and still using diff. Specially considering v2 diffs, the archived versions get null value, which can't be handled in the current map objects. There is two easy-ish solutions:

  • Either we copy f-droid's ReflectionDiffer.applyDiff() and maintain our fitted version.
  • or we duplicate your IndexV2, PackageV2 & RepoV2 in Diff-variations were the related fields are nullable. This would keep the the objects non-nullable for external, non-indexing works.

Another (mostly implausible) option is to use F-Droid's libs, but then it'll get pretty complex adding extensions (also considering the effort you put in this). So I'll wait your opinion, we can also discuss things in a call/chat.

@Iamlooker
Copy link
Member

@machiav3lli Thanks for going through this messy code. About automated parsing, we can switch to old school Jackson parsing if we wanted to, its just rewriting these two classes: V1Parser & V2Parser

Both the options sound good to me, but if I had to choose one it would be the second one, just duplicate the models, make some edits and its almost good to go (I am running away from reflection, they scare me now)

Also about extensions, we can simply extend these classes like ExodusSyncable or ExodusParser in future to add extensions and make them optional based on user preferences, this would be really simple as far as I can think.

You know me, my dm's are always open 😄 . We can even arrange a call at your preferable time.

@Iamlooker
Copy link
Member

Aah and also I think switching to old school parsing will allow us to not create a so many models and just rely on some builders or something, idk. Or or or wait... we can write our own json parser, the fastest one yet using json-schema which is automated but also maps to current models and is just perfect?

NVM 😅

@machiav3lli
Copy link
Contributor

@Iamlooker

About automated parsing, we can switch to old school Jackson parsing if we wanted to, its just rewriting these two classes: V1Parser & V2Parser

Hehe, nah nah not that complicated. It's just defining Diff-Variations of the mentioned 3 classes and declaring patchers.

Both the options sound good to me, but if I had to choose one it would be the second one, just duplicate the models, make some edits and its almost good to go (I am running away from reflection, they scare me now)

I've already written most of the logic locally and should be able to complete it and PR it before the weekend. It seems that sync:fdroid will have to be an android.library instead of jvm.library. I actually don't mind this, as I'd need anyway another implementation for Workers.

Maybe we can in the future streamline things and make an independent lib out of this, but not for now.

Also about extensions, we can simply extend these classes like ExodusSyncable or ExodusParser in future to add extensions and make them optional based on user preferences, this would be really simple as far as I can think.

It'd be even easier just adding the extensions to the respective serializable classes with empty/null defaults.

Aah and also I think switching to old school parsing will allow us to not create a so many models and just rely on some builders or something, idk. Or or or wait... we can write our own json parser, the fastest one yet using json-schema which is automated but also maps to current models and is just perfect?

Actually Foxy-Droid's old-school parser is still the one used in NS, which is afair the last part of Foxy-Droid still existing in NS, but will be replaced to one using kotlinx.serialization in 1.1.0, which is part of my plan for Index-V2+. We'll talk about it in more details when I start officially working on it.

@machiav3lli
Copy link
Contributor

PRed in #825

@grote
Copy link

grote commented Sep 4, 2024

When parsing the JSON diff as JSON, how do you differentiate a missing value from an explicit null using kotlinx.serialization?

@machiav3lli
Copy link
Contributor

@grote I solved this in a pretty hacky way (for now), defaulting values of the duplicate classes (Diff as suffix) to null, and then only writing the values existing in the diff over the ones in the current index. Taking maps, lists and non-atomic-objects into account. Check the IndexV2.kt, RepoV2.kt and PackageV2.kt files. Mainly the patchInto() functions are relevant to your question.

@grote
Copy link

grote commented Sep 4, 2024

I solved this in a pretty hacky way (for now), defaulting values of the duplicate classes (Diff as suffix) to null, and then only writing the values existing in the diff over the ones in the current index.

Right, but what if an app or a version got removed by setting it to null in the diff. How do you differentiate non-existence from an explicit null? At least this bit wasn't clear to me when looking at the code. I guess for maps, you get an entry of entry(key, null), but what if the donation option of an app was removed for example?

@machiav3lli
Copy link
Contributor

I guess for maps, you get an entry of entry(key, null),

Exactly.

but what if the donation option of an app was removed for example?

in the current implementation it'll be kept :p (the scenario wasn't included in the diffs I tested, so it slipped through). So I guess the patchers will have to go one level deeper.

Lists are one of the things I needed more information on (and didn't find). For example what diff we get when a mirror get updated: do we get the whole list updated? or only the updated mirror? The first one would increase the size of the diff (not relevant tbh), the second will lead to conflicts on the logic of patching.

@grote
Copy link

grote commented Sep 4, 2024

You may want to add test cases. Please observe licensing and attribution in case you copy test cases from the official v2 libs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request help wanted Extra attention is needed
Projects
Development

Successfully merging a pull request may close this issue.

9 participants