Fix ProGuard obfuscation crash on state restoration after process death #455
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Tracked by https://github.com/warting/android-signaturepad/issues (minified build crash)
This PR...
Prevents
ClassCastException/NullPointerExceptioncrashes in minified builds when restoringSignaturePadstate after process death.Considerations and implementation
The
EventParcelable class gets obfuscated by ProGuard/R8, causing Android's deserialization to fail inonRestoreInstanceState(). The fix adds a keep rule:Changes:
signature-core/consumer-rules.prowith keep rule forEventclassconsumerProguardFiles("consumer-rules.pro")insignature-core/build.gradle.ktssignature-core/proguard-rules.profor consistencyThe
consumerProguardFilesconfiguration ensures the rule is automatically applied to consuming apps via the library AAR.How to test
minifyEnabled trueadb shell am kill <package>Test(s) added
No tests added - this is a build configuration fix for ProGuard. The existing behavior (state restoration) already works in debug builds; this ensures it works in release builds.
Screenshots
Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
scans-in.gradle.com/usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -Xmx4096m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.14.1-bin/baw1sv0jfoi8rxs14qo3h49cs/gradle-8.14.1/lib/gradle-daemon-main-8.14.1.jar(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
This section details on the original issue you should resolve
<issue_title>App is crashing in minified build</issue_title>
<issue_description>App is crashing in minified build.
In emulator with Android 13 , app is crashing when app brought to foreground after process death.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
App should show restore the signature
Smartphone (please complete the following information):
Main cause of crash
java.lang.ClassCastException: Cannot cast [Lse.warting.signaturecore.Event; to android.os.Parcelable at java.lang.Class.cast(Class.java:4183)
Crash log
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Class.isInterface()' on a null object reference
at java.lang.Class.isAssignableFrom(Class.java:589)
at android.os.Parcel.readParcelableCreatorInternal(Parcel.java:4900)
at android.os.Parcel.readParcelableInternal(Parcel.java:4807)
at android.os.Parcel.readParcelableArrayInternal(Parcel.java:5018)
at android.os.Parcel.readValue(Parcel.java:4645)
at android.os.Parcel.readValue(Parcel.java:4347)
at android.os.Parcel.-$$Nest$mreadValue()
at android.os.Parcel$LazyValue.apply(Parcel.java:4445)
at android.os.Parcel$LazyValue.apply(Parcel.java:4404)
at android.os.BaseBundle.getValueAt(BaseBundle.java:394)
at android.os.BaseBundle.getValue(BaseBundle.java:374)
at android.os.Bundle.getParcelableArray(Bundle.java:1020)
at se.warting.signatureview.views.SignaturePad.onRestoreInstanceState(SignaturePad.kt:66)
at android.view.View.dispatchRestoreInstanceState(View.java:21631)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4007)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4007)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4007)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4007)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:4007)
at android.view.View.restoreHierarchyState(View.java:21609)
at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:703)
at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:3181)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:3166)
at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:639)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:289)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:114)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1685)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3319)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3237)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:3167)
at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:639)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:289)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:114)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1685)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3319)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3237)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:350)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1543)
at android.app.Activity.performStart(Activity.java:8367)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3670)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:224)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:204)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2307)
at...
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.