Error with Parcelable / Parceler when minifyEnabled true - parcelable

I am getting the following error when I set minifyEnabled true:
Unable to find generated Parcelable class for com.codeworks.myapp.FirestoreModel, verify that your class is configured properly and that the Parcelable class com.codeworks.myapp.FirestoreModel$$Parcelable is generated by Parceler.
I placed the following code as shown in the parceler.org website in proguard-rules.pro:
# Parceler configuration
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
-keep class org.parceler.Parceler$$Parcels
... and added the #Keep annotation in the FirestoreModel and the Fragment's class:
#Parcel
#Keep
class FirestoreModel{
...
}
#Keep
class MyFragment: Fragment() {
...
}
and then added some pro-guard rules, or as I understood it from the examples from stackoverflow.com (because I can't understand the rules from official documentation):
-keepnames class com.codeworks.myapp.MyFragment { *; }
-keepnames class com.codeworks.myappFirestoreModel { *; }
I am still getting the following error:
2020-04-21 08:07:38.554 28188-28188/com.codeworks.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.codeworks.myapp, PID: 28188
h.a.d: Unable to find generated Parcelable class for com.codeworks.myappFirestoreModel, verify that your class is configured properly and that the Parcelable class com.codeworks.myappFirestoreModel$$Parcelable is generated by Parceler.
at h.a.e$b.a(:154)
at h.a.e.a(:73)
at h.a.e.a(:57)
at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:103)
at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:68)
at com.firebase.ui.firestore.FirestoreRecyclerAdapter.b(:158)
at androidx.recyclerview.widget.RecyclerView$g.a(:7065)
at androidx.recyclerview.widget.RecyclerView$g.a(:7107)
at androidx.recyclerview.widget.RecyclerView$v.a(:6012)
at androidx.recyclerview.widget.RecyclerView$v.a(:6279)
at androidx.recyclerview.widget.RecyclerView$v.b(:6118)
at androidx.recyclerview.widget.RecyclerView$v.d(:6114)
at androidx.recyclerview.widget.LinearLayoutManager$c.a(:2303)
at androidx.recyclerview.widget.LinearLayoutManager.a(:1627)
at androidx.recyclerview.widget.LinearLayoutManager.a(:1587)
at androidx.recyclerview.widget.LinearLayoutManager.e(:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(:3851)
at androidx.recyclerview.widget.RecyclerView.consumePendingUpdateOperations(:1897)
at androidx.recyclerview.widget.RecyclerView$a.run(:414)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:972)
at android.view.Choreographer.doCallbacks(Choreographer.java:796)
at android.view.Choreographer.doFrame(Choreographer.java:727)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:957)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7464)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:549)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:955)
Apparently, the variable names of the onViewCreated function on MyFragment are being renamed despite the pro-guard rules.
The error seems to point to this lines:
// Here... at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:68)
adapter = object : FirestoreRecyclerAdapter<DpMemoModel, DpViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FirestoreViewHolder{
val view : View = LayoutInflater.from(parent.context).inflate(R.layout.firestore_cardview, parent, false)
return FirestoreViewHolder(view)
}
override fun onBindViewHolder(
holder: FirestoreViewHolder,
position: Int,
model: FirestoreModel
) {
...
// ...and Here: **at com.codeworks.myapp.MyFragment$onViewCreated$1.a(:103)**
val wrapped : Parcelable = Parcels.wrap(model)
holder.cardView.setOnClickListener {
val intent= Intent(activity, FirestoreInfoActivity::class.java)
intent.putExtra("parcel", wrapped)
startActivity(intent)
}
P.S.: I am using FirestoreRecyclerAdapter, I thought it was the best way since I was dealing with Cloud Firestore and Firebase Storage data.

I don't know what went wrong or what I've missed. I switched to #Parcelize for now. The code is a bit longer, but at least it works. Also, there is no need to use:
# Parceler configuration
-keep interface org.parceler.Parcel
-keep #org.parceler.Parcel class * { *; }
-keep class **$$Parcelable { *; }
-keep class org.parceler.Parceler$$Parcels
...in the proguard-rules.pro. All I need is #Keep annotation in the data class. I've spent a day and a half on research and trial and error, but, well, c'est la vie.

Related

Serializer for sealed interface (kotlinx.serialization)

I am trying to serialize my base class that is implementing two sealed interfaces. I have tried multiple approaches, yet i always get the error :
caused by: kotlinx.serialization.SerializationException: Class 'PayloadFromBuilder' is not registered for polymorphic serialization in the scope of 'Payload'.
Mark the base class as 'sealed' or register the serializer explicitly.
I was following mostly this guide Kotlinx/polymorphism and checked some similar questions here.
My code:
sealed inteface MyClass {
dataetc
}
#Serializable
private class DefaultMyClass(dataetc): MyClass
fun MyClass(dataetc): MyClass = DefaultMyClass
Sealed interface MyClassBuilder {
fun dataetc(value: ByteArray)
fun dataetc(value: ByteArray)
fun dataetc(value: ByteArray?)
}
#PublishedApi
#Serializable
#SerialName("payload")
internal class MyClassFromBuilder: MyClassBuilder, MyClass {
}
//Serialization
val module = SerializersModule {
polymorphic(MyClass::class) {
subclass(MyClassFromBuilder::class, MyClassFromBuilder.serializer())
default { MyClassFromBuilder.serializer() }
}
polymorphic(MyClassBuilder::class) {
subclass(MyClassFromBuilder::class, MyClassFromBuilder.serializer())
default { MyClassFromBuilder.serializer() }
}
}
val ConfiguredProtoBuf = ProtoBuf { serializersModule = module }
#ExperimentalSerializationApi
internal inline fun <reified T> ProtoBuf.encodeToMessage(value: T): Message =
Message(encodeToByteArray(value))
From what i have seen i think i am very close to the solution yet i am missing something, since my example is very generic if you need more info let me know, thank you in advance.
Note: In my several tries i have tried to annotate both sealed intefaces with #Polymorphic but i am not sure if it changed anything.
Note 2: My code breaks when i am calling the encodeToMessage fun
So i messed big time, turns out i was not using my ConfiguredProtoBuf when i was calling my encodeToMessage

String cannot be provided without an #Provides-annotated method

I'm trying to use ActivityScoped and ActivityComponent in a simple project, but i was getting below error
#javax.inject.Named("String2") java.lang.String cannot be provided without an
#Provides-annotated method.
public abstract static class SingletonC implements BaseApplication_GeneratedInjector,
#javax.inject.Named("String2") java.lang.String is injected at
com.example.hiltoplayground.TestViewModel(testString)
com.example.hiltoplayground.TestViewModel is injected at
com.example.hiltoplayground.TestViewModel_HiltModules.BindsModule.binds(vm)
#dagger.hilt.android.internal.lifecycle.HiltViewModelMap
java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>>
is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.example.hiltoplayground.BaseApplication_HiltComponents.SingletonC → com.example.hiltoplayground.BaseApplication_HiltComponents.ActivityRetainedC → com.example.hiltoplayground.BaseApplication_HiltComponents.ViewModelC]
But if i use SingletonComponent and #Singleton it runs perfect, no issue.
Here is the affected module
#Module
#InstallIn(ActivityComponent::class)
object MainModule {
#ActivityScoped
#Provides
#Named("String2")
fun provideTestString2 (
#ApplicationContext context: Context
) = context.getString(R.string.string_inject)
}
Below is the ViewModel (No problem here i guess)
#HiltViewModel
class TestViewModel #Inject constructor(
#Named("String2") testString: String): ViewModel() {
init{
Log.d("String2Message","Show $testString")
}
}
You can use #ActivityRetainedScoped in place of #ActivityScoped and make #InstallIn(ActivityRetainedComponent::class) from #InstallIn(ActivityComponent::class).

How to mock an SQLiteOpenHelper

I am trying to mock an SQLiteOpenHelper class in instrumented tests so whenever any fragment tries to get information from the database it returns a generic result. However, I keep getting an error saying:
org.mockito.exceptions.base.MockitoException: Cannot mock/spy class
com.example.cleaningschedule.helpers.DatabaseHandler Mockito cannot
mock/spy because :
final class
at com.example.cleaningschedule.ToDoListInstrumentedTest.oneTask(ToDoListInstrumentedTest.kt:81)
The test class is:
#RunWith(AndroidJUnit4::class)
class ToDoListInstrumentedTest {
#Rule
#JvmField var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
private fun getActivity() = activityRule.activity
#After
fun tearDown() {
InstrumentationRegistry.getInstrumentation().getTargetContext().deleteDatabase("TaskDatabase")
}
#Test
fun oneTask() {
val mock = mock(DatabaseHandler::class.java)
`when`(mock.getTasks()).thenThrow()
onView(withId(R.id.taskName)).check(matches(isDisplayed()))
}
}
The class I am trying to mock is:
class DatabaseHandler(context: Context): SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
companion object {
private const val DATABASE_VERSION = 5
private const val DATABASE_NAME = "TaskDatabase"
...
}
override fun onCreate(db: SQLiteDatabase?) {
...
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
...
}
fun getTasks(): MutableList<Pair<MutableList<String>, MutableList<Room>>> {
...
}
}
I have looked at several other similar questions but none have helped:
Error mocking Class which hold reference to SQLiteOpenHelper
Mock final class in java using mockito library - I had a lot of issues with import PowerMock
How to mock a final class with mockito - I have added the dependency and created the file with the mock-maker-inline line as suggested in the answers put I still get the same error. I also tried the answer that suggested Mockito.mock(SomeMockableType.class,AdditionalAnswers.delegatesTo(someInstanceThatIsNotMockableOrSpyable)) but this gave me a 'Not enough information to infer type variable T' error
Mock final class with Mockito 2
Mockito cannot mock/spy because : Final Class
Cannot mock/spy class java.util.Optional
I will made an Interface :
public interface ContainerHandler {
MutableList<Pair<MutableList<String>, MutableList<Room>>> getTasks();
}
Then I made DatabaseHandler inherit this interface, I call Mockito's mock function with the Interface.
val mock = mock(ContainerHandler::class.java)
`when`(mock.getTasks()).thenThrow()
And finally I inject my mock into the tested class.

How to fix Proguard removes java.security code

I am using java.security.PublicKey in my project
However, when I enable proguard, I get the following error:
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String java.security.PublicKey.getAlgorithm()' on a null object reference
After some investigation I found that it falls on
val entry = KeyStore.PrivateKeyEntry(keyPair.private, arrayOf(certificate)) <--- this line
keyStore.setEntry(alias, entry, keyProtection)
How do I make it work?
adding this to the proguard file does not work:
-keep class java.security.** { *; }
-keepclassmembers class java.security.** { *; }
-keep public interface java.security.Key {*;}
-keep public interface java.security.PublicKey {*;}
-keepclassmembers class * implements java.security.PublicKey {
public <methods>;
}
After even more investigations I found that a more specific line in the constructor of KeyStore.PrivateEntry() is causing the issue
And it is
Certificate[] clonedChain = chain.clone();
clonedChain[0].publicKey is left null
How do I make it clone the public key as well?
The issue was deprecated code.
I was using deprecated spongycastle code to generate a self signed certificate.
The public key in said certificate turned to be null, when on proguard.
I changed to the following:
api 'com.madgag.spongycastle:core:1.58.0.0'
api 'com.madgag.spongycastle:prov:1.58.0.0'
api 'com.madgag.spongycastle:bcpkix-jdk15on:1.58.0.0'
api 'com.madgag.spongycastle:bcpg-jdk15on:1.58.0.0'
And then:
fun KeyPair.toSelfSignedCertificate(principal: String, signatureAlgorithm: String, validity: Int): X509Certificate? {
val x500Name = X500Name("CN=$principal")
val today = Calendar.getInstance()
val notBefore = today.timeInMillis
today.add(Calendar.YEAR, validity)
val notAfter = today.timeInMillis
val sigGen: ContentSigner = JcaContentSignerBuilder(signatureAlgorithm).build(private)
val publicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(public.encoded))
val certGen = X509v3CertificateBuilder(x500Name,
RSAKeyGenParameterSpec.F4,
Date(notBefore),
Date(notAfter),
x500Name,
publicKeyInfo)
val certHolder: X509CertificateHolder = certGen.build(sigGen)
return JcaX509CertificateConverter().getCertificate(certHolder)
}
And it worked well.

Parcelable overload resolution ambiguity

I am trying to create a POJO (aka data classes in Kotlin) structure of a JSON response in Kotlin. I've implemented the Parcelable interface for each data class in the structure. In all of the data classes, I've auto generated the Parcelable implementation. The issue is the generated second constructor where the IDE is complaining about:
Overload resolution ambiguity
It states that it's being confused between these two constructors:
public constructor GeocodeRes(parcel: Parcel)
public constructor GeocodeRes(responset: ResponseRes)
Which I believe makes sense because ResponseRes is also of type Parcelable (ResponseRes implements Parcelable). So calling the GeocodeRes(parcel) method (within the createFromParcel companion method), it is getting confused.
That was until I removed ResponseRes from implementing the Parcelable class and it's still showing the same error.
Is there any reason to this? Am I setting this up properly? In all of the children data classes, they all implement the Parcelable interface (with dependence with eachother) but aren't running into any issues.
Here's my GeocodeRes class:
import android.os.Parcel
import android.os.Parcelable
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
data class GeocodeRes(
#SerializedName("Response") #Expose val responset: ResponseRes
) : Parcelable {
// this is the problem. the IDE is complaining that the usage is too ambiguous (). however, the only usage of this constructor is within this class - just doesn't tell me where exactly.
constructor(parcel: Parcel) : this(parcel.readParcelable(ResponseRes::class.java.classLoader)) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeParcelable(responset, flags)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<GeocodeRes> {
override fun createFromParcel(parcel: Parcel): GeocodeRes {
return GeocodeRes(parcel)
}
override fun newArray(size: Int): Array<GeocodeRes?> {
return arrayOfNulls(size)
}
}
}
Here's my ResponseRes class:
data class ResponseRes(
#SerializedName("MetaInfo") #Expose val metaInfo: MetaInfo,
#SerializedName("View") #Expose val views: List<View>
): Parcelable
{
[...]//parcel methods
}
however, the only usage of this constructor is within this class - just doesn't tell me where exactly
The problem is with the definition itself, not with any usage. It could never be used, and the error would still be there.
You should be able to fix this by specifying which Parcelable you want to read:
this(parcel.readParcelable<ResponseRes>(ResponseRes::class.java.classLoader))
The compiler can't decide if you mean that or
this(parcel.readParcelable<Parcel>(ResponseRes::class.java.classLoader))
Even though the second wouldn't be legal because Parcel doesn't implement Parcelable, if you look at the signature
<T extends Parcelable> T readParcelable(ClassLoader loader)
you can see only the return type can be used to infer T, not the argument. So the compiler need to pick the constructor overload before trying to infer T.