├── library-lint ├── src │ ├── test │ │ ├── resources │ │ │ └── java │ │ │ │ ├── Empty.java │ │ │ │ ├── BaseClass.java │ │ │ │ ├── ValidActivityOtherMethodKt.kt │ │ │ │ ├── ValidActivityOtherMethod.java │ │ │ │ ├── InvalidActivityNoSaveKt.kt │ │ │ │ ├── InvalidActivityNoRestoreKt.kt │ │ │ │ ├── InvalidActivityNoRestoreDirectImportKt.kt │ │ │ │ ├── InvalidActivityNoRestore.java │ │ │ │ ├── InvalidActivityNoSave.java │ │ │ │ ├── ValidActivityKt.kt │ │ │ │ ├── stub │ │ │ │ └── StateSaver.java │ │ │ │ ├── InvalidActivityNoRestoreDirectImport.java │ │ │ │ ├── ValidActivity.java │ │ │ │ ├── InvalidActivityOtherMethodKt.kt │ │ │ │ ├── ValidActivityDirectImportKt.kt │ │ │ │ ├── InvalidActivityOtherMethod.java │ │ │ │ └── ValidActivityDirectImport.java │ │ └── kotlin │ │ │ └── com │ │ │ └── evernote │ │ │ └── android │ │ │ └── state │ │ │ └── lint │ │ │ ├── registry │ │ │ └── StateIssueRegistryTest.kt │ │ │ ├── AbstractDetectorTest.kt │ │ │ └── detectors │ │ │ └── NonMatchingStateSaverDetectorTest.kt │ └── main │ │ └── kotlin │ │ └── com │ │ └── evernote │ │ └── android │ │ └── state │ │ └── lint │ │ ├── registry │ │ └── StateIssueRegistry.kt │ │ └── detector │ │ └── AndroidStateDetector.kt └── build.gradle ├── gradle.properties ├── library ├── src │ ├── test │ │ ├── resources │ │ │ └── mockito-extensions │ │ │ │ └── org.mockito.plugins.MockMaker │ │ ├── java │ │ │ └── com │ │ │ │ └── evernote │ │ │ │ └── android │ │ │ │ └── state │ │ │ │ └── test │ │ │ │ ├── TestSimple.java │ │ │ │ ├── TestStatic.java │ │ │ │ ├── TestFinal.java │ │ │ │ ├── TestPrivateField.java │ │ │ │ ├── TestInnerClassGeneric.java │ │ │ │ ├── TestPrivateClass.java │ │ │ │ ├── TestTypeNotSupported.java │ │ │ │ ├── TestInnerClass.java │ │ │ │ ├── TestInnerClassExtension.java │ │ │ │ ├── TestProperty.java │ │ │ │ ├── TestPrivateProperty.java │ │ │ │ ├── GenericSerializable.java │ │ │ │ ├── TestInheritance.java │ │ │ │ ├── TestInheritanceGeneric.java │ │ │ │ ├── TestJavaGenericSerializable.java │ │ │ │ ├── TestJavaEnum.java │ │ │ │ ├── TestNested.java │ │ │ │ ├── TestPrivateClassBundler.java │ │ │ │ ├── TestPrivateClassBundlerData.java │ │ │ │ ├── TestView.java │ │ │ │ ├── TestTypesReflection.java │ │ │ │ ├── TestParcelableArray.java │ │ │ │ ├── TestBundler.java │ │ │ │ ├── TestJavaList.java │ │ │ │ ├── TestPrivateInnerClass.java │ │ │ │ ├── TestTypes.java │ │ │ │ └── TestTypesProperty.java │ │ └── kotlin │ │ │ └── com │ │ │ └── evernote │ │ │ └── android │ │ │ └── state │ │ │ ├── test │ │ │ ├── TestKotlinGenericSerializable.kt │ │ │ ├── TestKotlinInnerClass.kt │ │ │ ├── TestKotlinBoolean.kt │ │ │ ├── TestKotlinParcelableArray.kt │ │ │ ├── TestKotlinEnum.kt │ │ │ ├── TestKotlinBundler.kt │ │ │ ├── TestKotlinComparable.kt │ │ │ ├── TestKotlinList.kt │ │ │ ├── TestKotlinPrivateInnerClass.kt │ │ │ └── KotlinBundlingTest.kt │ │ │ └── StateSaverTest.kt │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── evernote │ │ │ └── android │ │ │ └── state │ │ │ ├── State.java │ │ │ ├── StateReflection.java │ │ │ ├── bundlers │ │ │ ├── BundlerListString.java │ │ │ ├── BundlerListInteger.java │ │ │ ├── BundlerListCharSequence.java │ │ │ └── BundlerListParcelable.java │ │ │ ├── Bundler.java │ │ │ ├── Injector.java │ │ │ ├── AndroidLifecycleCallbacks.java │ │ │ ├── StateSaver.java │ │ │ └── StateSaverImpl.java │ └── androidTest │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ └── com │ │ └── evernote │ │ └── android │ │ └── state │ │ └── test │ │ └── GlobalStateSaverTest.kt ├── gradle.properties ├── lint.xml ├── proguard.cfg └── build.gradle ├── demo ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ └── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── kotlin │ │ └── com │ │ │ └── evernote │ │ │ └── android │ │ │ └── state │ │ │ └── demo │ │ │ ├── App.kt │ │ │ └── MainActivity.kt │ │ ├── java │ │ └── com │ │ │ └── evernote │ │ │ └── android │ │ │ └── state │ │ │ └── test │ │ │ └── lint │ │ │ ├── LintFailingActivityKotlin.kt │ │ │ └── LintFailingActivityJava.java │ │ └── AndroidManifest.xml ├── lint.xml └── build.gradle ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── pmd │ └── pmd-ruleset.xml ├── findbugs │ └── findbugs-filter.xml ├── gradle-quality.gradle ├── checkstyle │ └── checkstyle.xml └── gradle-push.gradle ├── processor ├── gradle.properties ├── src │ └── main │ │ └── resources │ │ └── META-INF │ │ └── gradle │ │ └── incremental.annotation.processors └── build.gradle ├── demo-java-8 ├── lint.xml ├── src │ ├── main │ │ ├── res │ │ │ └── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── evernote │ │ │ ├── different │ │ │ ├── TestProguardHelper.java │ │ │ ├── TestProguard.java │ │ │ └── TestProguardBundler.java │ │ │ └── android │ │ │ └── state │ │ │ └── test │ │ │ └── java8 │ │ │ ├── TestNested.java │ │ │ ├── TestTypes.java │ │ │ └── TestTypesProperty.java │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── evernote │ │ │ └── android │ │ │ └── state │ │ │ └── test │ │ │ └── java8 │ │ │ └── ProguardTest.java │ └── test │ │ └── java │ │ └── com │ │ └── evernote │ │ └── android │ │ └── state │ │ └── test │ │ └── java8 │ │ └── Java8BundlingTest.java ├── proguard.cfg └── build.gradle ├── settings.gradle ├── .gitignore ├── gradlew.bat ├── CHANGELOG.md ├── gradlew └── README.md /library-lint/src/test/resources/java/Empty.java: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #VERSION_NAME=1.4.1 2 | VERSION_NAME=1.4.1-SNAPSHOT 3 | VERSION_CODE=1 -------------------------------------------------------------------------------- /library/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /library/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=android-state 2 | POM_NAME=Android State 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | State Demo 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evernote/android-state/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /processor/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=android-state-processor 2 | POM_NAME=Android State Processor 3 | POM_PACKAGING=jar -------------------------------------------------------------------------------- /demo/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /processor/src/main/resources/META-INF/gradle/incremental.annotation.processors: -------------------------------------------------------------------------------- 1 | com.evernote.android.state.StateProcessor,isolating -------------------------------------------------------------------------------- /demo-java-8/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /library/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo' 2 | include ':library-lint' 3 | include ':library' 4 | include ':processor' 5 | include ':demo-java-8' 6 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evernote/android-state/HEAD/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evernote/android-state/HEAD/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evernote/android-state/HEAD/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evernote/android-state/HEAD/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo-java-8/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Evernote/android-state/HEAD/demo-java-8/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestSimple.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestSimple { 6 | @State 7 | int field; 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestStatic.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import com.evernote.android.state.State; 4 | // 5 | //public class TestStatic { 6 | // @State 7 | // public static int test; 8 | //} 9 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestFinal.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import com.evernote.android.state.State; 4 | // 5 | //public class TestFinal { 6 | // @State 7 | // public final int test = 5; 8 | //} 9 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestPrivateField.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import com.evernote.android.state.State; 4 | // 5 | //public class TestPrivateField { 6 | // @State 7 | // private int test; 8 | //} 9 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestInnerClassGeneric.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestInnerClassGeneric

{ 6 | 7 | public class Inner { 8 | @State int field; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestPrivateClass.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import com.evernote.android.state.State; 4 | // 5 | //public class TestPrivateClass { 6 | // private static class Inner { 7 | // @State 8 | // public int test; 9 | // } 10 | //} 11 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestTypeNotSupported.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import com.evernote.android.state.State; 4 | // 5 | //public class TestTypeNotSupported { 6 | // @State 7 | // public Other test; 8 | // 9 | // public static final class Other {} 10 | //} 11 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestInnerClass.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | public abstract class TestInnerClass { 9 | public class Inner { 10 | @State int field; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinGenericSerializable.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import com.evernote.android.state.State 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | class TestKotlinGenericSerializable { 9 | 10 | @State 11 | var genericSerializable: GenericSerializable? = null 12 | } -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinInnerClass.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import java.io.Serializable 4 | 5 | /** 6 | * Copyright 2017 Evernote Corporation. All rights reserved. 7 | * 8 | * Created by rwondratschek on 3/16/17. 9 | */ 10 | class TestKotlinInnerClass { 11 | class Inner : Serializable 12 | } -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/BaseClass.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.lint; 2 | 3 | /** 4 | * Created by junchengc on 3/29/17. 5 | */ 6 | public class BaseClass { 7 | private boolean mBoolean; 8 | protected String mString; 9 | public static final boolean STATIC_BOOLEAN = true; 10 | public static final String STATIC_STRING = "some junk"; 11 | } 12 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestInnerClassExtension.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | public class TestInnerClassExtension extends TestInnerClass { 9 | public class Inner extends TestInnerClass.Inner { 10 | @State int fields; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinBoolean.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import com.evernote.android.state.State 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | class TestKotlinBoolean { 9 | 10 | @State 11 | var test1 = false 12 | 13 | @State 14 | var isTest2 = false 15 | 16 | @State 17 | var mTest3 = false 18 | } -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestProperty.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestProperty { 6 | @State 7 | private int test; 8 | 9 | public int getTest() { 10 | return test; 11 | } 12 | 13 | public void setTest(int test) { 14 | this.test = test; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gradle/pmd/pmd-ruleset.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | This ruleset checks my code for bad stuff 8 | 9 | -------------------------------------------------------------------------------- /demo/src/main/kotlin/com/evernote/android/state/demo/App.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.demo 2 | 3 | import android.app.Application 4 | 5 | import com.evernote.android.state.StateSaver 6 | 7 | /** 8 | * @author rwondratschek 9 | */ 10 | class App : Application() { 11 | override fun onCreate() { 12 | super.onCreate() 13 | StateSaver.setEnabledForAllActivitiesAndSupportFragments(this, true) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestPrivateProperty.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import com.evernote.android.state.State; 4 | // 5 | //public class TestPrivateProperty { 6 | // @State 7 | // private int test; 8 | // 9 | // private int getTest() { 10 | // return test; 11 | // } 12 | // 13 | // public void setTest(int test) { 14 | // this.test = test; 15 | // } 16 | //} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Android generated 2 | bin 3 | gen 4 | target 5 | out 6 | build 7 | 8 | #Eclipse 9 | .project 10 | .classpath 11 | .settings 12 | 13 | #IntelliJ IDEA 14 | .idea 15 | *.iml 16 | classes 17 | *.ipr 18 | *.iws 19 | gen-external-apklibs 20 | 21 | #Maven 22 | release.properties 23 | pom.xml.* 24 | 25 | #Ant 26 | build.xml 27 | ant.properties 28 | local.properties 29 | 30 | #Gradle 31 | .gradle 32 | 33 | #Command line 34 | proguard-project.txt 35 | .DS_Store 36 | .tmp -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/GenericSerializable.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | public class GenericSerializable implements Serializable { 9 | 10 | private T mValue; 11 | 12 | public T getValue() { 13 | return mValue; 14 | } 15 | 16 | public void setValue(T value) { 17 | mValue = value; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /library-lint/src/main/kotlin/com/evernote/android/state/lint/registry/StateIssueRegistry.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.lint.registry 2 | 3 | import com.android.tools.lint.client.api.IssueRegistry 4 | import com.evernote.android.state.lint.detector.AndroidStateDetector 5 | 6 | class StateIssueRegistry : IssueRegistry() { 7 | override val issues = listOf(AndroidStateDetector.ISSUE) 8 | override val api: Int = com.android.tools.lint.detector.api.CURRENT_API 9 | } 10 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestInheritance.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestInheritance { 6 | 7 | @State 8 | public int mValue1; 9 | 10 | public static class InheritanceLevel1 extends TestInheritance { 11 | 12 | } 13 | 14 | public static class InheritanceLevel2 extends InheritanceLevel1 { 15 | 16 | @State 17 | public int mValue2; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demo-java-8/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /library/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestInheritanceGeneric.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestInheritanceGeneric

{ 6 | 7 | @State 8 | public int mValue1; 9 | 10 | public P mGenericValue; 11 | 12 | public static class InheritanceLevelGeneric1

extends TestInheritanceGeneric

{ 13 | 14 | } 15 | 16 | public static class InheritanceLevelGeneric2 extends InheritanceLevelGeneric1 { 17 | 18 | @State 19 | public int mValue2; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo-java-8/src/main/java/com/evernote/different/TestProguardHelper.java: -------------------------------------------------------------------------------- 1 | package com.evernote.different; 2 | 3 | import androidx.annotation.Keep; 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | public final class TestProguardHelper { 9 | 10 | private TestProguardHelper() { 11 | // no op 12 | } 13 | 14 | @Keep 15 | public static void setValue(TestProguard instance, int value) { 16 | instance.setValue(value); 17 | } 18 | 19 | @Keep 20 | public static void verifyValue(TestProguard instance, int value) { 21 | instance.verifyValue(value); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /demo/src/main/java/com/evernote/android/state/test/lint/LintFailingActivityKotlin.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test.lint 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Activity 5 | import android.os.Bundle 6 | 7 | import com.evernote.android.state.StateSaver 8 | 9 | /** 10 | * @author rwondratschek 11 | */ 12 | class LintFailingActivityKotlin : Activity() { 13 | 14 | @SuppressLint("NonMatchingStateSaverCalls") 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | StateSaver.restoreInstanceState(this, savedInstanceState) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestJavaGenericSerializable.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | /** 6 | * @author rwondratschek 7 | */ 8 | public class TestJavaGenericSerializable { 9 | 10 | @State 11 | private GenericSerializable mGenericSerializable; 12 | 13 | public GenericSerializable getGenericSerializable() { 14 | return mGenericSerializable; 15 | } 16 | 17 | public void setGenericSerializable(GenericSerializable genericSerializable) { 18 | mGenericSerializable = genericSerializable; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /library/proguard.cfg: -------------------------------------------------------------------------------- 1 | -dontwarn com.evernote.android.state.** 2 | -keep class com.evernote.android.state.** { *; } 3 | -keep class **$$StateSaver { *; } 4 | 5 | # generated class names 6 | -keepclasseswithmembernames class * { @com.evernote.android.state.State ;} 7 | -keepclasseswithmembernames class * { @com.evernote.android.state.StateReflection ;} 8 | 9 | # only keep non-private fields, there must be a getter / setter for private fields 10 | #-keepclassmembers class * { 11 | # @com.evernote.android.state.State !private ; 12 | #} 13 | 14 | # with reflection always keep the name of the field 15 | -keepclassmembers class * { @com.evernote.android.state.StateReflection ;} -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestJavaEnum.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | import com.evernote.android.state.StateReflection; 5 | 6 | /** 7 | * @author rwondratschek 8 | */ 9 | public class TestJavaEnum { 10 | 11 | public enum JavaEnum { 12 | LEFT, RIGHT 13 | } 14 | 15 | @State 16 | JavaEnum mJavaEnum = JavaEnum.LEFT; 17 | 18 | @StateReflection 19 | private JavaEnum mJavaEnum1 = JavaEnum.LEFT; 20 | 21 | public JavaEnum getJavaEnum1() { 22 | return mJavaEnum1; 23 | } 24 | 25 | public void setJavaEnum1(JavaEnum javaEnum1) { 26 | mJavaEnum1 = javaEnum1; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/src/main/java/com/evernote/android/state/test/lint/LintFailingActivityJava.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test.lint; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.os.Bundle; 6 | import androidx.annotation.Nullable; 7 | 8 | import com.evernote.android.state.StateSaver; 9 | 10 | /** 11 | * @author rwondratschek 12 | */ 13 | public class LintFailingActivityJava extends Activity { 14 | 15 | @SuppressLint("NonMatchingStateSaverCalls") 16 | @Override 17 | protected void onCreate(@Nullable Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | StateSaver.restoreInstanceState(this, savedInstanceState); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /library-lint/src/test/kotlin/com/evernote/android/state/lint/registry/StateIssueRegistryTest.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.lint.registry 2 | 3 | import com.evernote.android.state.lint.detector.AndroidStateDetector 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.Test 6 | 7 | 8 | /** 9 | * Test the [StateIssueRegistry]. 10 | */ 11 | class StateIssueRegistryTest { 12 | 13 | /** 14 | * Test that the issue registry contains the correct number of issues. 15 | */ 16 | @Test 17 | fun testNumberOfIssues() { 18 | assertThat(StateIssueRegistry().issues).hasSize(1) 19 | } 20 | 21 | /** 22 | * Test that the issue registry contains the correct issues. 23 | */ 24 | @Test 25 | fun testGetIssues() { 26 | assertThat(StateIssueRegistry().issues).contains(AndroidStateDetector.ISSUE) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestNested.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestNested { 6 | @State 7 | public int test; 8 | 9 | public static class Inner1 { 10 | @State 11 | public int test; 12 | 13 | public static class InnerInner1 { 14 | @State 15 | public int test; 16 | } 17 | public static class InnerInner2 { 18 | @State 19 | public int test; 20 | } 21 | } 22 | public static class Inner2 { 23 | @State 24 | public int test; 25 | 26 | public static class InnerInner1 { 27 | @State 28 | public int test; 29 | } 30 | public static class InnerInner2 { 31 | @State 32 | public int test; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo-java-8/src/main/java/com/evernote/android/state/test/java8/TestNested.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test.java8; 2 | 3 | import com.evernote.android.state.State; 4 | 5 | public class TestNested { 6 | @State 7 | public int test; 8 | 9 | public static class Inner1 { 10 | @State 11 | public int test; 12 | 13 | public static class InnerInner1 { 14 | @State 15 | public int test; 16 | } 17 | public static class InnerInner2 { 18 | @State 19 | public int test; 20 | } 21 | } 22 | public static class Inner2 { 23 | @State 24 | public int test; 25 | 26 | public static class InnerInner1 { 27 | @State 28 | public int test; 29 | } 30 | public static class InnerInner2 { 31 | @State 32 | public int test; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinParcelableArray.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import com.evernote.android.state.State 4 | import com.evernote.android.state.StateReflection 5 | 6 | /** 7 | * @author rwondratschek 8 | */ 9 | class TestKotlinParcelableArray { 10 | @State 11 | var parcelableArrayImpl1: Array = arrayOf(TestTypes.ParcelableImpl(0)) 12 | 13 | @StateReflection 14 | private var mParcelableArrayImpl2: Array = arrayOf(TestTypes.ParcelableImpl(0)) 15 | 16 | fun setToValue(value: Int) { 17 | parcelableArrayImpl1 = arrayOf(TestTypes.ParcelableImpl(value)) 18 | mParcelableArrayImpl2 = arrayOf(TestTypes.ParcelableImpl(value)) 19 | } 20 | 21 | fun isValue(value: Int): Boolean { 22 | return parcelableArrayImpl1[0].isValue(value) && mParcelableArrayImpl2[0].isValue(value) 23 | } 24 | } -------------------------------------------------------------------------------- /demo-java-8/proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 3 2 | -repackageclasses 3 | -dontusemixedcaseclassnames 4 | -dontskipnonpubliclibraryclasses 5 | -dontskipnonpubliclibraryclassmembers 6 | -dontpreverify 7 | -verbose 8 | -dontwarn 9 | -ignorewarnings 10 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*, !method/inlining/unique, !method/inlining/short 11 | 12 | -printconfiguration "build/outputs/mapping/configuration.txt" 13 | 14 | # Proguard rules that are applied to your test apk/code. 15 | -keepattributes *Annotation* 16 | 17 | -keep class junit.framework.** { *; } 18 | -keep class junit.runner.** { *; } 19 | 20 | -keep class android.test.** { *; } 21 | -keep class android.support.test.** { *; } 22 | -keep class org.junit.** { *; } 23 | -keep class org.hamcrest.** { *; } 24 | -keep class com.squareup.javawriter.JavaWriter { *; } 25 | -keep class com.google.devtools.** { *; } 26 | # Uncomment this if you use Mockito 27 | #-dontwarn org.mockito.** -------------------------------------------------------------------------------- /gradle/findbugs/findbugs-filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /processor/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'com.github.ben-manes.versions' 3 | apply from: "$rootDir/gradle/gradle-quality.gradle" 4 | 5 | sourceCompatibility = JavaVersion.VERSION_1_7 6 | targetCompatibility = JavaVersion.VERSION_1_7 7 | 8 | archivesBaseName = 'android-state-processor' 9 | 10 | test.dependsOn ":library:bundleReleaseAar" 11 | 12 | dependencies { 13 | implementation 'com.squareup:javapoet:1.11.1' 14 | 15 | compileOnly 'com.google.auto.service:auto-service:1.0-rc4' 16 | 17 | testImplementation 'com.google.android:android:4.1.1.4' 18 | testImplementation files('../library/build/intermediates/packaged-classes/release/classes.jar') 19 | 20 | testImplementation "junit:junit:$junitVersion" 21 | testImplementation 'com.google.testing.compile:compile-testing:0.15' 22 | testImplementation files(org.gradle.internal.jvm.Jvm.current().getToolsJar()) 23 | } 24 | 25 | jar { 26 | destinationDir = new File("$project.buildDir/outputs/jar/") 27 | } 28 | 29 | apply from: "$rootDir/gradle/gradle-push.gradle" -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/ValidActivityOtherMethodKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | class ValidActivityOtherMethodKt : Activity() { 21 | protected override fun onCreate(savedInstanceState: Bundle) { 22 | super.onCreate(savedInstanceState) 23 | restoreInstanceState() 24 | } 25 | 26 | private fun restoreInstanceState() { 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/ValidActivityOtherMethod.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | import com.evernote.android.state.StateSaver; 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | public class ValidActivityOtherMethod extends Activity { 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | restoreInstanceState(); 25 | } 26 | 27 | private void restoreInstanceState() { 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityNoSaveKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | class InvalidActivityNoSaveKt : Activity() { 21 | override fun onCreate(savedInstanceState: Bundle) { 22 | super.onCreate(savedInstanceState) 23 | StateSaver.restoreInstanceState(this, savedInstanceState) 24 | } 25 | 26 | override fun onSaveInstanceState(outState: Bundle) { 27 | super.onSaveInstanceState(outState) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityNoRestoreKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | class InvalidActivityNoRestoreKt : Activity() { 21 | protected override fun onCreate(savedInstanceState: Bundle) { 22 | super.onCreate(savedInstanceState) 23 | } 24 | 25 | protected override fun onSaveInstanceState(outState: Bundle) { 26 | super.onSaveInstanceState(outState) 27 | StateSaver.saveInstanceState(this, outState) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityNoRestoreDirectImportKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver.saveInstanceState 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | class InvalidActivityNoRestoreDirectImportKt : Activity() { 21 | protected override fun onCreate(savedInstanceState: Bundle) { 22 | super.onCreate(savedInstanceState) 23 | } 24 | 25 | protected override fun onSaveInstanceState(outState: Bundle) { 26 | super.onSaveInstanceState(outState) 27 | saveInstanceState(this, outState) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityNoRestore.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | import com.evernote.android.state.StateSaver; 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | public class InvalidActivityNoRestore extends Activity { 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | } 25 | 26 | @Override 27 | protected void onSaveInstanceState(Bundle outState) { 28 | super.onSaveInstanceState(outState); 29 | StateSaver.saveInstanceState(this, outState); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityNoSave.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | import com.evernote.android.state.StateSaver; 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | public class InvalidActivityNoSave extends Activity { 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | StateSaver.restoreInstanceState(this, savedInstanceState); 25 | } 26 | 27 | @Override 28 | protected void onSaveInstanceState(Bundle outState) { 29 | super.onSaveInstanceState(outState); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/ValidActivityKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | class ValidActivityKt : Activity() { 21 | protected override fun onCreate(savedInstanceState: Bundle) { 22 | super.onCreate(savedInstanceState) 23 | StateSaver.restoreInstanceState(this, savedInstanceState) 24 | } 25 | 26 | protected override fun onSaveInstanceState(outState: Bundle) { 27 | super.onSaveInstanceState(outState) 28 | StateSaver.saveInstanceState(this, outState) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/stub/StateSaver.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.os.Bundle; 15 | 16 | /** 17 | * Entry point to save and restore objects. 18 | */ 19 | @SuppressWarnings({"WeakerAccess", "unused"}) 20 | public final class StateSaver { 21 | public static void saveInstanceState(T target, Bundle state) { 22 | // STUB 23 | } 24 | public static void restoreInstanceState(T target, Bundle state) { 25 | // STUB 26 | } 27 | 28 | private StateSaver() { 29 | throw new UnsupportedOperationException(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityNoRestoreDirectImport.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | 16 | import com.evernote.android.state.StateSaver.saveInstanceState; 17 | 18 | /** 19 | * @author rwondratschek 20 | */ 21 | public class InvalidActivityNoRestoreDirectImport extends Activity { 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | } 26 | 27 | @Override 28 | protected void onSaveInstanceState(Bundle outState) { 29 | super.onSaveInstanceState(outState); 30 | saveInstanceState(this, outState); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/ValidActivity.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | import com.evernote.android.state.StateSaver; 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | public class ValidActivity extends Activity { 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | StateSaver.restoreInstanceState(this, savedInstanceState); 25 | } 26 | 27 | @Override 28 | protected void onSaveInstanceState(Bundle outState) { 29 | super.onSaveInstanceState(outState); 30 | StateSaver.saveInstanceState(this, outState); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/State.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.os.Bundle; 15 | 16 | import java.lang.annotation.ElementType; 17 | import java.lang.annotation.Retention; 18 | import java.lang.annotation.RetentionPolicy; 19 | import java.lang.annotation.Target; 20 | 21 | /** 22 | * Fields or properties with this annotation will be saved in a {@link Bundle} and restored 23 | * from it while using the {@link StateSaver}. 24 | */ 25 | @Target(ElementType.FIELD) 26 | @Retention(RetentionPolicy.CLASS) 27 | public @interface State { 28 | Class value() default Bundler.class; 29 | } 30 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityOtherMethodKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | class InvalidActivityOtherMethodKt : Activity() { 21 | protected override fun onCreate(savedInstanceState: Bundle) { 22 | super.onCreate(savedInstanceState) 23 | restoreInstanceState() 24 | } 25 | 26 | protected override fun onSaveInstanceState(outState: Bundle) { 27 | super.onSaveInstanceState(outState) 28 | StateSaver.saveInstanceState(this, outState) 29 | } 30 | 31 | private fun restoreInstanceState() { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestPrivateClassBundler.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import android.os.Bundle; 4 | // 5 | //import com.evernote.android.state.Bundler; 6 | //import com.evernote.android.state.State; 7 | // 8 | //public class TestPrivateClassBundler { 9 | // @State(MyBundler.class) 10 | // private Data mData2; 11 | // 12 | // public Data getData2() { 13 | // return mData2; 14 | // } 15 | // 16 | // public void setData2(Data data2) { 17 | // mData2 = data2; 18 | // } 19 | // 20 | // public static final class Data { 21 | // private int int1; 22 | // private int int2; 23 | // } 24 | // 25 | // private static final class MyBundler implements Bundler { 26 | // @Override 27 | // public void put(String key, Data value, Bundle bundle) { 28 | // bundle.putInt(key + "1", value.int1); 29 | // bundle.putInt(key + "2", value.int2); 30 | // } 31 | // 32 | // @Override 33 | // public Data get(String key, Bundle bundle) { 34 | // Data data = new Data(); 35 | // data.int1 = bundle.getInt(key + "1"); 36 | // data.int2 = bundle.getInt(key + "2"); 37 | // return data; 38 | // } 39 | // } 40 | //} 41 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinEnum.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.test 12 | 13 | import com.evernote.android.state.State 14 | import com.evernote.android.state.StateReflection 15 | 16 | /** 17 | * Copyright 2017 Evernote Corporation. All rights reserved. 18 | * 19 | * Created by rwondratschek on 2/13/17. 20 | */ 21 | enum class KotlinEnum { 22 | LEFT, RIGHT 23 | } 24 | 25 | class TestKotlinEnum { 26 | @State 27 | var kotlinEnum = KotlinEnum.LEFT 28 | 29 | @StateReflection 30 | private var kotlinEnum1 = KotlinEnum.LEFT 31 | 32 | fun getKotlinEnum1(): KotlinEnum { 33 | return kotlinEnum1 34 | } 35 | 36 | fun setKotlinEnum1(kotlinEnum: KotlinEnum) { 37 | kotlinEnum1 = kotlinEnum 38 | } 39 | } -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestPrivateClassBundlerData.java: -------------------------------------------------------------------------------- 1 | //package com.evernote.android.state.test; 2 | // 3 | //import android.os.Bundle; 4 | // 5 | //import com.evernote.android.state.Bundler; 6 | //import com.evernote.android.state.State; 7 | // 8 | //public class TestPrivateClassBundlerData { 9 | // @State(MyBundler.class) 10 | // private Data mData2; 11 | // 12 | // public Data getData2() { 13 | // return mData2; 14 | // } 15 | // 16 | // public void setData2(Data data2) { 17 | // mData2 = data2; 18 | // } 19 | // 20 | // private static final class Data { 21 | // private int int1; 22 | // private int int2; 23 | // } 24 | // 25 | // public static final class MyBundler implements Bundler { 26 | // @Override 27 | // public void put(String key, Data value, Bundle bundle) { 28 | // bundle.putInt(key + "1", value.int1); 29 | // bundle.putInt(key + "2", value.int2); 30 | // } 31 | // 32 | // @Override 33 | // public Data get(String key, Bundle bundle) { 34 | // Data data = new Data(); 35 | // data.int1 = bundle.getInt(key + "1"); 36 | // data.int2 = bundle.getInt(key + "2"); 37 | // return data; 38 | // } 39 | // } 40 | //} 41 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/ValidActivityDirectImportKt.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint 12 | 13 | import android.app.Activity 14 | import android.os.Bundle 15 | import com.evernote.android.state.StateSaver.saveInstanceState 16 | import com.evernote.android.state.StateSaver.restoreInstanceState 17 | 18 | /** 19 | * @author rwondratschek 20 | */ 21 | class ValidActivityDirectImportKt : Activity() { 22 | protected override fun onCreate(savedInstanceState: Bundle) { 23 | super.onCreate(savedInstanceState) 24 | restoreInstanceState(this, savedInstanceState) 25 | } 26 | 27 | protected override fun onSaveInstanceState(outState: Bundle) { 28 | super.onSaveInstanceState(outState) 29 | saveInstanceState(this, outState) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/InvalidActivityOtherMethod.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | import com.evernote.android.state.StateSaver; 16 | 17 | /** 18 | * @author rwondratschek 19 | */ 20 | public class InvalidActivityOtherMethod extends Activity { 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | restoreInstanceState(); 25 | } 26 | 27 | @Override 28 | protected void onSaveInstanceState(Bundle outState) { 29 | super.onSaveInstanceState(outState); 30 | StateSaver.saveInstanceState(this, outState); 31 | } 32 | 33 | private void restoreInstanceState() { 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /demo-java-8/src/main/java/com/evernote/different/TestProguard.java: -------------------------------------------------------------------------------- 1 | package com.evernote.different; 2 | // different package to avoid clash with Proguard rules 3 | 4 | import com.evernote.android.state.State; 5 | import com.evernote.android.state.StateReflection; 6 | 7 | /** 8 | * @author rwondratschek 9 | */ 10 | public class TestProguard { 11 | 12 | @State 13 | public int test1; 14 | 15 | @State 16 | private int test2; 17 | 18 | @StateReflection 19 | private int test3; 20 | 21 | public int getTest2() { 22 | return test2; 23 | } 24 | 25 | public void setTest2(int test2) { 26 | this.test2 = test2; 27 | } 28 | 29 | public void setValue(int value) { 30 | test1 = value; 31 | setTest2(value); 32 | test3 = value; 33 | } 34 | 35 | public void verifyValue(int value) { 36 | if (test1 != value) { 37 | throw new IllegalStateException("test1 different, expected " + value + ", is " + test1); 38 | } 39 | if (getTest2() != value) { 40 | throw new IllegalStateException("getTest2() different, expected " + value + ", is " + getTest2()); 41 | } 42 | if (test3 != value) { 43 | throw new IllegalStateException("test3 different, expected " + value + ", is " + test3); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /library-lint/src/test/resources/java/ValidActivityDirectImport.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.lint; 12 | 13 | import android.app.Activity; 14 | import android.os.Bundle; 15 | 16 | import static com.evernote.android.state.StateSaver.restoreInstanceState; 17 | import static com.evernote.android.state.StateSaver.saveInstanceState; 18 | 19 | /** 20 | * @author rwondratschek 21 | */ 22 | public class ValidActivityDirectImport extends Activity { 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | restoreInstanceState(this, savedInstanceState); 27 | } 28 | 29 | @Override 30 | protected void onSaveInstanceState(Bundle outState) { 31 | super.onSaveInstanceState(outState); 32 | saveInstanceState(this, outState); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinBundler.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import android.os.Bundle 4 | import com.evernote.android.state.Bundler 5 | import com.evernote.android.state.State 6 | import java.io.Serializable 7 | 8 | /** 9 | * @author rwondratschek 10 | */ 11 | class MyClass { 12 | @State(value = WrapperBundler::class) 13 | var wrapped = Wrapper(42) 14 | 15 | @State(value = WrapperBundlerGeneric::class) 16 | var wrappedGeneric = Wrapper(42) 17 | } 18 | 19 | class Wrapper(val content: T?) 20 | 21 | class WrapperBundler : Bundler> { 22 | override fun get(key: String, bundle: Bundle): Wrapper? { 23 | @Suppress("UNCHECKED_CAST") 24 | return Wrapper(bundle.getSerializable(key) as Serializable) 25 | } 26 | 27 | override fun put(key: String, value: Wrapper, bundle: Bundle) { 28 | bundle.putSerializable(key, value.content) 29 | } 30 | } 31 | 32 | class WrapperBundlerGeneric : Bundler> { 33 | override fun get(key: String, bundle: Bundle): Wrapper? { 34 | @Suppress("UNCHECKED_CAST") 35 | return Wrapper(bundle.getSerializable(key) as T) 36 | } 37 | 38 | override fun put(key: String, value: Wrapper, bundle: Bundle) { 39 | bundle.putSerializable(key, value.content) 40 | } 41 | } -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-kapt' 4 | apply plugin: 'com.github.ben-manes.versions' 5 | apply from: "$rootDir/gradle/gradle-quality.gradle" 6 | 7 | android { 8 | compileSdkVersion rootProject.ext.compileSdkVersion 9 | buildToolsVersion rootProject.ext.buildToolsVersion 10 | 11 | defaultConfig { 12 | applicationId "com.evernote.android.state.demo" 13 | 14 | minSdkVersion rootProject.ext.minSdkVersion 15 | targetSdkVersion rootProject.ext.targetSdkVersion 16 | 17 | versionName project.VERSION_NAME 18 | versionCode Integer.parseInt(project.VERSION_CODE) 19 | } 20 | 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_7 23 | targetCompatibility JavaVersion.VERSION_1_7 24 | } 25 | 26 | sourceSets { 27 | main.java.srcDirs += 'src/main/kotlin' 28 | test.java.srcDirs += 'src/test/kotlin' 29 | androidTest.java.srcDirs += 'src/androidTest/kotlin' 30 | } 31 | 32 | lintOptions { 33 | abortOnError true 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation project(':library') 39 | kapt project(':processor') 40 | implementation "androidx.appcompat:appcompat:1.0.0" 41 | 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 43 | } 44 | 45 | uploadArchives.enabled false 46 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/StateReflection.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.os.Bundle; 15 | 16 | import java.lang.annotation.ElementType; 17 | import java.lang.annotation.Retention; 18 | import java.lang.annotation.RetentionPolicy; 19 | import java.lang.annotation.Target; 20 | 21 | /** 22 | * Fields or properties with this annotation will be saved in a {@link Bundle} and restored 23 | * from it while using the {@link StateSaver}. Compared to {@link State} this strategy relies 24 | * on reflection. If you can, you should use {@link State} instead. 25 | */ 26 | @Target(ElementType.FIELD) 27 | @Retention(RetentionPolicy.CLASS) 28 | public @interface StateReflection { 29 | Class value() default Bundler.class; 30 | } 31 | -------------------------------------------------------------------------------- /library-lint/src/test/kotlin/com/evernote/android/state/lint/AbstractDetectorTest.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.lint 2 | 3 | import com.android.tools.lint.checks.infrastructure.LintDetectorTest 4 | import com.android.utils.SdkUtils 5 | import junit.framework.AssertionFailedError 6 | import java.io.File 7 | import java.io.InputStream 8 | 9 | abstract class AbstractDetectorTest : LintDetectorTest() { 10 | override fun getTestResource(relativePath: String, expectExists: Boolean): InputStream { 11 | val path = "resources/test/java/$relativePath".replace('/', File.separatorChar) 12 | return File(testDataRootDir, path).inputStream() 13 | } 14 | 15 | private val testDataRootDir: File 16 | get() { 17 | val source = javaClass.protectionDomain.codeSource 18 | val location = source.location 19 | val classesDir = SdkUtils.urlToFile(location) 20 | return classesDir.parentFile.absoluteFile.parentFile.parentFile 21 | } 22 | 23 | protected fun getTestFile(file: String) = TestFile().from(file, this).to(file)!! 24 | 25 | override fun tearDown() { 26 | try { 27 | super.tearDown() 28 | } catch (e: AssertionFailedError) { 29 | // this is weird 🤷 30 | if (e.message != "There was a crash during lint execution; consult log for details") { 31 | throw e 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestView.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import android.content.Context; 4 | import android.os.Parcelable; 5 | import android.view.View; 6 | 7 | import com.evernote.android.state.State; 8 | import com.evernote.android.state.StateSaver; 9 | 10 | public class TestView extends View { 11 | 12 | @State 13 | public int mState; 14 | 15 | public TestView(Context context) { 16 | super(context); 17 | } 18 | 19 | @Override 20 | protected Parcelable onSaveInstanceState() { 21 | return StateSaver.saveInstanceState(this, super.onSaveInstanceState()); 22 | } 23 | 24 | @Override 25 | protected void onRestoreInstanceState(Parcelable state) { 26 | super.onRestoreInstanceState(StateSaver.restoreInstanceState(this, state)); 27 | } 28 | 29 | public static class InnerView extends TestView { 30 | 31 | @State 32 | public int mStateInner; 33 | 34 | public InnerView(Context context) { 35 | super(context); 36 | } 37 | 38 | @Override 39 | protected Parcelable onSaveInstanceState() { 40 | return StateSaver.saveInstanceState(this, super.onSaveInstanceState()); 41 | } 42 | 43 | @Override 44 | protected void onRestoreInstanceState(Parcelable state) { 45 | super.onRestoreInstanceState(StateSaver.restoreInstanceState(this, state)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/bundlers/BundlerListString.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.bundlers; 12 | 13 | import android.os.Bundle; 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | 17 | import com.evernote.android.state.Bundler; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * @author rwondratschek 24 | */ 25 | public class BundlerListString implements Bundler> { 26 | @Override 27 | public void put(@NonNull String key, @NonNull List value, @NonNull Bundle bundle) { 28 | ArrayList arrayList = value instanceof ArrayList ? (ArrayList) value : new ArrayList<>(value); 29 | bundle.putStringArrayList(key, arrayList); 30 | } 31 | 32 | @Nullable 33 | @Override 34 | public List get(@NonNull String key, @NonNull Bundle bundle) { 35 | return bundle.getStringArrayList(key); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/bundlers/BundlerListInteger.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.bundlers; 12 | 13 | import android.os.Bundle; 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | 17 | import com.evernote.android.state.Bundler; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * @author rwondratschek 24 | */ 25 | public class BundlerListInteger implements Bundler> { 26 | @Override 27 | public void put(@NonNull String key, @NonNull List value, @NonNull Bundle bundle) { 28 | ArrayList arrayList = value instanceof ArrayList ? (ArrayList) value : new ArrayList<>(value); 29 | bundle.putIntegerArrayList(key, arrayList); 30 | } 31 | 32 | @Nullable 33 | @Override 34 | public List get(@NonNull String key, @NonNull Bundle bundle) { 35 | return bundle.getIntegerArrayList(key); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinComparable.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("unused") 2 | 3 | package com.evernote.android.state.test 4 | 5 | import com.evernote.android.state.State 6 | import java.io.Serializable 7 | import java.util.* 8 | 9 | /** 10 | * Copyright 2017 Evernote Corporation. All rights reserved. 11 | * 12 | * Created by rwondratschek on 3/1/17. 13 | */ 14 | class MyComparator : Comparator, Serializable { 15 | override fun compare(o1: MyComparable, o2: MyComparable): Int = 0 16 | } 17 | 18 | class MyComparable : Comparable, Serializable { 19 | override fun compareTo(other: MyComparable): Int = 0 20 | } 21 | 22 | interface MyInterface> { 23 | fun doSomething(other: T) 24 | } 25 | 26 | class MyInterfaceImpl : MyInterface, Serializable { 27 | override fun doSomething(other: MyInterfaceImpl) {} 28 | } 29 | 30 | interface MySerializableInterface> : Serializable { 31 | fun doSomething(other: T) 32 | } 33 | 34 | class MySerializableImpl : MySerializableInterface { 35 | override fun doSomething(other: MySerializableImpl) {} 36 | } 37 | 38 | class TestKotlinComparable { 39 | 40 | @State 41 | var comparator: MyComparator? = null 42 | 43 | @State 44 | var comparable: MyComparable? = null 45 | 46 | @State 47 | var myInterface: MyInterfaceImpl? = null 48 | 49 | @State 50 | var mySerializableInterface: MySerializableImpl? = null 51 | } -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/bundlers/BundlerListCharSequence.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.bundlers; 12 | 13 | import android.os.Bundle; 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | 17 | import com.evernote.android.state.Bundler; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * @author rwondratschek 24 | */ 25 | public class BundlerListCharSequence implements Bundler> { 26 | @Override 27 | public void put(@NonNull String key, @NonNull List value, @NonNull Bundle bundle) { 28 | ArrayList arrayList = value instanceof ArrayList ? (ArrayList) value : new ArrayList<>(value); 29 | bundle.putCharSequenceArrayList(key, arrayList); 30 | } 31 | 32 | @Nullable 33 | @Override 34 | public List get(@NonNull String key, @NonNull Bundle bundle) { 35 | return bundle.getCharSequenceArrayList(key); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestTypesReflection.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcelable; 5 | import android.util.SparseArray; 6 | 7 | import com.evernote.android.state.StateReflection; 8 | 9 | import java.util.ArrayList; 10 | 11 | public class TestTypesReflection { 12 | @StateReflection 13 | private int mInt; 14 | @StateReflection 15 | private int[] mIntArray; 16 | @StateReflection 17 | private Integer mIntegerObj; 18 | @StateReflection 19 | private Bundle mBundle; 20 | @StateReflection 21 | private Parcelable[] mParcelableArray; 22 | @StateReflection 23 | private ArrayList mParcelableArrayList; 24 | @StateReflection 25 | private SparseArray mParcelableSparseArray; 26 | 27 | public int getInt() { 28 | return mInt; 29 | } 30 | 31 | public void setInt(int anInt) { 32 | mInt = anInt; 33 | } 34 | 35 | public Integer getIntegerObj() { 36 | return mIntegerObj; 37 | } 38 | 39 | public void setIntegerObj(Integer integerObj) { 40 | mIntegerObj = integerObj; 41 | } 42 | 43 | public ArrayList getParcelableArrayList() { 44 | return mParcelableArrayList; 45 | } 46 | 47 | public void setParcelableArrayList(ArrayList parcelableArrayList) { 48 | mParcelableArrayList = parcelableArrayList; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestParcelableArray.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import com.evernote.android.state.State; 4 | import com.evernote.android.state.StateReflection; 5 | 6 | /** 7 | * @author rwondratschek 8 | */ 9 | public class TestParcelableArray { 10 | @State 11 | public TestTypes.ParcelableImpl[] mParcelableArrayImpl1 = new TestTypes.ParcelableImpl[]{new TestTypes.ParcelableImpl(0)}; 12 | 13 | @State 14 | private TestTypes.ParcelableImpl[] mParcelableArrayImpl2 = new TestTypes.ParcelableImpl[]{new TestTypes.ParcelableImpl(0)}; 15 | 16 | @StateReflection 17 | private TestTypes.ParcelableImpl[] mParcelableArrayImpl3 = new TestTypes.ParcelableImpl[]{new TestTypes.ParcelableImpl(0)}; 18 | 19 | public TestTypes.ParcelableImpl[] getParcelableArrayImpl2() { 20 | return mParcelableArrayImpl2; 21 | } 22 | 23 | public void setParcelableArrayImpl2(TestTypes.ParcelableImpl[] parcelableArrayImpl2) { 24 | mParcelableArrayImpl2 = parcelableArrayImpl2; 25 | } 26 | 27 | public void setToValue(int value) { 28 | mParcelableArrayImpl1 = new TestTypes.ParcelableImpl[]{new TestTypes.ParcelableImpl(value)}; 29 | mParcelableArrayImpl2 = new TestTypes.ParcelableImpl[]{new TestTypes.ParcelableImpl(value)}; 30 | mParcelableArrayImpl3 = new TestTypes.ParcelableImpl[]{new TestTypes.ParcelableImpl(value)}; 31 | } 32 | 33 | public boolean isValue(int value) { 34 | return mParcelableArrayImpl1[0].isValue(value) && mParcelableArrayImpl2[0].isValue(value) && mParcelableArrayImpl3[0].isValue(value); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/bundlers/BundlerListParcelable.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.bundlers; 12 | 13 | import android.os.Bundle; 14 | import android.os.Parcelable; 15 | import androidx.annotation.NonNull; 16 | import androidx.annotation.Nullable; 17 | 18 | import com.evernote.android.state.Bundler; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * @author rwondratschek 25 | */ 26 | public class BundlerListParcelable implements Bundler> { 27 | @SuppressWarnings("unchecked") 28 | @Override 29 | public void put(@NonNull String key, @NonNull List value, @NonNull Bundle bundle) { 30 | ArrayList arrayList = value instanceof ArrayList ? (ArrayList) value : new ArrayList<>(value); 31 | bundle.putParcelableArrayList(key, arrayList); 32 | } 33 | 34 | @Nullable 35 | @Override 36 | public List get(@NonNull String key, @NonNull Bundle bundle) { 37 | return bundle.getParcelableArrayList(key); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /library-lint/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | apply plugin: 'kotlin' 3 | apply from: "$rootDir/gradle/gradle-quality.gradle" 4 | 5 | archivesBaseName = 'android-state-lint' 6 | 7 | sourceCompatibility = JavaVersion.VERSION_1_7 8 | targetCompatibility = JavaVersion.VERSION_1_7 9 | 10 | dependencies { 11 | // Lint dependencies 12 | compileOnly "com.android.tools.lint:lint-api:$lintVersion" 13 | compileOnly "com.android.tools.lint:lint-checks:$lintVersion" 14 | 15 | // Kotlin 16 | compileOnly "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 17 | testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" 18 | 19 | testImplementation "junit:junit:$junitVersion" 20 | testImplementation "org.assertj:assertj-core:$assertjVersion" 21 | 22 | // Lint test dependencies 23 | testImplementation "com.android.tools.lint:lint:$lintVersion" 24 | testImplementation "com.android.tools.lint:lint-tests:$lintVersion" 25 | testImplementation "com.android.tools:testutils:$lintVersion" 26 | } 27 | 28 | jar { 29 | baseName 'com.evernote.android.state.lint' 30 | version '1.0' 31 | 32 | manifest { 33 | attributes 'Manifest-Version': 1.0 34 | attributes 'Lint-Registry': 'com.evernote.android.state.lint.registry.StateIssueRegistry' 35 | } 36 | 37 | sourceSets { 38 | main { 39 | java.srcDirs += 'src/main/kotlin' 40 | } 41 | test { 42 | java.srcDirs += 'src/test/kotlin' 43 | } 44 | } 45 | } 46 | 47 | test { 48 | testLogging { 49 | showStandardStreams = true 50 | exceptionFormat = 'full' 51 | events "passed", "skipped", "failed", "standardOut", "standardError" 52 | } 53 | } -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestBundler.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | 7 | import com.evernote.android.state.Bundler; 8 | import com.evernote.android.state.State; 9 | import com.evernote.android.state.StateReflection; 10 | 11 | public class TestBundler { 12 | 13 | @State(MyBundler.class) 14 | private Data mData2; 15 | 16 | @StateReflection(value = MyBundler.class) 17 | private Data mDataReflOtherName; 18 | 19 | public Data getData2() { 20 | return mData2; 21 | } 22 | 23 | public void setData2(Data data2) { 24 | mData2 = data2; 25 | } 26 | 27 | public Data getDataRefl() { 28 | return mDataReflOtherName; 29 | } 30 | 31 | public void setDataRefl(Data data) { 32 | mDataReflOtherName = data; 33 | } 34 | 35 | public static final class Data { 36 | public int int1; 37 | public int int2; 38 | } 39 | 40 | public static final class MyBundler implements Bundler { 41 | @Override 42 | public void put(@NonNull String key, @NonNull Data value, @NonNull Bundle bundle) { 43 | bundle.putInt(key + "1", value.int1); 44 | bundle.putInt(key + "2", value.int2); 45 | } 46 | 47 | @Override 48 | @Nullable 49 | public Data get(@NonNull String key, @NonNull Bundle bundle) { 50 | if (bundle.containsKey(key + "1")) { 51 | Data data = new Data(); 52 | data.int1 = bundle.getInt(key + "1"); 53 | data.int2 = bundle.getInt(key + "2"); 54 | return data; 55 | } else { 56 | return null; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/Bundler.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.os.Bundle; 15 | 16 | import androidx.annotation.NonNull; 17 | import androidx.annotation.Nullable; 18 | 19 | /** 20 | * Helper interface to save any object inside a {@link Bundle}. 21 | * 22 | * @param The object class. 23 | */ 24 | public interface Bundler { 25 | 26 | /** 27 | * Save the given value inside of the bundle. 28 | * 29 | * @param key The base key for this value. Each field of the value should have a separate key with this prefix. 30 | * @param value The object which should be saved in the bundle. 31 | * @param bundle The bundle where the value should be stored. 32 | */ 33 | void put(@NonNull String key, @NonNull T value, @NonNull Bundle bundle); 34 | 35 | /** 36 | * Restore the value from the bundle. 37 | * 38 | * @param key The base key for this value. Each field of the value should have a separate key with this prefix. 39 | * @param bundle The bundle in which the value is stored. 40 | * @return The object restored from the bundle. 41 | */ 42 | @Nullable 43 | T get(@NonNull String key, @NonNull Bundle bundle); 44 | } 45 | -------------------------------------------------------------------------------- /gradle/gradle-quality.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'checkstyle' 2 | //apply plugin: 'findbugs' 3 | //apply plugin: 'pmd' 4 | 5 | check.dependsOn 'checkstyle'//, 'pmd' //,'findbugs' 6 | 7 | checkstyle { 8 | configFile file("${rootDir}/gradle/checkstyle/checkstyle.xml") 9 | } 10 | 11 | task checkstyle(type: Checkstyle) { 12 | source 'src' 13 | include '**/*.java' 14 | exclude '**/gen/**' 15 | exclude '**/test/resources/**' 16 | exclude '**/com/google/devtools/**' 17 | 18 | classpath = files() 19 | } 20 | 21 | task findbugs(type: FindBugs) { 22 | ignoreFailures = true 23 | effort = "max" 24 | reportLevel = "high" 25 | excludeFilter = new File("${project.rootDir}/build-config/findbugs/findbugs-filter.xml") 26 | classes = files("$project.buildDir/intermediates/classes/") 27 | 28 | source 'src' 29 | include '**/*.java' 30 | exclude '**/gen/**' 31 | 32 | reports { 33 | xml.enabled = false 34 | html.enabled = true 35 | 36 | xml { 37 | destination file("$project.buildDir/reports/findbugs/findbugs.xml") 38 | xml.withMessages true 39 | } 40 | html { 41 | destination file("$project.buildDir/reports/findbugs/findbugs.html") 42 | } 43 | } 44 | 45 | classpath = files() 46 | } 47 | 48 | task pmd(type: Pmd) { 49 | ruleSetFiles = files("${project.rootDir}/build-config/pmd/pmd-ruleset.xml") 50 | ignoreFailures = true 51 | ruleSets = ["java-basic", "java-braces", "java-strings"] 52 | 53 | source 'src' 54 | include '**/*.java' 55 | exclude '**/gen/**' 56 | 57 | reports { 58 | xml.enabled = true 59 | html.enabled = true 60 | 61 | xml { 62 | destination file("$project.buildDir/reports/pmd/pmd.xml") 63 | } 64 | html { 65 | destination file("$project.buildDir/reports/pmd/pmd.html") 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /demo/src/main/kotlin/com/evernote/android/state/demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek - initial version 10 | *******************************************************************************/ 11 | package com.evernote.android.state.demo 12 | 13 | import android.os.Bundle 14 | import androidx.fragment.app.Fragment 15 | import androidx.fragment.app.FragmentActivity 16 | import android.util.Log 17 | 18 | import com.evernote.android.state.State 19 | import com.evernote.android.state.StateReflection 20 | 21 | /** 22 | * @author rwondratschek 23 | */ 24 | class MainActivity : FragmentActivity() { 25 | 26 | @State 27 | var test1: Int = 0 28 | 29 | @StateReflection 30 | private var test2: Int = 0 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | test1++ 35 | test2++ 36 | 37 | if (savedInstanceState == null) { 38 | supportFragmentManager.beginTransaction().add(TestFragment(), "Tag").commit() 39 | } 40 | } 41 | 42 | override fun onResume() { 43 | super.onResume() 44 | Log.i("MainActivity", "onResume mTest1=$test1 test2=$test2") 45 | } 46 | } 47 | 48 | class TestFragment : Fragment() { 49 | 50 | @State 51 | var test: Int = 0 52 | 53 | override fun onCreate(savedInstanceState: Bundle?) { 54 | super.onCreate(savedInstanceState) 55 | test++ 56 | } 57 | 58 | override fun onResume() { 59 | super.onResume() 60 | Log.i("TestFragment", "onResume test=" + test) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/StateSaverTest.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state 2 | 3 | import android.os.Bundle 4 | import com.nhaarman.mockito_kotlin.anyOrNull 5 | import com.nhaarman.mockito_kotlin.eq 6 | import com.nhaarman.mockito_kotlin.never 7 | import com.nhaarman.mockito_kotlin.spy 8 | import com.nhaarman.mockito_kotlin.times 9 | import com.nhaarman.mockito_kotlin.verify 10 | import org.junit.FixMethodOrder 11 | import org.junit.Test 12 | import org.junit.runners.MethodSorters 13 | 14 | 15 | /** 16 | * @author rwondratschek 17 | */ 18 | @FixMethodOrder(MethodSorters.JVM) 19 | class StateSaverTest { 20 | 21 | @Suppress("ReplacePutWithAssignment") 22 | @Test 23 | fun verifyClassIsResolvedOnlyOnce() { 24 | val injectors = spy, Injector>>(mutableMapOf()) 25 | val stateSaver = StateSaverImpl(injectors) 26 | 27 | val savedObject = StateSaverTest() 28 | stateSaver.saveInstanceState(savedObject, Bundle()) 29 | verify(injectors, times(1)).put(eq(StateSaverTest::class.java), anyOrNull()) 30 | 31 | stateSaver.saveInstanceState(savedObject, Bundle()) 32 | stateSaver.restoreInstanceState(savedObject, Bundle()) 33 | verify(injectors, times(1)).put(eq(StateSaverTest::class.java), anyOrNull()) 34 | } 35 | 36 | @Suppress("ReplacePutWithAssignment") 37 | @Test 38 | fun verifyJavaClassIsSkipped() { 39 | val injectors = spy, Injector>>(mutableMapOf()) 40 | val stateSaver = StateSaverImpl(injectors) 41 | 42 | val savedObject = Any() 43 | stateSaver.saveInstanceState(savedObject, Bundle()) 44 | verify(injectors, never()).put(anyOrNull(), anyOrNull()) 45 | } 46 | 47 | @Suppress("ReplacePutWithAssignment") 48 | @Test 49 | fun verifyAndroidClassIsSkipped() { 50 | val injectors = spy, Injector>>(mutableMapOf()) 51 | val stateSaver = StateSaverImpl(injectors) 52 | 53 | val savedObject = Bundle() 54 | stateSaver.saveInstanceState(savedObject, Bundle()) 55 | verify(injectors, never()).put(anyOrNull(), anyOrNull()) 56 | } 57 | } -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/Injector.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.os.Bundle; 15 | import android.os.Parcelable; 16 | 17 | public class Injector { 18 | 19 | protected Injector() { 20 | } 21 | 22 | public abstract static class Object extends Injector { 23 | /*package*/ static final Object DEFAULT = new Object() { 24 | @Override 25 | public void restore(java.lang.Object target, Bundle state) { 26 | // no op 27 | } 28 | 29 | @Override 30 | public void save(java.lang.Object target, Bundle state) { 31 | // no op 32 | } 33 | }; 34 | 35 | public abstract void restore(T target, Bundle state); 36 | 37 | public abstract void save(T target, Bundle state); 38 | } 39 | 40 | public abstract static class View extends Injector { 41 | 42 | /*package*/ static final View DEFAULT = new View() { 43 | @Override 44 | public Parcelable restore(java.lang.Object target, Parcelable state) { 45 | return state; 46 | } 47 | 48 | @Override 49 | public Parcelable save(java.lang.Object target, Parcelable state) { 50 | return state; 51 | } 52 | }; 53 | 54 | public abstract Parcelable restore(T target, Parcelable state); 55 | 56 | public abstract Parcelable save(T target, Parcelable state); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinList.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.test 12 | 13 | import com.evernote.android.state.State 14 | import com.evernote.android.state.bundlers.BundlerListInteger 15 | import com.evernote.android.state.bundlers.BundlerListParcelable 16 | import com.evernote.android.state.bundlers.BundlerListString 17 | import java.io.Serializable 18 | 19 | /** 20 | * Copyright 2017 Evernote Corporation. All rights reserved. 21 | * 22 | * Created by rwondratschek on 2/13/17. 23 | */ 24 | class TestKotlinList { 25 | 26 | @State(BundlerListString::class) 27 | var stringList = listOf("Hello", "World") 28 | 29 | @State(BundlerListInteger::class) 30 | var emptyList = emptyList() 31 | 32 | @State(BundlerListParcelable::class) 33 | var parcelableList = mutableListOf(TestTypes.ParcelableImpl(1)) 34 | 35 | @State @Suppress("unused") 36 | var innerClass: TestKotlinInnerClass.Inner? = null 37 | 38 | @State(BundlerListParcelable::class) 39 | var nullableList: List? = null 40 | 41 | @State(BundlerListParcelable::class) 42 | lateinit var lateInitList: List 43 | 44 | @State 45 | var nullableArrayList: ArrayList? = null 46 | 47 | @State 48 | lateinit var lateInitArrayList: ArrayList 49 | 50 | @State 51 | var mParcelable: TestTypes.ParcelableImpl? = null 52 | 53 | @State 54 | var arrayListString = arrayListOf("Hello", "World") 55 | 56 | @State 57 | var arrayListSerializable = arrayListOf() 58 | } 59 | 60 | class MySerializable : TestView(null), Serializable -------------------------------------------------------------------------------- /demo-java-8/src/androidTest/java/com/evernote/android/state/test/java8/ProguardTest.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test.java8; 2 | 3 | import android.os.Bundle; 4 | import android.support.test.runner.AndroidJUnit4; 5 | 6 | import com.evernote.android.state.StateSaver; 7 | import com.evernote.different.TestProguard; 8 | import com.evernote.different.TestProguardBundler; 9 | import com.evernote.different.TestProguardHelper; 10 | 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | /** 15 | * @author rwondratschek 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ProguardTest { 19 | 20 | @Test 21 | public void testProguardRules() { 22 | TestProguard data = new TestProguard(); 23 | TestProguardHelper.verifyValue(data, 0); 24 | TestProguardHelper.setValue(data, 1); 25 | 26 | Bundle bundle = new Bundle(); 27 | StateSaver.saveInstanceState(data, bundle); 28 | 29 | TestProguardHelper.setValue(data, 0); 30 | TestProguardHelper.verifyValue(data, 0); 31 | 32 | StateSaver.restoreInstanceState(data, bundle); 33 | TestProguardHelper.verifyValue(data, 1); 34 | } 35 | 36 | @Test 37 | public void verifyCodeObfuscated() throws Exception { 38 | TestProguard.class.getDeclaredField("test1"); // test1 39 | // TestProguard.class.getDeclaredField("a"); // test2 40 | TestProguard.class.getDeclaredField("test2"); // test2 41 | TestProguard.class.getDeclaredField("test3"); 42 | TestProguard.class.getDeclaredMethod("a"); // getTest2() 43 | 44 | // TestProguardBundler.class.getDeclaredField("a"); // mData2 45 | TestProguardBundler.class.getDeclaredField("mData2"); // mData2 46 | TestProguardBundler.class.getDeclaredField("mDataReflOtherName"); 47 | TestProguardBundler.class.getDeclaredMethod("a"); // getData2() 48 | } 49 | 50 | @Test 51 | public void testBundler() { 52 | TestProguardBundler data = new TestProguardBundler(); 53 | data.verifyValue(0); 54 | 55 | data.setValue(1); 56 | 57 | Bundle bundle = new Bundle(); 58 | StateSaver.saveInstanceState(data, bundle); 59 | 60 | data.setValue(0); 61 | data.verifyValue(0); 62 | 63 | StateSaver.restoreInstanceState(data, bundle); 64 | data.verifyValue(1); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestJavaList.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.test; 12 | 13 | import com.evernote.android.state.State; 14 | import com.evernote.android.state.bundlers.BundlerListInteger; 15 | import com.evernote.android.state.bundlers.BundlerListParcelable; 16 | import com.evernote.android.state.bundlers.BundlerListString; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.List; 21 | 22 | /** 23 | * @author rwondratschek 24 | */ 25 | public class TestJavaList { 26 | 27 | @State(BundlerListString.class) 28 | private List stringList = Collections.unmodifiableList(new ArrayList(){{ 29 | add("Hello"); 30 | add("World"); 31 | }}); 32 | 33 | @State(BundlerListInteger.class) 34 | private List emptyList = Collections.emptyList(); 35 | 36 | @State(BundlerListParcelable.class) 37 | private List parcelableList = Collections.singletonList(new TestTypes.ParcelableImpl(1)); 38 | 39 | @State(BundlerListParcelable.class) 40 | List parcelableListPublic = Collections.singletonList(new TestTypes.ParcelableImpl(1)); 41 | 42 | public List getStringList() { 43 | return stringList; 44 | } 45 | 46 | public void setStringList(List stringList) { 47 | this.stringList = stringList; 48 | } 49 | 50 | public List getEmptyList() { 51 | return emptyList; 52 | } 53 | 54 | public void setEmptyList(List emptyList) { 55 | this.emptyList = emptyList; 56 | } 57 | 58 | public List getParcelableList() { 59 | return parcelableList; 60 | } 61 | 62 | public void setParcelableList(List parcelableList) { 63 | this.parcelableList = parcelableList; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demo-java-8/src/main/java/com/evernote/different/TestProguardBundler.java: -------------------------------------------------------------------------------- 1 | package com.evernote.different; 2 | 3 | import android.os.Bundle; 4 | import androidx.annotation.Keep; 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | 8 | import com.evernote.android.state.Bundler; 9 | import com.evernote.android.state.State; 10 | import com.evernote.android.state.StateReflection; 11 | 12 | public class TestProguardBundler { 13 | 14 | @State(MyBundler.class) 15 | private Data mData2 = new Data(); 16 | 17 | @StateReflection(value = MyBundler.class) 18 | private Data mDataReflOtherName = new Data(); 19 | 20 | public Data getData2() { 21 | return mData2; 22 | } 23 | 24 | public void setData2(Data data2) { 25 | mData2 = data2; 26 | } 27 | 28 | @Keep 29 | public void setValue(int value) { 30 | mData2.int1 = value; 31 | mData2.int2 = value; 32 | mDataReflOtherName.int1 = value; 33 | mDataReflOtherName.int2 = value; 34 | } 35 | 36 | @Keep 37 | public void verifyValue(int value) { 38 | if (mData2.int1 != value) { 39 | throw new IllegalStateException(); 40 | } 41 | if (mData2.int2 != value) { 42 | throw new IllegalStateException(); 43 | } 44 | if (mDataReflOtherName.int1 != value) { 45 | throw new IllegalStateException(); 46 | } 47 | if (mDataReflOtherName.int2 != value) { 48 | throw new IllegalStateException(); 49 | } 50 | } 51 | 52 | public static final class Data { 53 | public int int1; 54 | public int int2; 55 | } 56 | 57 | public static final class MyBundler implements Bundler { 58 | @Override 59 | public void put(@NonNull String key, @NonNull Data value, @NonNull Bundle bundle) { 60 | bundle.putInt(key + "1", value.int1); 61 | bundle.putInt(key + "2", value.int2); 62 | } 63 | 64 | @Override 65 | @Nullable 66 | public Data get(@NonNull String key, @NonNull Bundle bundle) { 67 | if (bundle.containsKey(key + "1")) { 68 | Data data = new Data(); 69 | data.int1 = bundle.getInt(key + "1"); 70 | data.int2 = bundle.getInt(key + "2"); 71 | return data; 72 | } else { 73 | return null; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/AndroidLifecycleCallbacks.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state; 2 | 3 | import android.app.Activity; 4 | import android.app.Application.ActivityLifecycleCallbacks; 5 | import android.os.Bundle; 6 | 7 | import androidx.fragment.app.Fragment; 8 | import androidx.fragment.app.FragmentActivity; 9 | import androidx.fragment.app.FragmentManager; 10 | 11 | /** 12 | * @author rwondratschek 13 | */ 14 | /*package*/ final class AndroidLifecycleCallbacks extends FragmentManager.FragmentLifecycleCallbacks implements ActivityLifecycleCallbacks { 15 | 16 | static final AndroidLifecycleCallbacks INSTANCE = new AndroidLifecycleCallbacks(); 17 | 18 | boolean mEnabled; // the extra flag is necessary in case this feature gets disabled, then we still can receive some callbacks for fragments 19 | 20 | private AndroidLifecycleCallbacks() { 21 | 22 | } 23 | 24 | @Override 25 | public void onActivityCreated(Activity activity, Bundle savedInstanceState) { 26 | if (mEnabled) { 27 | StateSaver.restoreInstanceState(activity, savedInstanceState); 28 | } 29 | 30 | if (activity instanceof FragmentActivity) { 31 | ((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(this, true); 32 | } 33 | } 34 | 35 | @Override 36 | public void onActivitySaveInstanceState(Activity activity, Bundle outState) { 37 | if (mEnabled) { 38 | StateSaver.saveInstanceState(activity, outState); 39 | } 40 | } 41 | 42 | @Override 43 | public void onFragmentPreCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { 44 | if (mEnabled) { 45 | StateSaver.restoreInstanceState(f, savedInstanceState); 46 | } 47 | } 48 | 49 | @Override 50 | public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) { 51 | if (mEnabled) { 52 | StateSaver.saveInstanceState(f, outState); 53 | } 54 | } 55 | 56 | @Override 57 | public void onActivityStarted(Activity activity) { 58 | } 59 | 60 | @Override 61 | public void onActivityResumed(Activity activity) { 62 | } 63 | 64 | @Override 65 | public void onActivityPaused(Activity activity) { 66 | } 67 | 68 | @Override 69 | public void onActivityStopped(Activity activity) { 70 | } 71 | 72 | @Override 73 | public void onActivityDestroyed(Activity activity) { 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /demo-java-8/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.github.ben-manes.versions' 3 | apply plugin: 'de.mobilej.unmock' 4 | apply from: "$rootDir/gradle/gradle-quality.gradle" 5 | 6 | android { 7 | compileSdkVersion rootProject.ext.compileSdkVersion 8 | buildToolsVersion rootProject.ext.buildToolsVersion 9 | 10 | defaultConfig { 11 | applicationId "com.evernote.android.state.demo.java8" 12 | 13 | minSdkVersion rootProject.ext.minSdkVersion 14 | targetSdkVersion rootProject.ext.targetSdkVersion 15 | 16 | versionName project.VERSION_NAME 17 | versionCode Integer.parseInt(project.VERSION_CODE) 18 | 19 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_8 24 | targetCompatibility JavaVersion.VERSION_1_8 25 | } 26 | 27 | testOptions { 28 | unitTests.returnDefaultValues = true 29 | unitTests.includeAndroidResources = true 30 | unitTests.all { 31 | testLogging { 32 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' 33 | } 34 | } 35 | } 36 | 37 | buildTypes { 38 | debug { 39 | minifyEnabled true 40 | testProguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.cfg' 41 | } 42 | 43 | release { 44 | minifyEnabled true 45 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard.cfg' 46 | } 47 | } 48 | 49 | lintOptions { 50 | abortOnError true 51 | } 52 | } 53 | 54 | dependencies { 55 | implementation project(':library') 56 | annotationProcessor project(':processor') 57 | 58 | implementation "androidx.appcompat:appcompat:1.0.0" 59 | 60 | androidTestImplementation "androidx.annotation:annotation:1.0.0" 61 | androidTestImplementation "com.android.support.test:runner:$testRuleVersion" 62 | androidTestImplementation "com.android.support.test:rules:testRuleVersion" 63 | androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" 64 | androidTestImplementation "com.squareup.assertj:assertj-android:$assertjAndroidVersion" 65 | 66 | testImplementation "junit:junit:$junitVersion" 67 | testImplementation "org.assertj:assertj-core:$assertjVersion" 68 | 69 | unmock rootProject.ext.unmockVersion 70 | } 71 | 72 | uploadArchives.enabled false -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/TestKotlinPrivateInnerClass.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import android.os.Parcel 4 | import android.os.Parcelable 5 | import com.evernote.android.state.StateReflection 6 | 7 | /** 8 | * @author rwondratschek 9 | */ 10 | class TestKotlinPrivateInnerClass { 11 | @StateReflection 12 | private var inner= Inner.A 13 | 14 | @StateReflection 15 | private var parcelable = ParcelableImpl(1) 16 | 17 | @StateReflection 18 | private var innerList = arrayListOf(Inner.A) 19 | 20 | @StateReflection 21 | private var parcelableList = arrayListOf(ParcelableImpl(1)) 22 | 23 | init { 24 | setToA() 25 | } 26 | 27 | fun setToA() { 28 | inner = Inner.A 29 | parcelable = ParcelableImpl(1) 30 | innerList = arrayListOf(Inner.A) 31 | parcelableList = arrayListOf(ParcelableImpl(1)) 32 | } 33 | 34 | fun setToB() { 35 | inner = Inner.B 36 | parcelable = ParcelableImpl(2) 37 | innerList = arrayListOf(Inner.B) 38 | parcelableList = arrayListOf(ParcelableImpl(2)) 39 | } 40 | 41 | fun isA(): Boolean { 42 | return inner == Inner.A && parcelable.value == 1 && innerList[0] == Inner.A && parcelableList[0].value == 1 43 | } 44 | 45 | fun isB(): Boolean { 46 | return inner == Inner.B && parcelable.value == 2 && innerList[0] == Inner.B && parcelableList[0].value == 2 47 | } 48 | 49 | private enum class Inner { 50 | A, B 51 | } 52 | 53 | private class ParcelableImpl : Parcelable { 54 | var value: Int = 0 55 | 56 | constructor(anInt: Int) { 57 | value = anInt 58 | } 59 | 60 | protected constructor(`in`: Parcel) { 61 | value = `in`.readInt() 62 | } 63 | 64 | override fun equals(o: Any?): Boolean { 65 | if (this === o) return true 66 | if (o == null || javaClass != o.javaClass) return false 67 | 68 | val that = o as ParcelableImpl? 69 | 70 | return value == that!!.value 71 | } 72 | 73 | override fun hashCode(): Int { 74 | return value 75 | } 76 | 77 | override fun writeToParcel(dest: Parcel, flags: Int) { 78 | dest.writeInt(value) 79 | } 80 | 81 | override fun describeContents(): Int { 82 | return 0 83 | } 84 | 85 | companion object { 86 | 87 | val CREATOR: Parcelable.Creator = object : Parcelable.Creator { 88 | override fun createFromParcel(`in`: Parcel): ParcelableImpl { 89 | return ParcelableImpl(`in`) 90 | } 91 | 92 | override fun newArray(size: Int): Array { 93 | return arrayOfNulls(size) 94 | } 95 | } 96 | } 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.ben-manes.versions' 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-kapt' 5 | apply plugin: 'de.mobilej.unmock' 6 | apply from: "$rootDir/gradle/gradle-quality.gradle" 7 | 8 | archivesBaseName = 'android-state' 9 | 10 | android { 11 | compileSdkVersion rootProject.ext.compileSdkVersion 12 | buildToolsVersion rootProject.ext.buildToolsVersion 13 | 14 | resourcePrefix 'state_' 15 | 16 | useLibrary 'android.test.mock' 17 | 18 | defaultConfig { 19 | minSdkVersion rootProject.ext.minSdkVersion 20 | targetSdkVersion rootProject.ext.targetSdkVersion 21 | 22 | versionName project.VERSION_NAME 23 | versionCode Integer.parseInt(project.VERSION_CODE) 24 | 25 | consumerProguardFiles 'proguard.cfg' 26 | 27 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 28 | } 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_7 32 | targetCompatibility JavaVersion.VERSION_1_7 33 | } 34 | 35 | lintOptions { 36 | abortOnError true 37 | } 38 | 39 | sourceSets { 40 | //main.java.srcDirs += 'src/main/kotlin' 41 | test.java.srcDirs += 'src/test/kotlin' 42 | androidTest.java.srcDirs += 'src/androidTest/kotlin' 43 | } 44 | 45 | testOptions { 46 | unitTests.returnDefaultValues = true 47 | unitTests.includeAndroidResources = true 48 | unitTests.all { 49 | testLogging { 50 | events 'passed', 'skipped', 'failed', 'standardOut', 'standardError' 51 | } 52 | } 53 | } 54 | } 55 | 56 | dependencies { 57 | implementation "androidx.fragment:fragment:1.0.0" 58 | lintChecks project(':library-lint') 59 | 60 | testImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 61 | 62 | testImplementation "junit:junit:$junitVersion" 63 | testImplementation "org.assertj:assertj-core:$assertjVersion" 64 | testImplementation "com.nhaarman:mockito-kotlin-kt1.1:$kotlinMockitoVersion" 65 | testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlinVersion" 66 | testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlinVersion" 67 | 68 | kaptTest project(':processor') 69 | 70 | androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 71 | androidTestImplementation "androidx.annotation:annotation:1.0.0" 72 | androidTestImplementation "com.android.support.test:runner:$testRuleVersion" 73 | androidTestImplementation "com.android.support.test:rules:$testRuleVersion" 74 | androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" 75 | androidTestImplementation "com.squareup.assertj:assertj-android:$assertjAndroidVersion" 76 | 77 | kaptAndroidTest project(':processor') 78 | 79 | //noinspection GradleDependency 80 | unmock rootProject.ext.unmockVersion 81 | } 82 | 83 | apply from: "$rootDir/gradle/gradle-push.gradle" -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestPrivateInnerClass.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.evernote.android.state.StateReflection; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | 11 | /** 12 | * @author rwondratschek 13 | */ 14 | public class TestPrivateInnerClass { 15 | 16 | @StateReflection 17 | private Inner mInner2; 18 | 19 | @StateReflection 20 | private ParcelableImpl mParcelable2; 21 | 22 | @StateReflection 23 | private ArrayList mInnerList = new ArrayList<>(); 24 | 25 | @StateReflection 26 | private ArrayList mParcelableList = new ArrayList<>(); 27 | 28 | public TestPrivateInnerClass() { 29 | setToA(); 30 | } 31 | 32 | public void setToA() { 33 | mInner2 = Inner.A; 34 | mParcelable2 = new ParcelableImpl(1); 35 | mInnerList = new ArrayList<>(Collections.singletonList(Inner.A)); 36 | mParcelableList = new ArrayList<>(Collections.singletonList(new ParcelableImpl(1))); 37 | } 38 | 39 | public void setToB() { 40 | mInner2 = Inner.B; 41 | mParcelable2 = new ParcelableImpl(2); 42 | mInnerList = new ArrayList<>(Collections.singletonList(Inner.B)); 43 | mParcelableList = new ArrayList<>(Collections.singletonList(new ParcelableImpl(2))); 44 | } 45 | 46 | public boolean isA() { 47 | return mInner2 == Inner.A && mParcelable2.mInt == 1 && mInnerList.get(0) == Inner.A && mParcelableList.get(0).mInt == 1; 48 | } 49 | 50 | public boolean isB() { 51 | return mInner2 == Inner.B && mParcelable2.mInt == 2 && mInnerList.get(0) == Inner.B && mParcelableList.get(0).mInt == 2; 52 | } 53 | 54 | private enum Inner { 55 | A, B 56 | } 57 | 58 | private static class ParcelableImpl implements Parcelable { 59 | private int mInt; 60 | 61 | public ParcelableImpl(int anInt) { 62 | mInt = anInt; 63 | } 64 | 65 | protected ParcelableImpl(Parcel in) { 66 | mInt = in.readInt(); 67 | } 68 | 69 | @Override 70 | public boolean equals(Object o) { 71 | if (this == o) return true; 72 | if (o == null || getClass() != o.getClass()) return false; 73 | 74 | ParcelableImpl that = (ParcelableImpl) o; 75 | 76 | return mInt == that.mInt; 77 | } 78 | 79 | @Override 80 | public int hashCode() { 81 | return mInt; 82 | } 83 | 84 | @Override 85 | public void writeToParcel(Parcel dest, int flags) { 86 | dest.writeInt(mInt); 87 | } 88 | 89 | @Override 90 | public int describeContents() { 91 | return 0; 92 | } 93 | 94 | public static final Creator CREATOR = new Creator() { 95 | @Override 96 | public ParcelableImpl createFromParcel(Parcel in) { 97 | return new ParcelableImpl(in); 98 | } 99 | 100 | @Override 101 | public ParcelableImpl[] newArray(int size) { 102 | return new ParcelableImpl[size]; 103 | } 104 | }; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.4.1 (2018-10-06) 2 | 3 | * Remove Android dependency from processor, this resolves problems with the Jetifier, see #56 4 | 5 | ## 1.4.0 (2018-09-23) 6 | 7 | * Migrated dependencies to AndroidX 8 | * Sort elements by name to make processor deterministic, see #57 (Thanks @DSteve595) 9 | * Fix compile error with generic inner classes and kapt, see #54 (Thanks @janbina) 10 | 11 | ## 1.3.1 (2018-06-10) 12 | 13 | * Avoid obfuscating the class name if the class contains a field annotated with `@State`, see #43 14 | * Don't use the serializable type for parcelable arrays, see #53 15 | 16 | ## 1.3.0 (2018-05-28) 17 | 18 | * Support incremental annotation processing, see #48 19 | * Omit the type for private inner classes when using reflection, see #44 20 | 21 | ## 1.2.1 (2018-03-18) 22 | 23 | * Fix an issue with kapt where a class cannot be found, see #42 24 | * Fix problems with the Proguard rules and allow better obfuscation, see #43 25 | 26 | ## 1.2.0 (2018-02-02) 27 | 28 | * Add the option to automatically save the instance state of all activities and fragments from the support library 29 | * Support Kotlin in Lint checks 30 | 31 | ## 1.1.6 (2017-11-01) 32 | 33 | * Don't crash when the license header file cannot be read, see #35 34 | 35 | ## 1.1.5 (2017-08-31) 36 | 37 | * Handle generics in bundlers properly, see #32 38 | 39 | ## 1.1.4 (2017-08-18) 40 | 41 | * Add a small optimization, if the object doesn't contain any state variable, see #31 42 | * Fix potential `ClassCastException`, see #30 43 | * Serialize `ArrayList` if it contains `Serializable` elements, see #29 44 | 45 | ## 1.1.3 (2017-07-24) 46 | 47 | * Fix wrong handling of properties in Kotlin in Hungarian notation, see #26 48 | 49 | ## 1.1.2 (2017-07-11) 50 | 51 | * Fix nullable lists containing `Parcelable` elements in Kotlin, see #25 52 | 53 | ## 1.1.1 (2017-06-22) 54 | 55 | * Add a Proguard rule to keep the class name if the class contains a `State` or `StateReflection annoation`, see #23 56 | * Handle a generic `Serializable` or `Parcelable` properly, see #24 57 | 58 | ## 1.1.0 (2017-06-05) 59 | 60 | * Remove unnecessary Proguard rules prevent proper obfuscation, see #15 61 | * Add new annotation `StateReflection` to properly support obfuscation even though reflection is being used, see #15 62 | * Bundle library classes in annotation processor, see #13 63 | * Detect correct getter and setter for boolean properties with a "is" prefix in Kotlin, see #18 64 | * Add a Lint check to find matching save and restore calls to ensure a proper usage of the library 65 | 66 | ## 1.0.6 (2017-04-07) 67 | 68 | * Fix inner classes in Kotlin, see #11 69 | * Add a license header to each generated file, see #10 70 | 71 | ## 1.0.5 (2017-03-02) 72 | 73 | * Fix a Stackoverflow error when using Kotlin generics, see #9 74 | 75 | ## 1.0.4 (2017-02-24) 76 | 77 | * Fix a Stackoverflow error when using Kotlin enums, see #8 78 | 79 | ## 1.0.3 (2017-02-15) 80 | 81 | * Provide some `Bundler` implementations for lists, see #4 82 | * Add a workaround for the Jack compiler where the package name is always "package", may fix #5 83 | * Erase generics of extended classes, fixes #6 84 | 85 | ## 1.0.2 (2017-02-15) 86 | 87 | * NOT AVAILABLE because of Maven Central sync issues 88 | 89 | ## 1.0.1 (2017-01-27) 90 | 91 | * Insert missing casted type for `Parcelable` or `Serializable` getters 92 | * Allow `is` as getter prefix for boolean getters 93 | 94 | ## 1.0.0 (2017-01-26) 95 | 96 | * Initial release -------------------------------------------------------------------------------- /demo-java-8/src/main/java/com/evernote/android/state/test/java8/TestTypes.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test.java8; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | import android.util.SparseArray; 7 | 8 | import com.evernote.android.state.State; 9 | 10 | import java.io.Serializable; 11 | import java.util.ArrayList; 12 | 13 | public class TestTypes { 14 | @State 15 | public boolean mBoolean; 16 | @State 17 | public boolean[] mBooleanArray; 18 | @State 19 | public Boolean mBooleanObj; 20 | @State 21 | public byte mByte; 22 | @State 23 | public byte[] mByteArray; 24 | @State 25 | public Byte mByteObj; 26 | @State 27 | public char mChar; 28 | @State 29 | public char[] mCharArray; 30 | @State 31 | public Character mCharObj; 32 | @State 33 | public double mDouble; 34 | @State 35 | public double[] mDoubleArray; 36 | @State 37 | public Double mDoubleObj; 38 | @State 39 | public float mFloat; 40 | @State 41 | public float[] mFloatArray; 42 | @State 43 | public Float mFloatObj; 44 | @State 45 | public int mInt; 46 | @State 47 | public int[] mIntArray; 48 | @State 49 | public Integer mIntegerObj; 50 | @State 51 | public long mLong; 52 | @State 53 | public long[] mLongArray; 54 | @State 55 | public Long mLongObj; 56 | @State 57 | public short mShort; 58 | @State 59 | public short[] mShortArray; 60 | @State 61 | public Short mShortObj; 62 | @State 63 | public CharSequence mCharSequence; 64 | @State 65 | public CharSequence[] mCharSequenceArray; 66 | @State 67 | public String mString; 68 | @State 69 | public String[] mStringArray; 70 | @State 71 | public ArrayList mCharSequenceArrayList; 72 | @State 73 | public ArrayList mIntegerArrayList; 74 | @State 75 | public ArrayList mStringArrayList; 76 | @State 77 | public Bundle mBundle; 78 | @State 79 | public Parcelable[] mParcelableArray; 80 | @State 81 | public ParcelableImpl mParcelableImpl; 82 | @State 83 | public SerializableImpl mSerializableImpl; 84 | @State 85 | public ArrayList mParcelableArrayList; 86 | @State 87 | public SparseArray mParcelableSparseArray; 88 | 89 | public static class ParcelableImpl implements Parcelable { 90 | private int mInt; 91 | 92 | public ParcelableImpl(int anInt) { 93 | mInt = anInt; 94 | } 95 | 96 | protected ParcelableImpl(Parcel in) { 97 | mInt = in.readInt(); 98 | } 99 | 100 | @Override 101 | public boolean equals(Object o) { 102 | if (this == o) return true; 103 | if (o == null || getClass() != o.getClass()) return false; 104 | 105 | ParcelableImpl that = (ParcelableImpl) o; 106 | 107 | return mInt == that.mInt; 108 | } 109 | 110 | @Override 111 | public int hashCode() { 112 | return mInt; 113 | } 114 | 115 | @Override 116 | public void writeToParcel(Parcel dest, int flags) { 117 | dest.writeInt(mInt); 118 | } 119 | 120 | @Override 121 | public int describeContents() { 122 | return 0; 123 | } 124 | 125 | public static final Creator CREATOR = new Creator() { 126 | @Override 127 | public ParcelableImpl createFromParcel(Parcel in) { 128 | return new ParcelableImpl(in); 129 | } 130 | 131 | @Override 132 | public ParcelableImpl[] newArray(int size) { 133 | return new ParcelableImpl[size]; 134 | } 135 | }; 136 | } 137 | 138 | public static class SerializableImpl implements Serializable { 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestTypes.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | import android.util.SparseArray; 7 | 8 | import com.evernote.android.state.State; 9 | 10 | import java.io.Serializable; 11 | import java.util.ArrayList; 12 | 13 | public class TestTypes { 14 | @State 15 | public boolean mBoolean; 16 | @State 17 | public boolean[] mBooleanArray; 18 | @State 19 | public Boolean mBooleanObj; 20 | @State 21 | public byte mByte; 22 | @State 23 | public byte[] mByteArray; 24 | @State 25 | public Byte mByteObj; 26 | @State 27 | public char mChar; 28 | @State 29 | public char[] mCharArray; 30 | @State 31 | public Character mCharObj; 32 | @State 33 | public double mDouble; 34 | @State 35 | public double[] mDoubleArray; 36 | @State 37 | public Double mDoubleObj; 38 | @State 39 | public float mFloat; 40 | @State 41 | public float[] mFloatArray; 42 | @State 43 | public Float mFloatObj; 44 | @State 45 | public int mInt; 46 | @State 47 | public int[] mIntArray; 48 | @State 49 | public Integer mIntegerObj; 50 | @State 51 | public long mLong; 52 | @State 53 | public long[] mLongArray; 54 | @State 55 | public Long mLongObj; 56 | @State 57 | public short mShort; 58 | @State 59 | public short[] mShortArray; 60 | @State 61 | public Short mShortObj; 62 | @State 63 | public CharSequence mCharSequence; 64 | @State 65 | public CharSequence[] mCharSequenceArray; 66 | @State 67 | public String mString; 68 | @State 69 | public String[] mStringArray; 70 | @State 71 | public ArrayList mCharSequenceArrayList; 72 | @State 73 | public ArrayList mIntegerArrayList; 74 | @State 75 | public ArrayList mStringArrayList; 76 | @State 77 | public Bundle mBundle; 78 | @State 79 | public Parcelable[] mParcelableArray; 80 | @State 81 | public ParcelableImpl mParcelableImpl; 82 | @State 83 | public SerializableImpl mSerializableImpl; 84 | @State 85 | public ArrayList mParcelableArrayList; 86 | @State 87 | public SparseArray mParcelableSparseArray; 88 | 89 | public static class ParcelableImpl implements Parcelable { 90 | private int mInt; 91 | 92 | public ParcelableImpl(int anInt) { 93 | mInt = anInt; 94 | } 95 | 96 | protected ParcelableImpl(Parcel in) { 97 | mInt = in.readInt(); 98 | } 99 | 100 | public boolean isValue(int value) { 101 | return mInt == value; 102 | } 103 | 104 | @Override 105 | public boolean equals(Object o) { 106 | if (this == o) return true; 107 | if (o == null || getClass() != o.getClass()) return false; 108 | 109 | ParcelableImpl that = (ParcelableImpl) o; 110 | 111 | return mInt == that.mInt; 112 | } 113 | 114 | @Override 115 | public int hashCode() { 116 | return mInt; 117 | } 118 | 119 | @Override 120 | public void writeToParcel(Parcel dest, int flags) { 121 | dest.writeInt(mInt); 122 | } 123 | 124 | @Override 125 | public int describeContents() { 126 | return 0; 127 | } 128 | 129 | public static final Creator CREATOR = new Creator() { 130 | @Override 131 | public ParcelableImpl createFromParcel(Parcel in) { 132 | return new ParcelableImpl(in); 133 | } 134 | 135 | @Override 136 | public ParcelableImpl[] newArray(int size) { 137 | return new ParcelableImpl[size]; 138 | } 139 | }; 140 | } 141 | 142 | public static class SerializableImpl implements Serializable { 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/StateSaver.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.app.Application; 15 | import android.os.Bundle; 16 | import android.os.Parcelable; 17 | import androidx.annotation.NonNull; 18 | import androidx.annotation.Nullable; 19 | import android.view.View; 20 | 21 | import java.util.LinkedHashMap; 22 | 23 | /** 24 | * Entry point to save and restore objects. 25 | */ 26 | @SuppressWarnings({"WeakerAccess", "unused"}) 27 | public final class StateSaver { 28 | 29 | public static final String SUFFIX = "$$StateSaver"; 30 | public static final String ANDROID_PREFIX = "android."; 31 | public static final String JAVA_PREFIX = "java."; 32 | 33 | private static final StateSaverImpl IMPL = new StateSaverImpl(new LinkedHashMap, Injector>()); 34 | 35 | /** 36 | * Save the given {@code target} in the passed in {@link Bundle}. 37 | * 38 | * @param target The object containing fields annotated with {@link State}. 39 | * @param state The object is saved into this bundle. 40 | */ 41 | public static void saveInstanceState(@NonNull T target, @NonNull Bundle state) { 42 | IMPL.saveInstanceState(target, state); 43 | } 44 | 45 | /** 46 | * Restore the given {@code target} from the passed in {@link Bundle}. 47 | * 48 | * @param target The object containing fields annotated with {@link State}. 49 | * @param state The object is being restored from this bundle. Nothing is restored if the argument is {@code null}. 50 | */ 51 | public static void restoreInstanceState(@NonNull T target, @Nullable Bundle state) { 52 | IMPL.restoreInstanceState(target, state); 53 | } 54 | 55 | /** 56 | * Save the state of the given view and the other state inside of the returned {@link Parcelable}. 57 | * 58 | * @param target The view containing fields annotated with {@link State}. 59 | * @param state The super state of the parent class of the view. Usually it isn't {@code null}. 60 | * @return A parcelable containing the view's state and its super state. 61 | */ 62 | @NonNull 63 | public static Parcelable saveInstanceState(@NonNull T target, @Nullable Parcelable state) { 64 | return IMPL.saveInstanceState(target, state); 65 | } 66 | 67 | /** 68 | * Restore the sate of the given view and return the super state of the parent class. 69 | * 70 | * @param target The view containing fields annotated with {@link State}. 71 | * @param state The parcelable containing the view's state and its super sate. 72 | * @return The super state of teh parent class of the view. Usually it isn't {@code null}. 73 | */ 74 | @Nullable 75 | public static Parcelable restoreInstanceState(@NonNull T target, @Nullable Parcelable state) { 76 | return IMPL.restoreInstanceState(target, state); 77 | } 78 | 79 | /** 80 | * Turns automatic instance saving on or off for all activities and fragments from the support library. This avoids 81 | * manual calls of {@link #saveInstanceState(Object, Bundle)} and {@link #restoreInstanceState(Object, Bundle)}, instead 82 | * the library is doing this automatically for you. It's still necessary to annotate fields with {@link State}, though. 83 | *
84 | *
85 | * The best place to turn on this feature is in your {@link Application#onCreate()} method. 86 | * 87 | * @param application Your application instance. 88 | * @param enabled Whether this feature should be enabled. 89 | */ 90 | public static void setEnabledForAllActivitiesAndSupportFragments(@NonNull Application application, boolean enabled) { 91 | IMPL.setEnabledForAllActivitiesAndSupportFragments(application, enabled); 92 | } 93 | 94 | private StateSaver() { 95 | throw new UnsupportedOperationException(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /demo-java-8/src/test/java/com/evernote/android/state/test/java8/Java8BundlingTest.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | package com.evernote.android.state.test.java8; 12 | 13 | import android.os.Bundle; 14 | 15 | import com.evernote.android.state.StateSaver; 16 | 17 | import org.junit.Before; 18 | import org.junit.FixMethodOrder; 19 | import org.junit.Test; 20 | import org.junit.runners.MethodSorters; 21 | 22 | import java.util.ArrayList; 23 | 24 | import static org.assertj.core.api.Assertions.assertThat; 25 | 26 | /** 27 | * @author rwondratschek 28 | */ 29 | @FixMethodOrder(MethodSorters.JVM) 30 | public class Java8BundlingTest { 31 | 32 | private Bundle mBundle; 33 | 34 | @Before 35 | public void setUp() { 36 | mBundle = new Bundle(); 37 | } 38 | 39 | @Test 40 | public void testTypes() { 41 | TestTypes object = createSavedInstance(TestTypes.class); 42 | object.mBooleanObj = Boolean.FALSE; 43 | 44 | StateSaver.restoreInstanceState(object, mBundle); 45 | assertThat(object.mBooleanObj).isNull(); 46 | 47 | object.mInt = 5; 48 | object.mIntegerObj = 6; 49 | object.mParcelableArrayList = new ArrayList() {{ 50 | add(new TestTypes.ParcelableImpl(7)); 51 | }}; 52 | 53 | StateSaver.saveInstanceState(object, mBundle); 54 | object.mInt = 0; 55 | object.mIntegerObj = null; 56 | object.mParcelableArrayList = null; 57 | 58 | StateSaver.restoreInstanceState(object, mBundle); 59 | assertThat(object.mInt).isEqualTo(5); 60 | assertThat(object.mIntegerObj).isNotNull().isEqualTo(6); 61 | assertThat(object.mParcelableArrayList).isNotNull().isNotEmpty().containsExactly(new TestTypes.ParcelableImpl(7)); 62 | } 63 | 64 | @Test 65 | public void testTypesProperty() { 66 | TestTypesProperty object = createSavedInstance(TestTypesProperty.class); 67 | object.setBooleanObj(Boolean.FALSE); 68 | 69 | StateSaver.restoreInstanceState(object, mBundle); 70 | assertThat(object.getBooleanObj()).isNull(); 71 | 72 | object.setInt(5); 73 | object.setIntegerObj(6); 74 | object.setParcelableImplExtension(new TestTypesProperty.ParcelableImplExtension(7, 8)); 75 | object.setParcelableArrayList(new ArrayList() {{ 76 | add(new TestTypes.ParcelableImpl(9)); 77 | }}); 78 | 79 | StateSaver.saveInstanceState(object, mBundle); 80 | object.setInt(0); 81 | object.setIntegerObj(0); 82 | object.setParcelableImplExtension(null); 83 | object.setParcelableArrayList(null); 84 | 85 | StateSaver.restoreInstanceState(object, mBundle); 86 | assertThat(object.getInt()).isEqualTo(5); 87 | assertThat(object.getIntegerObj()).isNotNull().isEqualTo(6); 88 | assertThat(object.getParcelableImplExtension()).isNotNull().isEqualTo(new TestTypesProperty.ParcelableImplExtension(7, 8)); 89 | assertThat(object.getParcelableArrayList()).isNotNull().isNotEmpty().containsExactly(new TestTypes.ParcelableImpl(9)); 90 | } 91 | 92 | @Test 93 | public void testNested() { 94 | TestNested.Inner1.InnerInner1 object1 = createSavedInstance(TestNested.Inner1.InnerInner1.class); 95 | TestNested.Inner2.InnerInner1 object2 = createSavedInstance(TestNested.Inner2.InnerInner1.class); 96 | 97 | object1.test = 5; 98 | object2.test = 5; 99 | 100 | StateSaver.restoreInstanceState(object1, mBundle); 101 | StateSaver.restoreInstanceState(object2, mBundle); 102 | 103 | assertThat(object1.test).isEqualTo(0); 104 | assertThat(object2.test).isEqualTo(0); 105 | } 106 | 107 | private T createSavedInstance(Class clazz) { 108 | try { 109 | T instance = clazz.newInstance(); 110 | StateSaver.saveInstanceState(instance, mBundle); 111 | return instance; 112 | } catch (Exception e) { 113 | throw new RuntimeException(e); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /library-lint/src/main/kotlin/com/evernote/android/state/lint/detector/AndroidStateDetector.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.lint.detector 2 | 3 | import com.android.tools.lint.client.api.UElementHandler 4 | import com.android.tools.lint.detector.api.Category 5 | import com.android.tools.lint.detector.api.Context 6 | import com.android.tools.lint.detector.api.Detector 7 | import com.android.tools.lint.detector.api.Implementation 8 | import com.android.tools.lint.detector.api.Issue 9 | import com.android.tools.lint.detector.api.JavaContext 10 | import com.android.tools.lint.detector.api.Scope 11 | import com.android.tools.lint.detector.api.Severity 12 | import org.jetbrains.uast.UCallExpression 13 | import org.jetbrains.uast.UDeclaration 14 | import org.jetbrains.uast.UElement 15 | import org.jetbrains.uast.getContainingUClass 16 | import org.jetbrains.uast.getContainingUFile 17 | import java.util.EnumSet 18 | 19 | /** 20 | * This detector crawls through the Java and Kotlin files and looks for non-matching instances of 21 | * saveInstanceState and restoreInstanceState for the StateSaver library. 22 | * 23 | * Created by junchengc on 5/15/17. 24 | */ 25 | class AndroidStateDetector : Detector(), Detector.UastScanner { 26 | companion object Issues { 27 | private val IMPLEMENTATION = Implementation(AndroidStateDetector::class.java, Scope.JAVA_FILE_SCOPE) 28 | private const val ADVICE = "StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState()." 29 | 30 | @JvmField 31 | val ISSUE = Issue.create( 32 | "NonMatchingStateSaverCalls", 33 | ADVICE, 34 | "$ADVICE Failing to do so could result in weird behaviors after activity recreation where certain states are not persisted.", 35 | Category.CORRECTNESS, 36 | 6, 37 | Severity.WARNING, 38 | IMPLEMENTATION 39 | ) 40 | 41 | private const val SAVE_INSTANCE_STATE_METHOD = "saveInstanceState" 42 | private const val RESTORE_INSTANCE_STATE_METHOD = "restoreInstanceState" 43 | private val APPLICABLE_METHODS = listOf(SAVE_INSTANCE_STATE_METHOD, RESTORE_INSTANCE_STATE_METHOD) 44 | private const val STATE_SAVER_CLASS_SIMPLE = "StateSaver" 45 | private const val STATE_SAVER_CLASS = "com.evernote.android.state.$STATE_SAVER_CLASS_SIMPLE" 46 | } 47 | 48 | private val saveCalls = mutableMapOf() 49 | private val restoreCalls = mutableMapOf() 50 | 51 | override fun afterCheckFile(context: Context) { 52 | try { 53 | if (context !is JavaContext) return 54 | 55 | val allClasses = mutableSetOf().apply { 56 | addAll(saveCalls.keys) 57 | addAll(restoreCalls.keys) 58 | } 59 | 60 | allClasses 61 | .filter { saveCalls.containsKey(it) xor restoreCalls.containsKey(it) } 62 | .map { (saveCalls[it] ?: restoreCalls[it])!! } 63 | .forEach { 64 | context.report(ISSUE, it, context.getLocation(it), ADVICE) 65 | } 66 | } finally { 67 | saveCalls.clear() 68 | restoreCalls.clear() 69 | } 70 | } 71 | 72 | override fun createUastHandler(context: JavaContext) = object : UElementHandler() { 73 | override fun visitCallExpression(node: UCallExpression) { 74 | val methodName = node.methodIdentifier?.name ?: return 75 | val uastParent = node.uastParent ?: return 76 | 77 | if (!APPLICABLE_METHODS.contains(methodName)) return 78 | 79 | val uFile = node.getContainingUFile() ?: return 80 | val imports = uFile.imports.mapNotNull { it.importReference?.asSourceString() } 81 | val parent = uastParent.psi?.text ?: return // StateSaver.restoreInstanceState or surrounding method with direct import 82 | 83 | if (!isStateSaverMethod(methodName, imports, parent)) return 84 | 85 | val cls = node.getContainingUClass()!! 86 | when (methodName) { 87 | SAVE_INSTANCE_STATE_METHOD -> saveCalls[cls] = node 88 | RESTORE_INSTANCE_STATE_METHOD -> restoreCalls[cls] = node 89 | } 90 | } 91 | 92 | private fun isStateSaverMethod(methodName: String, imports: List, parent: String) = 93 | if (imports.contains("$STATE_SAVER_CLASS.$methodName")) { 94 | true // direct import 95 | } else { 96 | // StateSaver.methodName check 97 | imports.contains(STATE_SAVER_CLASS) && parent.startsWith("$STATE_SAVER_CLASS_SIMPLE.$methodName") 98 | } 99 | } 100 | 101 | override fun getApplicableUastTypes(): List> = listOf(UCallExpression::class.java) 102 | override fun getApplicableFiles(): EnumSet = Scope.JAVA_FILE_SCOPE 103 | override fun getApplicableMethodNames(): List = APPLICABLE_METHODS 104 | } 105 | -------------------------------------------------------------------------------- /library/src/androidTest/kotlin/com/evernote/android/state/test/GlobalStateSaverTest.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.content.Intent 6 | import android.content.pm.ActivityInfo 7 | import android.content.res.Configuration 8 | import android.os.Bundle 9 | import android.os.Looper 10 | import android.support.test.InstrumentationRegistry 11 | import android.support.test.rule.ActivityTestRule 12 | import android.support.test.runner.AndroidJUnit4 13 | import android.support.test.runner.lifecycle.ActivityLifecycleMonitorRegistry 14 | import android.support.test.runner.lifecycle.Stage 15 | import androidx.fragment.app.Fragment 16 | import androidx.fragment.app.FragmentActivity 17 | import com.evernote.android.state.State 18 | import com.evernote.android.state.StateSaver 19 | import org.assertj.core.api.Assertions.assertThat 20 | import org.junit.Rule 21 | import org.junit.Test 22 | import org.junit.runner.RunWith 23 | import java.util.concurrent.CountDownLatch 24 | 25 | /** 26 | * @author rwondratschek 27 | */ 28 | @RunWith(AndroidJUnit4::class) 29 | class GlobalStateSaverTest { 30 | 31 | @Rule 32 | @JvmField 33 | val activityRule = ActivityTestRule(MyActivity::class.java, true, false) 34 | 35 | @Test 36 | fun verifyStateSaverGloballyEnabled() { 37 | val context = InstrumentationRegistry.getTargetContext() 38 | StateSaver.setEnabledForAllActivitiesAndSupportFragments(context.applicationContext as Application, true) 39 | activityRule.launchActivity(Intent(context, MyActivity::class.java)) // launch manually delayed 40 | 41 | var count = 0 42 | 43 | repeat(4) { 44 | count++ 45 | assertThat(activity.state).isEqualTo(count) 46 | assertThat(activity.fragment.state).isEqualTo(count) 47 | assertThat(activity.fragment.innerFragment.state).isEqualTo(count) 48 | 49 | changeOrientation() 50 | } 51 | } 52 | 53 | @Test 54 | fun verifyStateSaverGloballyDisabled() { 55 | val context = InstrumentationRegistry.getTargetContext() 56 | StateSaver.setEnabledForAllActivitiesAndSupportFragments(context.applicationContext as Application, false) 57 | activityRule.launchActivity(Intent(context, MyActivity::class.java)) // launch manually delayed 58 | 59 | val count = 1 60 | 61 | repeat(4) { 62 | assertThat(activity.state).isEqualTo(count) 63 | assertThat(activity.fragment.state).isEqualTo(count) 64 | assertThat(activity.fragment.innerFragment.state).isEqualTo(count) 65 | 66 | changeOrientation() 67 | } 68 | } 69 | 70 | private fun changeOrientation() { 71 | activity.requestedOrientation = 72 | if (activity.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { 73 | ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 74 | } else { 75 | ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 76 | } 77 | 78 | InstrumentationRegistry.getInstrumentation().waitForIdleSync() 79 | Thread.sleep(1000) 80 | InstrumentationRegistry.getInstrumentation().waitForIdleSync() 81 | } 82 | 83 | private val activity: MyActivity 84 | get() { 85 | return if (Looper.getMainLooper() == Looper.myLooper()) { 86 | var result: Activity? = null 87 | for (i in 1..4) { 88 | result = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED).firstOrNull { it is MyActivity } 89 | if (result !is MyActivity) { 90 | Thread.sleep(1000) 91 | } 92 | } 93 | result as MyActivity 94 | } else { 95 | val latch = CountDownLatch(1) 96 | lateinit var result: MyActivity 97 | activityRule.runOnUiThread { 98 | result = activity 99 | latch.countDown() 100 | } 101 | latch.await() 102 | result 103 | } 104 | } 105 | } 106 | 107 | class MyActivity : FragmentActivity() { 108 | 109 | @State 110 | var state = 0 111 | 112 | val fragment: MyFragment get() = supportFragmentManager.findFragmentByTag("Tag") as MyFragment 113 | 114 | override fun onCreate(savedInstanceState: Bundle?) { 115 | super.onCreate(savedInstanceState) 116 | if (savedInstanceState == null) { 117 | supportFragmentManager.beginTransaction().add(MyFragment().apply { 118 | arguments = Bundle().apply { putBoolean("create_inner", true) } 119 | }, "Tag").commit() 120 | } 121 | state++ 122 | } 123 | } 124 | 125 | class MyFragment : Fragment() { 126 | 127 | @State 128 | var state = 0 129 | 130 | val innerFragment: MyFragment get() = fragmentManager!!.findFragmentByTag("Inner") as MyFragment 131 | 132 | override fun onCreate(savedInstanceState: Bundle?) { 133 | super.onCreate(savedInstanceState) 134 | if (savedInstanceState == null && arguments?.getBoolean("create_inner", false) == true) { 135 | fragmentManager!!.beginTransaction().add(MyFragment(), "Inner").commit() 136 | } 137 | state++ 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /gradle/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /library/src/main/java/com/evernote/android/state/StateSaverImpl.java: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Frankie Sardo, and Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Frankie Sardo - initial API and implementation 10 | * Ralf Wondratschek - documentation and feature enhancement 11 | *******************************************************************************/ 12 | package com.evernote.android.state; 13 | 14 | import android.app.Application; 15 | import android.os.Bundle; 16 | import android.os.Parcelable; 17 | import androidx.annotation.NonNull; 18 | import androidx.annotation.Nullable; 19 | import android.view.View; 20 | 21 | import java.util.Map; 22 | 23 | /*package*/ @SuppressWarnings({"unused", "WeakerAccess"}) 24 | final class StateSaverImpl { 25 | 26 | private final Map, Injector> mInjectors; 27 | 28 | /*package*/ StateSaverImpl(Map, Injector> injectors) { 29 | mInjectors = injectors; 30 | } 31 | 32 | private Injector getInjector(Class cls) { 33 | Injector injector = mInjectors.get(cls); 34 | if (injector != null || mInjectors.containsKey(cls)) { 35 | return injector; 36 | } 37 | String clsName = cls.getName(); 38 | if (clsName.startsWith(StateSaver.ANDROID_PREFIX) || clsName.startsWith(StateSaver.JAVA_PREFIX)) { 39 | return null; 40 | } 41 | try { 42 | Class injectorClass = Class.forName(clsName + StateSaver.SUFFIX); 43 | injector = (Injector) injectorClass.newInstance(); 44 | } catch (Exception e) { 45 | injector = getInjector(cls.getSuperclass()); 46 | } 47 | mInjectors.put(cls, injector); 48 | return injector; 49 | } 50 | 51 | @SuppressWarnings("unchecked") 52 | private T safeGet(Object target, Injector nop) { 53 | try { 54 | Class targetClass = target.getClass(); 55 | Injector injector = getInjector(targetClass); 56 | if (injector == null) { 57 | injector = nop; 58 | } 59 | return (T) injector; 60 | } catch (Exception e) { 61 | throw new RuntimeException("Unable to inject state for " + target, e); 62 | } 63 | } 64 | 65 | /** 66 | * Save the given {@code target} in the passed in {@link Bundle}. 67 | * 68 | * @param target The object containing fields annotated with {@link State}. 69 | * @param state The object is saved into this bundle. 70 | */ 71 | /*package*/ void saveInstanceState(@NonNull T target, @NonNull Bundle state) { 72 | Injector.Object injector = safeGet(target, Injector.Object.DEFAULT); 73 | injector.save(target, state); 74 | } 75 | 76 | /** 77 | * Restore the given {@code target} from the passed in {@link Bundle}. 78 | * 79 | * @param target The object containing fields annotated with {@link State}. 80 | * @param state The object is being restored from this bundle. Nothing is restored if the argument is {@code null}. 81 | */ 82 | /*package*/ void restoreInstanceState(@NonNull T target, @Nullable Bundle state) { 83 | if (state != null) { 84 | Injector.Object injector = safeGet(target, Injector.Object.DEFAULT); 85 | injector.restore(target, state); 86 | } 87 | } 88 | 89 | /** 90 | * Save the state of the given view and the other state inside of the returned {@link Parcelable}. 91 | * 92 | * @param target The view containing fields annotated with {@link State}. 93 | * @param state The super state of the parent class of the view. Usually it isn't {@code null}. 94 | * @return A parcelable containing the view's state and its super state. 95 | */ 96 | @NonNull 97 | /*package*/ Parcelable saveInstanceState(@NonNull T target, @Nullable Parcelable state) { 98 | Injector.View injector = safeGet(target, Injector.View.DEFAULT); 99 | return injector.save(target, state); 100 | } 101 | 102 | /** 103 | * Restore the sate of the given view and return the super state of the parent class. 104 | * 105 | * @param target The view containing fields annotated with {@link State}. 106 | * @param state The parcelable containing the view's state and its super sate. 107 | * @return The super state of teh parent class of the view. Usually it isn't {@code null}. 108 | */ 109 | @Nullable 110 | /*package*/ Parcelable restoreInstanceState(@NonNull T target, @Nullable Parcelable state) { 111 | if (state != null) { 112 | Injector.View injector = safeGet(target, Injector.View.DEFAULT); 113 | return injector.restore(target, state); 114 | } else { 115 | return null; 116 | } 117 | } 118 | 119 | /** 120 | * Turns automatic instance saving on or off for all activities and fragments from the support library. This avoids 121 | * manual calls of {@link #saveInstanceState(Object, Bundle)} and {@link #restoreInstanceState(Object, Bundle)}, instead 122 | * the library is doing this automatically for you. It's still necessary to annotate fields with {@link State}, though. 123 | *
124 | *
125 | * The best place to turn on this feature is in your {@link Application#onCreate()} method. 126 | * 127 | * @param application Your application instance. 128 | * @param enabled Whether this feature should be enabled. 129 | */ 130 | /*package*/ void setEnabledForAllActivitiesAndSupportFragments(@NonNull Application application, boolean enabled) { 131 | if (AndroidLifecycleCallbacks.INSTANCE.mEnabled != enabled) { 132 | if (enabled) { 133 | application.registerActivityLifecycleCallbacks(AndroidLifecycleCallbacks.INSTANCE); 134 | } else { 135 | application.unregisterActivityLifecycleCallbacks(AndroidLifecycleCallbacks.INSTANCE); 136 | } 137 | AndroidLifecycleCallbacks.INSTANCE.mEnabled = enabled; 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | This library is not maintained anymore. 4 | 5 | # ~~Android-State~~ 6 | 7 | A utility library for Android to save objects in a `Bundle` without any boilerplate. It uses an annotation processor to wire up all dependencies. 8 | 9 | ## Download 10 | 11 | Download the latest [library](https://search.maven.org/#search%7Cga%7C1%7Ca%3A%22android-state%22) and [processor](https://search.maven.org/#search%7Cga%7C1%7Ca%3A%22android-state-processor%22) or grab via Gradle: 12 | 13 | ```groovy 14 | dependencies { 15 | implementation 'com.evernote:android-state:1.4.1' 16 | // Java only project 17 | annotationProcessor 'com.evernote:android-state-processor:1.4.1' 18 | 19 | // Kotlin with or without Java 20 | kapt 'com.evernote:android-state-processor:1.4.1' 21 | } 22 | ``` 23 | 24 | You can read the [JavaDoc here](https://evernote.github.io/android-state/javadoc/). 25 | 26 | ## Usage 27 | 28 | It's recommended to turn on a global setting in your `Application` class to save and restore the instance state of all activities and fragments from the support library. After that you only need to annotate your fields with `@State` inside of your fragments and activities. You can save any type which can be saved in a `Bundle` like the `String`, `Serializable`, and `Parcelable` data structures. 29 | 30 | ```java 31 | public class App extends Application { 32 | @Override 33 | public void onCreate() { 34 | super.onCreate(); 35 | StateSaver.setEnabledForAllActivitiesAndSupportFragments(this, true); 36 | } 37 | } 38 | 39 | public class MainActivity extends Activity { 40 | 41 | @State 42 | public int mValue; 43 | 44 | // ... 45 | } 46 | 47 | class MainFragment : Fragment() { 48 | 49 | @State 50 | var title = "My fragment" 51 | } 52 | ``` 53 | 54 | If you want to save the state of any other object or didn't turn on the global setting, then you need to use the `StateSaver` class for manually saving and restoring the instance state. 55 | 56 | ```java 57 | public class MainActivity extends Activity { 58 | 59 | @State 60 | public int mValue; 61 | 62 | @Override 63 | protected void onCreate(Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | StateSaver.restoreInstanceState(this, savedInstanceState); 66 | } 67 | 68 | @Override 69 | protected void onSaveInstanceState(Bundle outState) { 70 | super.onSaveInstanceState(outState); 71 | StateSaver.saveInstanceState(this, outState); 72 | } 73 | } 74 | ``` 75 | 76 | ## Advanced 77 | 78 | You can also save state in a `View` class. 79 | 80 | ```java 81 | public class TestView extends View { 82 | 83 | @State 84 | public int mState; 85 | 86 | public TestView(Context context) { 87 | super(context); 88 | } 89 | 90 | @Override 91 | protected Parcelable onSaveInstanceState() { 92 | return StateSaver.saveInstanceState(this, super.onSaveInstanceState()); 93 | } 94 | 95 | @Override 96 | protected void onRestoreInstanceState(Parcelable state) { 97 | super.onRestoreInstanceState(StateSaver.restoreInstanceState(this, state)); 98 | } 99 | } 100 | ``` 101 | 102 | It is recommended that saved properties not be `private`. If a property is `private`, then a non-private getter and setter method are required. This is especially useful for Kotlin, because properties are `private` by default and the aforementioned methods are generated by the compiler. 103 | 104 | If you **want** your getter and setter to be used rather than the field value being used directly, the field **must** be private. 105 | 106 | ```kotlin 107 | class DemoPresenter : Presenter() { 108 | 109 | @State 110 | var counter = 0 111 | 112 | // ... 113 | } 114 | 115 | ``` 116 | 117 | Of course, this also works in Java. 118 | 119 | ```java 120 | public class TitleUpdater { 121 | 122 | @State 123 | private String mTitle; 124 | 125 | public String getTitle() { 126 | return mTitle; 127 | } 128 | 129 | public void setTitle(String title) { 130 | mTitle = title; 131 | } 132 | } 133 | ``` 134 | 135 | If you have a private field and don't want to provide a getter or setter method, then you can fallback to reflection. However, this method is not recommended. 136 | 137 | ```java 138 | public class ImageProcessor { 139 | 140 | @StateReflection 141 | private byte[] mImageData; 142 | 143 | // ... 144 | } 145 | ``` 146 | 147 | A custom bundler can be useful, if a class doesn't implement the `Parcelable` or `Serializable` interface, which oftentimes happens with third party dependencies. 148 | 149 | ```java 150 | public class MappingProvider { 151 | 152 | @State(PairBundler.class) 153 | public Pair mMapping; 154 | 155 | public static final class PairBundler implements Bundler> { 156 | @Override 157 | public void put(@NonNull String key, @NonNull Pair value, @NonNull Bundle bundle) { 158 | bundle.putString(key + "first", value.first); 159 | bundle.putInt(key + "second", value.second); 160 | } 161 | 162 | @Nullable 163 | @Override 164 | public Pair get(@NonNull String key, @NonNull Bundle bundle) { 165 | if (bundle.containsKey(key + "first")) { 166 | return new Pair<>(bundle.getString(key + "first"), bundle.getInt(key + "second")); 167 | } else { 168 | return null; 169 | } 170 | } 171 | } 172 | } 173 | ``` 174 | 175 | ### Lint 176 | 177 | The library comes with Lint rules to verify a correct usage of the library. The lint checks work in Java and Kotlin files. 178 | 179 | ### ProGuard 180 | 181 | This library comes with a ProGuard config. No further steps are required, but all necessary rules can be found [here](library/proguard.cfg). 182 | 183 | ## [Icepick](https://github.com/frankiesardo/icepick) 184 | 185 | This library is based on [Icepick](https://github.com/frankiesardo/icepick), a great library from Frankie Sardo. However, Icepick is missing some features important to us: it [doesn't support properties](https://github.com/frankiesardo/icepick/issues/81) which is a [bummer for Kotlin](https://github.com/frankiesardo/icepick/issues/47). Also, Icepick does not support private fields which may break encapsulation. A tool shouldn't force you into this direction. 186 | 187 | Since Icepick is implemented in Clojure, we decided that it's better for us to rewrite the annotation processor in Java. Unfortunately, that makes it hard to push our features into Icepick itself. That's why we decided to fork the project. 188 | 189 | There are also alternatives for Kotlin like [IceKick](https://github.com/tinsukE/icekick). We did not want to use two libraries to solve the same problem for two different languages; we wanted to have one solution for all scenarios. 190 | 191 | Upgrading to this library from Icepick is easy. The API is the same; only the packages and the class name (i.e. from `Icepick` to `StateSaver`) have changed. If Icepick works well for you, then there's no need to upgrade. 192 | 193 | ## License 194 | 195 | ``` 196 | Copyright (c) 2017 Evernote Corporation. 197 | All rights reserved. This program and the accompanying materials 198 | are made available under the terms of the Eclipse Public License v1.0 199 | which accompanies this distribution, and is available at 200 | 201 | http://www.eclipse.org/legal/epl-v10.html 202 | 203 | Files produced by Android-State code generator are not subject to terms 204 | of the Eclipse Public License 1.0 and can be used as set out in the 205 | copyright notice included in the generated files. 206 | ``` 207 | -------------------------------------------------------------------------------- /library-lint/src/test/kotlin/com/evernote/android/state/lint/detectors/NonMatchingStateSaverDetectorTest.kt: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.lint.detectors 2 | 3 | import com.evernote.android.state.lint.AbstractDetectorTest 4 | import com.evernote.android.state.lint.detector.AndroidStateDetector 5 | import org.assertj.core.api.Assertions.assertThat 6 | 7 | /** 8 | * Created by junchengc on 5/11/17. 9 | */ 10 | 11 | private const val NO_WARNINGS = "No warnings." 12 | 13 | class NonMatchingStateSaverDetectorTest : AbstractDetectorTest() { 14 | 15 | private val stateSaverJava = getTestFile("stub/StateSaver.java") 16 | 17 | override fun getDetector() = AndroidStateDetector() 18 | override fun getIssues() = listOf(AndroidStateDetector.ISSUE) 19 | 20 | fun testEmpty() { 21 | assertThat(lintFiles(getTestFile("Empty.java"))).isEqualTo(NO_WARNINGS) 22 | } 23 | 24 | fun testValidActivity() { 25 | assertThat(lintFiles(stateSaverJava, getTestFile("ValidActivity.java"))).isEqualTo(NO_WARNINGS) 26 | } 27 | 28 | fun testValidActivityKotlin() { 29 | assertThat(lintFiles(stateSaverJava, getTestFile("ValidActivityKt.kt"))).isEqualTo(NO_WARNINGS) 30 | } 31 | 32 | fun testValidActivityDirectImport() { 33 | assertThat(lintFiles(stateSaverJava, getTestFile("ValidActivityDirectImport.java"))).isEqualTo(NO_WARNINGS) 34 | } 35 | 36 | fun testValidActivityDirectImportKotlin() { 37 | assertThat(lintFiles(stateSaverJava, getTestFile("ValidActivityDirectImportKt.kt"))).isEqualTo(NO_WARNINGS) 38 | } 39 | 40 | fun testValidActivityOtherMethod() { 41 | assertThat(lintFiles(stateSaverJava, getTestFile("ValidActivityOtherMethod.java"))).isEqualTo(NO_WARNINGS) 42 | } 43 | 44 | fun testValidActivityOtherMethodKotlin() { 45 | assertThat(lintFiles(stateSaverJava, getTestFile("ValidActivityOtherMethodKt.kt"))).isEqualTo(NO_WARNINGS) 46 | } 47 | 48 | fun testInvalidActivityNoSave() { 49 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoSave.java"))).isEqualTo( 50 | """ 51 | InvalidActivityNoSave.java:24: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 52 | StateSaver.restoreInstanceState(this, savedInstanceState); 53 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 | 0 errors, 1 warnings 55 | 56 | """.trimIndent() 57 | ) 58 | } 59 | 60 | fun testInvalidActivityNoSaveKotlin() { 61 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoSaveKt.kt"))).isEqualTo( 62 | """ 63 | InvalidActivityNoSaveKt.kt:23: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 64 | StateSaver.restoreInstanceState(this, savedInstanceState) 65 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 66 | 0 errors, 1 warnings 67 | 68 | """.trimIndent() 69 | ) 70 | } 71 | 72 | fun testInvalidActivityNoRestore() { 73 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoRestore.java"))).isEqualTo( 74 | """ 75 | InvalidActivityNoRestore.java:29: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 76 | StateSaver.saveInstanceState(this, outState); 77 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 78 | 0 errors, 1 warnings 79 | 80 | """.trimIndent() 81 | ) 82 | } 83 | 84 | fun testInvalidActivityNoRestoreKotlin() { 85 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoRestoreKt.kt"))).isEqualTo( 86 | """ 87 | InvalidActivityNoRestoreKt.kt:27: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 88 | StateSaver.saveInstanceState(this, outState) 89 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 90 | 0 errors, 1 warnings 91 | 92 | """.trimIndent() 93 | ) 94 | } 95 | 96 | fun testInvalidActivityNoRestoreDirectImport() { 97 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoRestoreDirectImport.java"))).isEqualTo( 98 | """ 99 | InvalidActivityNoRestoreDirectImport.java:30: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 100 | saveInstanceState(this, outState); 101 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 102 | 0 errors, 1 warnings 103 | 104 | """.trimIndent() 105 | ) 106 | } 107 | 108 | fun testInvalidActivityNoRestoreDirectImportKotlin() { 109 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoRestoreDirectImportKt.kt"))).isEqualTo( 110 | """ 111 | InvalidActivityNoRestoreDirectImportKt.kt:27: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 112 | saveInstanceState(this, outState) 113 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 114 | 0 errors, 1 warnings 115 | 116 | """.trimIndent() 117 | ) 118 | } 119 | 120 | fun testInvalidActivityOtherMethod() { 121 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityOtherMethod.java"))).isEqualTo( 122 | """ 123 | InvalidActivityOtherMethod.java:30: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 124 | StateSaver.saveInstanceState(this, outState); 125 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 126 | 0 errors, 1 warnings 127 | 128 | """.trimIndent() 129 | ) 130 | } 131 | 132 | fun testInvalidActivityOtherMethodKotlin() { 133 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityOtherMethodKt.kt"))).isEqualTo( 134 | """ 135 | InvalidActivityOtherMethodKt.kt:28: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 136 | StateSaver.saveInstanceState(this, outState) 137 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 138 | 0 errors, 1 warnings 139 | 140 | """.trimIndent() 141 | ) 142 | } 143 | 144 | fun testMultipleFiles() { 145 | assertThat(lintFiles(stateSaverJava, getTestFile("InvalidActivityNoRestoreKt.kt"), getTestFile("InvalidActivityNoSave.java"))).isEqualTo( 146 | """ 147 | InvalidActivityNoRestoreKt.kt:27: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 148 | StateSaver.saveInstanceState(this, outState) 149 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 150 | InvalidActivityNoSave.java:24: Warning: StateSaver calls should always occur in pairs. StateSaver.saveInstanceState() should always have a matching call to StateSaver.restoreInstanceState(). [NonMatchingStateSaverCalls] 151 | StateSaver.restoreInstanceState(this, savedInstanceState); 152 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 153 | 0 errors, 2 warnings 154 | 155 | """.trimIndent() 156 | ) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /gradle/gradle-push.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | apply plugin: 'signing' 3 | 4 | @SuppressWarnings(["GrMethodMayBeStatic", "GroovyUnusedDeclaration"]) 5 | def isReleaseBuild() { 6 | return !VERSION_NAME.contains("SNAPSHOT") 7 | } 8 | 9 | def isAndroidProject() { 10 | return project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library') 11 | } 12 | 13 | def isLocal() { 14 | return hasProperty('local') ? Boolean.parseBoolean(getProperty('local')) : true 15 | } 16 | 17 | def getReleaseRepositoryUrl() { 18 | if (this.isLocal()) { 19 | return hasProperty('RELEASE_REPOSITORY_LOCAL') ? RELEASE_REPOSITORY_LOCAL : "file://${System.env.HOME}/.m2/repository" 20 | } else { 21 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 22 | } 23 | } 24 | 25 | def getSnapshotRepositoryUrl() { 26 | if (this.isLocal()) { 27 | return hasProperty('SNAPSHOT_REPOSITORY_LOCAL') ? SNAPSHOT_REPOSITORY_LOCAL : "file://${System.env.HOME}/.m2/repository" 28 | } else { 29 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : "https://oss.sonatype.org/content/repositories/snapshots/" 30 | } 31 | } 32 | 33 | def getRepositoryUsername() { 34 | return hasProperty('SONATYPE_USERNAME') ? SONATYPE_USERNAME : "" 35 | } 36 | 37 | def getRepositoryPassword() { 38 | return hasProperty('SONATYPE_PASSWORD') ? SONATYPE_PASSWORD : "" 39 | } 40 | 41 | if (isAndroidProject()) { 42 | android.libraryVariants.all { variant -> 43 | variant.outputs.each { output -> 44 | File outputFile = output.outputFile 45 | if (outputFile != null && outputFile.name.endsWith('.aar') && 'release' == variant.buildType.name) { 46 | output.outputFileName = "${project.archivesBaseName}-${version}.aar" 47 | } 48 | } 49 | } 50 | } 51 | 52 | version = VERSION_NAME 53 | 54 | artifacts { 55 | archives file("$buildDir/outputs/jar/${project.archivesBaseName}-${project.version}-sources.jar") 56 | archives file("$buildDir/outputs/jar/${project.archivesBaseName}-${project.version}-javadoc.jar") 57 | } 58 | 59 | afterEvaluate { project -> 60 | uploadArchives { 61 | //noinspection GradleMisplacedStatement 62 | repositories { 63 | //noinspection GroovyAssignabilityCheck 64 | mavenDeployer { 65 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 66 | 67 | pom.groupId = 'com.evernote' 68 | pom.artifactId = POM_ARTIFACT_ID 69 | pom.version = VERSION_NAME 70 | 71 | repository(url: this.getReleaseRepositoryUrl()) { 72 | authentication(userName: this.getRepositoryUsername(), password: this.getRepositoryPassword()) 73 | } 74 | snapshotRepository(url: this.getSnapshotRepositoryUrl()) { 75 | authentication(userName: this.getRepositoryUsername(), password: this.getRepositoryPassword()) 76 | } 77 | 78 | pom.project { 79 | name POM_NAME 80 | packaging POM_PACKAGING 81 | description 'Android library to save object states into a bundle.' 82 | url 'https://github.com/evernote/android-state' 83 | inceptionYear '2017' 84 | 85 | scm { 86 | url 'https://github.com/evernote/android-state' 87 | connection 'scm:git:git://github.com/evernote/android-state.git' 88 | developerConnection 'scm:git:git@github.com:evernote/android-state.git' 89 | } 90 | 91 | developers { 92 | developer { 93 | name 'Ralf Wondratschek' 94 | email 'rwondratschek@evernote.com' 95 | id 'rwondratschek' 96 | url 'http://vrallev.net' 97 | timezone '+1' 98 | roles { 99 | role 'developer' 100 | } 101 | } 102 | } 103 | 104 | licenses { 105 | license { 106 | name 'Eclipse Public License - v 1.0' 107 | url 'https://github.com/evernote/android-state/blob/master/LICENSE' 108 | distribution 'repo' 109 | } 110 | } 111 | 112 | issueManagement { 113 | system 'GitHub Issues' 114 | url 'https://github.com/evernote/android-state/issues' 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | signing { 122 | required { gradle.taskGraph.hasTask("uploadArchives") } 123 | sign configurations.archives 124 | } 125 | } 126 | 127 | project.tasks.whenTaskAdded { task -> 128 | if (task.name == 'signArchives') { 129 | this.addSourcesAndJavadocTask task 130 | } 131 | } 132 | 133 | def addSourcesAndJavadocTask(Task uploadTask) { 134 | if (this.isAndroidProject()) { 135 | android.libraryVariants.all { variant -> 136 | // javadoc jar 137 | project.task("${variant.name.capitalize()}Javadoc", type: Javadoc) { 138 | failOnError true 139 | 140 | destinationDir = new File("$project.buildDir/javadoc/$variant.name") 141 | 142 | source = variant.javaCompile.source 143 | 144 | ext.androidJar = "${project.android.sdkDirectory}/platforms/${project.android.compileSdkVersion}/android.jar" 145 | 146 | options { 147 | linksOffline("http://d.android.com/reference", "${project.android.sdkDirectory}/docs/reference") 148 | links("http://docs.oracle.com/javase/7/docs/api/") 149 | setMemberLevel(JavadocMemberLevel.PACKAGE) 150 | addStringOption('Xdoclint:none', '-quiet') 151 | docEncoding = 'UTF-8' 152 | encoding = 'UTF-8' 153 | charSet = 'UTF-8' 154 | } 155 | 156 | exclude '**/BuildConfig.java' 157 | exclude '**/R.java' 158 | } 159 | 160 | project.task("generate${variant.name.capitalize()}JavadocJar", type: Jar, dependsOn: "${variant.name.capitalize()}Javadoc") { 161 | classifier 'javadoc' 162 | 163 | description = 'Assembles a jar archive containing the generated Javadoc API documentation of $variant.name.' 164 | 165 | destinationDir = new File("$project.buildDir/outputs/jar/") 166 | 167 | exclude '**/BuildConfig.class' 168 | exclude '**/R.class' 169 | 170 | from "$project.buildDir/javadoc/$variant.name" 171 | } 172 | 173 | // sources jar 174 | project.task("generate${variant.name.capitalize()}SourcesJar", type: Jar) { 175 | classifier = 'sources' 176 | 177 | description = 'Assembles a jar archive containing the main sources of $variant.name..' 178 | 179 | destinationDir = new File("$project.buildDir/outputs/jar/") 180 | 181 | // exclude generated files 182 | exclude '**/BuildConfig.java' 183 | exclude '**/R.java' 184 | 185 | from variant.javaCompile.source 186 | } 187 | 188 | if (variant.name.equalsIgnoreCase('release')) { 189 | uploadTask.dependsOn project.tasks.getByName("assemble${variant.name.capitalize()}") 190 | uploadTask.dependsOn project.tasks.getByName("generate${variant.name.capitalize()}JavadocJar") 191 | uploadTask.dependsOn project.tasks.getByName("generate${variant.name.capitalize()}SourcesJar") 192 | } 193 | } 194 | } else { 195 | task sourcesJar(type: Jar, dependsOn: classes) { 196 | classifier = 'sources' 197 | destinationDir = new File("$project.buildDir/outputs/jar/") 198 | from sourceSets.main.allSource 199 | } 200 | 201 | task javadocJar(type: Jar, dependsOn: javadoc) { 202 | classifier = 'javadoc' 203 | destinationDir = new File("$project.buildDir/outputs/jar/") 204 | from javadoc.destinationDir 205 | } 206 | 207 | if (JavaVersion.current().isJava8Compatible()) { 208 | allprojects { 209 | tasks.withType(Javadoc) { 210 | options.addStringOption('Xdoclint:none', '-quiet') 211 | } 212 | } 213 | } 214 | 215 | uploadTask.dependsOn project.tasks.getByName("jar") 216 | uploadTask.dependsOn project.tasks.getByName("javadocJar") 217 | uploadTask.dependsOn project.tasks.getByName("sourcesJar") 218 | } 219 | } -------------------------------------------------------------------------------- /library/src/test/kotlin/com/evernote/android/state/test/KotlinBundlingTest.kt: -------------------------------------------------------------------------------- 1 | /* ***************************************************************************** 2 | * Copyright (c) 2017 Evernote Corporation. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Eclipse Public License v1.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.eclipse.org/legal/epl-v10.html 7 | * 8 | * Contributors: 9 | * Ralf Wondratschek 10 | *******************************************************************************/ 11 | @file:Suppress("UsePropertyAccessSyntax") 12 | 13 | package com.evernote.android.state.test 14 | 15 | import android.os.Bundle 16 | import com.evernote.android.state.StateSaver 17 | import org.assertj.core.api.Assertions.assertThat 18 | import org.junit.FixMethodOrder 19 | import org.junit.Test 20 | import org.junit.runners.MethodSorters 21 | 22 | /** 23 | * Copyright 2017 Evernote Corporation. All rights reserved. 24 | * 25 | * Created by rwondratschek on 2/13/17. 26 | */ 27 | @FixMethodOrder(MethodSorters.JVM) 28 | class KotlinBundlingTest { 29 | 30 | @Test 31 | fun testKotlinList() { 32 | val kotlinList = TestKotlinList() 33 | assertThat(kotlinList.emptyList).isEmpty() 34 | assertThat(kotlinList.stringList).isNotEmpty() 35 | assertThat(kotlinList.parcelableList[0]).isEqualTo(TestTypes.ParcelableImpl(1)) 36 | 37 | val bundle = Bundle() 38 | StateSaver.saveInstanceState(kotlinList, bundle) 39 | 40 | kotlinList.stringList = mutableListOf("single") 41 | kotlinList.parcelableList = mutableListOf(TestTypes.ParcelableImpl(2)) 42 | assertThat(kotlinList.stringList).containsExactly("single") 43 | assertThat(kotlinList.parcelableList[0]).isEqualTo(TestTypes.ParcelableImpl(2)) 44 | 45 | StateSaver.restoreInstanceState(kotlinList, bundle) 46 | assertThat(kotlinList.stringList).containsExactly("Hello", "World") 47 | assertThat(kotlinList.parcelableList[0]).isEqualTo(TestTypes.ParcelableImpl(1)) 48 | } 49 | 50 | @Test 51 | fun testJavaList() { 52 | val kotlinList = com.evernote.android.state.test.TestJavaList() 53 | assertThat(kotlinList.emptyList).isEmpty() 54 | assertThat(kotlinList.stringList).isNotEmpty() 55 | assertThat(kotlinList.parcelableList[0]).isEqualTo(TestTypes.ParcelableImpl(1)) 56 | assertThat(kotlinList.parcelableListPublic[0]).isEqualTo(TestTypes.ParcelableImpl(1)) 57 | 58 | val bundle = Bundle() 59 | StateSaver.saveInstanceState(kotlinList, bundle) 60 | 61 | kotlinList.stringList = mutableListOf("single") 62 | kotlinList.parcelableList = mutableListOf(TestTypes.ParcelableImpl(2)) 63 | kotlinList.parcelableListPublic = mutableListOf(TestTypes.ParcelableImpl(2)) 64 | assertThat(kotlinList.stringList).containsExactly("single") 65 | assertThat(kotlinList.parcelableList[0]).isEqualTo(TestTypes.ParcelableImpl(2)) 66 | assertThat(kotlinList.parcelableListPublic[0]).isEqualTo(TestTypes.ParcelableImpl(2)) 67 | 68 | StateSaver.restoreInstanceState(kotlinList, bundle) 69 | assertThat(kotlinList.stringList).containsExactly("Hello", "World") 70 | assertThat(kotlinList.parcelableList[0]).isEqualTo(TestTypes.ParcelableImpl(1)) 71 | assertThat(kotlinList.parcelableListPublic[0]).isEqualTo(TestTypes.ParcelableImpl(1)) 72 | } 73 | 74 | @Test 75 | fun testKotlinEnum() { 76 | val kotlinEnum = TestKotlinEnum() 77 | assertThat(kotlinEnum.kotlinEnum).isEqualTo(KotlinEnum.LEFT) 78 | assertThat(kotlinEnum.getKotlinEnum1()).isEqualTo(KotlinEnum.LEFT) 79 | 80 | val bundle = Bundle() 81 | StateSaver.saveInstanceState(kotlinEnum, bundle) 82 | 83 | kotlinEnum.kotlinEnum = KotlinEnum.RIGHT 84 | kotlinEnum.setKotlinEnum1(KotlinEnum.RIGHT) 85 | StateSaver.restoreInstanceState(kotlinEnum, bundle) 86 | assertThat(kotlinEnum.kotlinEnum).isEqualTo(KotlinEnum.LEFT) 87 | assertThat(kotlinEnum.getKotlinEnum1()).isEqualTo(KotlinEnum.LEFT) 88 | 89 | kotlinEnum.kotlinEnum = KotlinEnum.RIGHT 90 | kotlinEnum.setKotlinEnum1(KotlinEnum.RIGHT) 91 | StateSaver.saveInstanceState(kotlinEnum, bundle) 92 | 93 | kotlinEnum.kotlinEnum = KotlinEnum.LEFT 94 | kotlinEnum.setKotlinEnum1(KotlinEnum.LEFT) 95 | StateSaver.restoreInstanceState(kotlinEnum, bundle) 96 | assertThat(kotlinEnum.kotlinEnum).isEqualTo(KotlinEnum.RIGHT) 97 | assertThat(kotlinEnum.getKotlinEnum1()).isEqualTo(KotlinEnum.RIGHT) 98 | } 99 | 100 | @Test 101 | fun testKotlinBoolean() { 102 | val kotlinBoolean = TestKotlinBoolean() 103 | assertThat(kotlinBoolean.test1).isFalse() 104 | assertThat(kotlinBoolean.isTest2).isFalse() 105 | assertThat(kotlinBoolean.mTest3).isFalse() 106 | 107 | val bundle = Bundle() 108 | StateSaver.saveInstanceState(kotlinBoolean, bundle) 109 | 110 | kotlinBoolean.test1 = true 111 | kotlinBoolean.isTest2 = true 112 | kotlinBoolean.mTest3 = true 113 | 114 | StateSaver.restoreInstanceState(kotlinBoolean, bundle) 115 | assertThat(kotlinBoolean.test1).isFalse() 116 | assertThat(kotlinBoolean.isTest2).isFalse() 117 | assertThat(kotlinBoolean.mTest3).isFalse() 118 | 119 | kotlinBoolean.test1 = true 120 | kotlinBoolean.isTest2 = true 121 | kotlinBoolean.mTest3 = true 122 | StateSaver.saveInstanceState(kotlinBoolean, bundle) 123 | 124 | kotlinBoolean.test1 = false 125 | kotlinBoolean.isTest2 = false 126 | kotlinBoolean.mTest3 = false 127 | 128 | StateSaver.restoreInstanceState(kotlinBoolean, bundle) 129 | assertThat(kotlinBoolean.test1).isTrue() 130 | assertThat(kotlinBoolean.isTest2).isTrue() 131 | assertThat(kotlinBoolean.mTest3).isTrue() 132 | } 133 | 134 | @Test 135 | fun testGenericSerializable() { 136 | val javaGenericSerializable = TestJavaGenericSerializable() 137 | val kotlinGenericSerializable = TestKotlinGenericSerializable() 138 | assertThat(javaGenericSerializable.genericSerializable).isNull() 139 | assertThat(kotlinGenericSerializable.genericSerializable).isNull() 140 | 141 | val bundle = Bundle() 142 | StateSaver.saveInstanceState(javaGenericSerializable, bundle) 143 | StateSaver.saveInstanceState(kotlinGenericSerializable, bundle) 144 | 145 | javaGenericSerializable.genericSerializable = GenericSerializable() 146 | kotlinGenericSerializable.genericSerializable = GenericSerializable() 147 | 148 | StateSaver.restoreInstanceState(javaGenericSerializable, bundle) 149 | StateSaver.restoreInstanceState(kotlinGenericSerializable, bundle) 150 | assertThat(javaGenericSerializable.genericSerializable).isNull() 151 | assertThat(kotlinGenericSerializable.genericSerializable).isNull() 152 | 153 | javaGenericSerializable.genericSerializable = GenericSerializable() 154 | kotlinGenericSerializable.genericSerializable = GenericSerializable() 155 | StateSaver.saveInstanceState(javaGenericSerializable, bundle) 156 | StateSaver.saveInstanceState(kotlinGenericSerializable, bundle) 157 | 158 | javaGenericSerializable.genericSerializable = null 159 | kotlinGenericSerializable.genericSerializable = null 160 | 161 | StateSaver.restoreInstanceState(javaGenericSerializable, bundle) 162 | StateSaver.restoreInstanceState(kotlinGenericSerializable, bundle) 163 | assertThat(javaGenericSerializable.genericSerializable as Any).isNotNull() 164 | assertThat(kotlinGenericSerializable.genericSerializable as Any).isNotNull() 165 | } 166 | 167 | @Test 168 | fun testWithNoneConcreteType() { 169 | val item = MyClass() 170 | 171 | assertThat(item.wrapped.content).isEqualTo(42) 172 | assertThat(item.wrappedGeneric.content).isEqualTo(42) 173 | 174 | val bundle = Bundle() 175 | StateSaver.saveInstanceState(item, bundle) 176 | 177 | item.wrapped = Wrapper(10) 178 | item.wrappedGeneric = Wrapper(10) 179 | 180 | StateSaver.restoreInstanceState(item, bundle) 181 | assertThat(item.wrapped.content).isEqualTo(42) 182 | assertThat(item.wrappedGeneric.content).isEqualTo(42) 183 | 184 | item.wrapped = Wrapper(10) 185 | item.wrappedGeneric = Wrapper(10) 186 | StateSaver.saveInstanceState(item, bundle) 187 | 188 | item.wrapped = Wrapper(42) 189 | item.wrappedGeneric = Wrapper(42) 190 | 191 | StateSaver.restoreInstanceState(item, bundle) 192 | assertThat(item.wrapped.content).isEqualTo(10) 193 | assertThat(item.wrappedGeneric.content).isEqualTo(10) 194 | } 195 | 196 | @Test 197 | fun testPrivateInnerClass() { 198 | val item = TestKotlinPrivateInnerClass() 199 | assertThat(item.isA()).isTrue() 200 | 201 | val bundle = Bundle() 202 | StateSaver.saveInstanceState(item, bundle) 203 | 204 | item.setToB() 205 | 206 | StateSaver.restoreInstanceState(item, bundle) 207 | assertThat(item.isA()).isTrue() 208 | 209 | item.setToB() 210 | StateSaver.saveInstanceState(item, bundle) 211 | 212 | item.setToA() 213 | 214 | StateSaver.restoreInstanceState(item, bundle) 215 | assertThat(item.isB()).isTrue() 216 | } 217 | 218 | @Test 219 | fun testParcelableArray() { 220 | val item = TestKotlinParcelableArray() 221 | assertThat(item.isValue(0)).isTrue() 222 | 223 | val bundle = Bundle() 224 | StateSaver.saveInstanceState(item, bundle) 225 | 226 | item.setToValue(1) 227 | 228 | StateSaver.restoreInstanceState(item, bundle) 229 | assertThat(item.isValue(0)).isTrue() 230 | 231 | item.setToValue(1) 232 | StateSaver.saveInstanceState(item, bundle) 233 | 234 | item.setToValue(0) 235 | 236 | StateSaver.restoreInstanceState(item, bundle) 237 | assertThat(item.isValue(1)).isTrue() 238 | } 239 | } -------------------------------------------------------------------------------- /library/src/test/java/com/evernote/android/state/test/TestTypesProperty.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | import android.util.SparseArray; 7 | 8 | import com.evernote.android.state.State; 9 | 10 | import java.util.ArrayList; 11 | 12 | public class TestTypesProperty { 13 | @State 14 | private boolean mBoolean; 15 | @State 16 | private boolean[] mBooleanArray; 17 | @State 18 | private Boolean mBooleanObj; 19 | @State 20 | private byte mByte; 21 | @State 22 | private byte[] mByteArray; 23 | @State 24 | private Byte mByteObj; 25 | @State 26 | private char mChar; 27 | @State 28 | private char[] mCharArray; 29 | @State 30 | private Character mCharObj; 31 | @State 32 | private double mDouble; 33 | @State 34 | private double[] mDoubleArray; 35 | @State 36 | private Double mDoubleObj; 37 | @State 38 | private float mFloat; 39 | @State 40 | private float[] mFloatArray; 41 | @State 42 | private Float mFloatObj; 43 | @State 44 | private int mInt; 45 | @State 46 | private int[] mIntArray; 47 | @State 48 | private Integer mIntegerObj; 49 | @State 50 | private long mLong; 51 | @State 52 | private long[] mLongArray; 53 | @State 54 | private Long mLongObj; 55 | @State 56 | private short mShort; 57 | @State 58 | private short[] mShortArray; 59 | @State 60 | private Short mShortObj; 61 | @State 62 | private CharSequence mCharSequence; 63 | @State 64 | private CharSequence[] mCharSequenceArray; 65 | @State 66 | private String mString; 67 | @State 68 | private String[] mStringArray; 69 | @State 70 | private ArrayList mCharSequenceArrayList; 71 | @State 72 | private ArrayList mIntegerArrayList; 73 | @State 74 | private ArrayList mStringArrayList; 75 | @State 76 | private Bundle mBundle; 77 | @State 78 | private Parcelable[] mParcelableArray; 79 | @State 80 | private TestTypes.ParcelableImpl mParcelableImpl; 81 | @State 82 | private ParcelableImplExtension mParcelableImplExtension; 83 | @State 84 | private TestTypes.SerializableImpl mSerializableImpl; 85 | @State 86 | private ArrayList mParcelableArrayList; 87 | @State 88 | private SparseArray mParcelableSparseArray; 89 | 90 | public boolean isBoolean() { 91 | return mBoolean; 92 | } 93 | 94 | public void setBoolean(boolean aBoolean) { 95 | mBoolean = aBoolean; 96 | } 97 | 98 | public boolean[] getBooleanArray() { 99 | return mBooleanArray; 100 | } 101 | 102 | public void setBooleanArray(boolean[] booleanArray) { 103 | mBooleanArray = booleanArray; 104 | } 105 | 106 | public Boolean getBooleanObj() { 107 | return mBooleanObj; 108 | } 109 | 110 | public void setBooleanObj(Boolean booleanObj) { 111 | mBooleanObj = booleanObj; 112 | } 113 | 114 | public byte getByte() { 115 | return mByte; 116 | } 117 | 118 | public void setByte(byte aByte) { 119 | mByte = aByte; 120 | } 121 | 122 | public byte[] getByteArray() { 123 | return mByteArray; 124 | } 125 | 126 | public void setByteArray(byte[] byteArray) { 127 | mByteArray = byteArray; 128 | } 129 | 130 | public Byte getByteObj() { 131 | return mByteObj; 132 | } 133 | 134 | public void setByteObj(Byte byteObj) { 135 | mByteObj = byteObj; 136 | } 137 | 138 | public char getChar() { 139 | return mChar; 140 | } 141 | 142 | public void setChar(char aChar) { 143 | mChar = aChar; 144 | } 145 | 146 | public char[] getCharArray() { 147 | return mCharArray; 148 | } 149 | 150 | public void setCharArray(char[] charArray) { 151 | mCharArray = charArray; 152 | } 153 | 154 | public Character getCharObj() { 155 | return mCharObj; 156 | } 157 | 158 | public void setCharObj(Character charObj) { 159 | mCharObj = charObj; 160 | } 161 | 162 | public double getDouble() { 163 | return mDouble; 164 | } 165 | 166 | public void setDouble(double aDouble) { 167 | mDouble = aDouble; 168 | } 169 | 170 | public double[] getDoubleArray() { 171 | return mDoubleArray; 172 | } 173 | 174 | public void setDoubleArray(double[] doubleArray) { 175 | mDoubleArray = doubleArray; 176 | } 177 | 178 | public Double getDoubleObj() { 179 | return mDoubleObj; 180 | } 181 | 182 | public void setDoubleObj(Double doubleObj) { 183 | mDoubleObj = doubleObj; 184 | } 185 | 186 | public float getFloat() { 187 | return mFloat; 188 | } 189 | 190 | public void setFloat(float aFloat) { 191 | mFloat = aFloat; 192 | } 193 | 194 | public float[] getFloatArray() { 195 | return mFloatArray; 196 | } 197 | 198 | public void setFloatArray(float[] floatArray) { 199 | mFloatArray = floatArray; 200 | } 201 | 202 | public Float getFloatObj() { 203 | return mFloatObj; 204 | } 205 | 206 | public void setFloatObj(Float floatObj) { 207 | mFloatObj = floatObj; 208 | } 209 | 210 | public int getInt() { 211 | return mInt; 212 | } 213 | 214 | public void setInt(int anInt) { 215 | mInt = anInt; 216 | } 217 | 218 | public int[] getIntArray() { 219 | return mIntArray; 220 | } 221 | 222 | public void setIntArray(int[] intArray) { 223 | mIntArray = intArray; 224 | } 225 | 226 | public Integer getIntegerObj() { 227 | return mIntegerObj; 228 | } 229 | 230 | public void setIntegerObj(Integer integerObj) { 231 | mIntegerObj = integerObj; 232 | } 233 | 234 | public long getLong() { 235 | return mLong; 236 | } 237 | 238 | public void setLong(long aLong) { 239 | mLong = aLong; 240 | } 241 | 242 | public long[] getLongArray() { 243 | return mLongArray; 244 | } 245 | 246 | public void setLongArray(long[] longArray) { 247 | mLongArray = longArray; 248 | } 249 | 250 | public Long getLongObj() { 251 | return mLongObj; 252 | } 253 | 254 | public void setLongObj(Long longObj) { 255 | mLongObj = longObj; 256 | } 257 | 258 | public short getShort() { 259 | return mShort; 260 | } 261 | 262 | public void setShort(short aShort) { 263 | mShort = aShort; 264 | } 265 | 266 | public short[] getShortArray() { 267 | return mShortArray; 268 | } 269 | 270 | public void setShortArray(short[] shortArray) { 271 | mShortArray = shortArray; 272 | } 273 | 274 | public Short getShortObj() { 275 | return mShortObj; 276 | } 277 | 278 | public void setShortObj(Short shortObj) { 279 | mShortObj = shortObj; 280 | } 281 | 282 | public CharSequence getCharSequence() { 283 | return mCharSequence; 284 | } 285 | 286 | public void setCharSequence(CharSequence charSequence) { 287 | mCharSequence = charSequence; 288 | } 289 | 290 | public CharSequence[] getCharSequenceArray() { 291 | return mCharSequenceArray; 292 | } 293 | 294 | public void setCharSequenceArray(CharSequence[] charSequenceArray) { 295 | mCharSequenceArray = charSequenceArray; 296 | } 297 | 298 | public String getString() { 299 | return mString; 300 | } 301 | 302 | public void setString(String string) { 303 | mString = string; 304 | } 305 | 306 | public String[] getStringArray() { 307 | return mStringArray; 308 | } 309 | 310 | public void setStringArray(String[] stringArray) { 311 | mStringArray = stringArray; 312 | } 313 | 314 | public ArrayList getCharSequenceArrayList() { 315 | return mCharSequenceArrayList; 316 | } 317 | 318 | public void setCharSequenceArrayList(ArrayList charSequenceArrayList) { 319 | mCharSequenceArrayList = charSequenceArrayList; 320 | } 321 | 322 | public ArrayList getIntegerArrayList() { 323 | return mIntegerArrayList; 324 | } 325 | 326 | public void setIntegerArrayList(ArrayList integerArrayList) { 327 | mIntegerArrayList = integerArrayList; 328 | } 329 | 330 | public ArrayList getStringArrayList() { 331 | return mStringArrayList; 332 | } 333 | 334 | public void setStringArrayList(ArrayList stringArrayList) { 335 | mStringArrayList = stringArrayList; 336 | } 337 | 338 | public Bundle getBundle() { 339 | return mBundle; 340 | } 341 | 342 | public void setBundle(Bundle bundle) { 343 | mBundle = bundle; 344 | } 345 | 346 | public Parcelable[] getParcelableArray() { 347 | return mParcelableArray; 348 | } 349 | 350 | public void setParcelableArray(Parcelable[] parcelableArray) { 351 | mParcelableArray = parcelableArray; 352 | } 353 | 354 | public TestTypes.ParcelableImpl getParcelableImpl() { 355 | return mParcelableImpl; 356 | } 357 | 358 | public void setParcelableImpl(TestTypes.ParcelableImpl parcelableImpl) { 359 | mParcelableImpl = parcelableImpl; 360 | } 361 | 362 | public TestTypes.SerializableImpl getSerializableImpl() { 363 | return mSerializableImpl; 364 | } 365 | 366 | public void setSerializableImpl(TestTypes.SerializableImpl serializableImpl) { 367 | mSerializableImpl = serializableImpl; 368 | } 369 | 370 | public ArrayList getParcelableArrayList() { 371 | return mParcelableArrayList; 372 | } 373 | 374 | public void setParcelableArrayList(ArrayList parcelableArrayList) { 375 | mParcelableArrayList = parcelableArrayList; 376 | } 377 | 378 | public SparseArray getParcelableSparseArray() { 379 | return mParcelableSparseArray; 380 | } 381 | 382 | public void setParcelableSparseArray(SparseArray parcelableSparseArray) { 383 | mParcelableSparseArray = parcelableSparseArray; 384 | } 385 | 386 | public ParcelableImplExtension getParcelableImplExtension() { 387 | return mParcelableImplExtension; 388 | } 389 | 390 | public void setParcelableImplExtension(ParcelableImplExtension parcelableImplExtension) { 391 | mParcelableImplExtension = parcelableImplExtension; 392 | } 393 | 394 | public static class ParcelableImplExtension extends TestTypes.ParcelableImpl { 395 | private int mInt2; 396 | 397 | public ParcelableImplExtension(int anInt, int anInt2) { 398 | super(anInt); 399 | mInt2 = anInt2; 400 | } 401 | 402 | protected ParcelableImplExtension(Parcel in) { 403 | super(in); 404 | mInt2 = in.readInt(); 405 | } 406 | 407 | @Override 408 | public boolean equals(Object o) { 409 | if (this == o) return true; 410 | if (o == null || getClass() != o.getClass()) return false; 411 | if (!super.equals(o)) return false; 412 | 413 | ParcelableImplExtension that = (ParcelableImplExtension) o; 414 | 415 | return mInt2 == that.mInt2; 416 | } 417 | 418 | @Override 419 | public int hashCode() { 420 | int result = super.hashCode(); 421 | result = 31 * result + mInt2; 422 | return result; 423 | } 424 | 425 | @Override 426 | public void writeToParcel(Parcel dest, int flags) { 427 | super.writeToParcel(dest, flags); 428 | dest.writeInt(mInt2); 429 | } 430 | 431 | @Override 432 | public int describeContents() { 433 | return 0; 434 | } 435 | 436 | public static final Creator CREATOR = new Creator() { 437 | @Override 438 | public TestTypes.ParcelableImpl createFromParcel(Parcel in) { 439 | return new TestTypes.ParcelableImpl(in); 440 | } 441 | 442 | @Override 443 | public TestTypes.ParcelableImpl[] newArray(int size) { 444 | return new TestTypes.ParcelableImpl[size]; 445 | } 446 | }; 447 | } 448 | 449 | } 450 | -------------------------------------------------------------------------------- /demo-java-8/src/main/java/com/evernote/android/state/test/java8/TestTypesProperty.java: -------------------------------------------------------------------------------- 1 | package com.evernote.android.state.test.java8; 2 | 3 | import android.os.Bundle; 4 | import android.os.Parcel; 5 | import android.os.Parcelable; 6 | import android.util.SparseArray; 7 | 8 | import com.evernote.android.state.State; 9 | 10 | import java.util.ArrayList; 11 | 12 | public class TestTypesProperty { 13 | @State 14 | private boolean mBoolean; 15 | @State 16 | private boolean[] mBooleanArray; 17 | @State 18 | private Boolean mBooleanObj; 19 | @State 20 | private byte mByte; 21 | @State 22 | private byte[] mByteArray; 23 | @State 24 | private Byte mByteObj; 25 | @State 26 | private char mChar; 27 | @State 28 | private char[] mCharArray; 29 | @State 30 | private Character mCharObj; 31 | @State 32 | private double mDouble; 33 | @State 34 | private double[] mDoubleArray; 35 | @State 36 | private Double mDoubleObj; 37 | @State 38 | private float mFloat; 39 | @State 40 | private float[] mFloatArray; 41 | @State 42 | private Float mFloatObj; 43 | @State 44 | private int mInt; 45 | @State 46 | private int[] mIntArray; 47 | @State 48 | private Integer mIntegerObj; 49 | @State 50 | private long mLong; 51 | @State 52 | private long[] mLongArray; 53 | @State 54 | private Long mLongObj; 55 | @State 56 | private short mShort; 57 | @State 58 | private short[] mShortArray; 59 | @State 60 | private Short mShortObj; 61 | @State 62 | private CharSequence mCharSequence; 63 | @State 64 | private CharSequence[] mCharSequenceArray; 65 | @State 66 | private String mString; 67 | @State 68 | private String[] mStringArray; 69 | @State 70 | private ArrayList mCharSequenceArrayList; 71 | @State 72 | private ArrayList mIntegerArrayList; 73 | @State 74 | private ArrayList mStringArrayList; 75 | @State 76 | private Bundle mBundle; 77 | @State 78 | private Parcelable[] mParcelableArray; 79 | @State 80 | private TestTypes.ParcelableImpl mParcelableImpl; 81 | @State 82 | private ParcelableImplExtension mParcelableImplExtension; 83 | @State 84 | private TestTypes.SerializableImpl mSerializableImpl; 85 | @State 86 | private ArrayList mParcelableArrayList; 87 | @State 88 | private SparseArray mParcelableSparseArray; 89 | 90 | public boolean isBoolean() { 91 | return mBoolean; 92 | } 93 | 94 | public void setBoolean(boolean aBoolean) { 95 | mBoolean = aBoolean; 96 | } 97 | 98 | public boolean[] getBooleanArray() { 99 | return mBooleanArray; 100 | } 101 | 102 | public void setBooleanArray(boolean[] booleanArray) { 103 | mBooleanArray = booleanArray; 104 | } 105 | 106 | public Boolean getBooleanObj() { 107 | return mBooleanObj; 108 | } 109 | 110 | public void setBooleanObj(Boolean booleanObj) { 111 | mBooleanObj = booleanObj; 112 | } 113 | 114 | public byte getByte() { 115 | return mByte; 116 | } 117 | 118 | public void setByte(byte aByte) { 119 | mByte = aByte; 120 | } 121 | 122 | public byte[] getByteArray() { 123 | return mByteArray; 124 | } 125 | 126 | public void setByteArray(byte[] byteArray) { 127 | mByteArray = byteArray; 128 | } 129 | 130 | public Byte getByteObj() { 131 | return mByteObj; 132 | } 133 | 134 | public void setByteObj(Byte byteObj) { 135 | mByteObj = byteObj; 136 | } 137 | 138 | public char getChar() { 139 | return mChar; 140 | } 141 | 142 | public void setChar(char aChar) { 143 | mChar = aChar; 144 | } 145 | 146 | public char[] getCharArray() { 147 | return mCharArray; 148 | } 149 | 150 | public void setCharArray(char[] charArray) { 151 | mCharArray = charArray; 152 | } 153 | 154 | public Character getCharObj() { 155 | return mCharObj; 156 | } 157 | 158 | public void setCharObj(Character charObj) { 159 | mCharObj = charObj; 160 | } 161 | 162 | public double getDouble() { 163 | return mDouble; 164 | } 165 | 166 | public void setDouble(double aDouble) { 167 | mDouble = aDouble; 168 | } 169 | 170 | public double[] getDoubleArray() { 171 | return mDoubleArray; 172 | } 173 | 174 | public void setDoubleArray(double[] doubleArray) { 175 | mDoubleArray = doubleArray; 176 | } 177 | 178 | public Double getDoubleObj() { 179 | return mDoubleObj; 180 | } 181 | 182 | public void setDoubleObj(Double doubleObj) { 183 | mDoubleObj = doubleObj; 184 | } 185 | 186 | public float getFloat() { 187 | return mFloat; 188 | } 189 | 190 | public void setFloat(float aFloat) { 191 | mFloat = aFloat; 192 | } 193 | 194 | public float[] getFloatArray() { 195 | return mFloatArray; 196 | } 197 | 198 | public void setFloatArray(float[] floatArray) { 199 | mFloatArray = floatArray; 200 | } 201 | 202 | public Float getFloatObj() { 203 | return mFloatObj; 204 | } 205 | 206 | public void setFloatObj(Float floatObj) { 207 | mFloatObj = floatObj; 208 | } 209 | 210 | public int getInt() { 211 | return mInt; 212 | } 213 | 214 | public void setInt(int anInt) { 215 | mInt = anInt; 216 | } 217 | 218 | public int[] getIntArray() { 219 | return mIntArray; 220 | } 221 | 222 | public void setIntArray(int[] intArray) { 223 | mIntArray = intArray; 224 | } 225 | 226 | public Integer getIntegerObj() { 227 | return mIntegerObj; 228 | } 229 | 230 | public void setIntegerObj(Integer integerObj) { 231 | mIntegerObj = integerObj; 232 | } 233 | 234 | public long getLong() { 235 | return mLong; 236 | } 237 | 238 | public void setLong(long aLong) { 239 | mLong = aLong; 240 | } 241 | 242 | public long[] getLongArray() { 243 | return mLongArray; 244 | } 245 | 246 | public void setLongArray(long[] longArray) { 247 | mLongArray = longArray; 248 | } 249 | 250 | public Long getLongObj() { 251 | return mLongObj; 252 | } 253 | 254 | public void setLongObj(Long longObj) { 255 | mLongObj = longObj; 256 | } 257 | 258 | public short getShort() { 259 | return mShort; 260 | } 261 | 262 | public void setShort(short aShort) { 263 | mShort = aShort; 264 | } 265 | 266 | public short[] getShortArray() { 267 | return mShortArray; 268 | } 269 | 270 | public void setShortArray(short[] shortArray) { 271 | mShortArray = shortArray; 272 | } 273 | 274 | public Short getShortObj() { 275 | return mShortObj; 276 | } 277 | 278 | public void setShortObj(Short shortObj) { 279 | mShortObj = shortObj; 280 | } 281 | 282 | public CharSequence getCharSequence() { 283 | return mCharSequence; 284 | } 285 | 286 | public void setCharSequence(CharSequence charSequence) { 287 | mCharSequence = charSequence; 288 | } 289 | 290 | public CharSequence[] getCharSequenceArray() { 291 | return mCharSequenceArray; 292 | } 293 | 294 | public void setCharSequenceArray(CharSequence[] charSequenceArray) { 295 | mCharSequenceArray = charSequenceArray; 296 | } 297 | 298 | public String getString() { 299 | return mString; 300 | } 301 | 302 | public void setString(String string) { 303 | mString = string; 304 | } 305 | 306 | public String[] getStringArray() { 307 | return mStringArray; 308 | } 309 | 310 | public void setStringArray(String[] stringArray) { 311 | mStringArray = stringArray; 312 | } 313 | 314 | public ArrayList getCharSequenceArrayList() { 315 | return mCharSequenceArrayList; 316 | } 317 | 318 | public void setCharSequenceArrayList(ArrayList charSequenceArrayList) { 319 | mCharSequenceArrayList = charSequenceArrayList; 320 | } 321 | 322 | public ArrayList getIntegerArrayList() { 323 | return mIntegerArrayList; 324 | } 325 | 326 | public void setIntegerArrayList(ArrayList integerArrayList) { 327 | mIntegerArrayList = integerArrayList; 328 | } 329 | 330 | public ArrayList getStringArrayList() { 331 | return mStringArrayList; 332 | } 333 | 334 | public void setStringArrayList(ArrayList stringArrayList) { 335 | mStringArrayList = stringArrayList; 336 | } 337 | 338 | public Bundle getBundle() { 339 | return mBundle; 340 | } 341 | 342 | public void setBundle(Bundle bundle) { 343 | mBundle = bundle; 344 | } 345 | 346 | public Parcelable[] getParcelableArray() { 347 | return mParcelableArray; 348 | } 349 | 350 | public void setParcelableArray(Parcelable[] parcelableArray) { 351 | mParcelableArray = parcelableArray; 352 | } 353 | 354 | public TestTypes.ParcelableImpl getParcelableImpl() { 355 | return mParcelableImpl; 356 | } 357 | 358 | public void setParcelableImpl(TestTypes.ParcelableImpl parcelableImpl) { 359 | mParcelableImpl = parcelableImpl; 360 | } 361 | 362 | public TestTypes.SerializableImpl getSerializableImpl() { 363 | return mSerializableImpl; 364 | } 365 | 366 | public void setSerializableImpl(TestTypes.SerializableImpl serializableImpl) { 367 | mSerializableImpl = serializableImpl; 368 | } 369 | 370 | public ArrayList getParcelableArrayList() { 371 | return mParcelableArrayList; 372 | } 373 | 374 | public void setParcelableArrayList(ArrayList parcelableArrayList) { 375 | mParcelableArrayList = parcelableArrayList; 376 | } 377 | 378 | public SparseArray getParcelableSparseArray() { 379 | return mParcelableSparseArray; 380 | } 381 | 382 | public void setParcelableSparseArray(SparseArray parcelableSparseArray) { 383 | mParcelableSparseArray = parcelableSparseArray; 384 | } 385 | 386 | public ParcelableImplExtension getParcelableImplExtension() { 387 | return mParcelableImplExtension; 388 | } 389 | 390 | public void setParcelableImplExtension(ParcelableImplExtension parcelableImplExtension) { 391 | mParcelableImplExtension = parcelableImplExtension; 392 | } 393 | 394 | public static class ParcelableImplExtension extends TestTypes.ParcelableImpl { 395 | private int mInt2; 396 | 397 | public ParcelableImplExtension(int anInt, int anInt2) { 398 | super(anInt); 399 | mInt2 = anInt2; 400 | } 401 | 402 | protected ParcelableImplExtension(Parcel in) { 403 | super(in); 404 | mInt2 = in.readInt(); 405 | } 406 | 407 | @Override 408 | public boolean equals(Object o) { 409 | if (this == o) return true; 410 | if (o == null || getClass() != o.getClass()) return false; 411 | if (!super.equals(o)) return false; 412 | 413 | ParcelableImplExtension that = (ParcelableImplExtension) o; 414 | 415 | return mInt2 == that.mInt2; 416 | } 417 | 418 | @Override 419 | public int hashCode() { 420 | int result = super.hashCode(); 421 | result = 31 * result + mInt2; 422 | return result; 423 | } 424 | 425 | @Override 426 | public void writeToParcel(Parcel dest, int flags) { 427 | super.writeToParcel(dest, flags); 428 | dest.writeInt(mInt2); 429 | } 430 | 431 | @Override 432 | public int describeContents() { 433 | return 0; 434 | } 435 | 436 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 437 | @Override 438 | public TestTypes.ParcelableImpl createFromParcel(Parcel in) { 439 | return new TestTypes.ParcelableImpl(in); 440 | } 441 | 442 | @Override 443 | public TestTypes.ParcelableImpl[] newArray(int size) { 444 | return new TestTypes.ParcelableImpl[size]; 445 | } 446 | }; 447 | } 448 | 449 | } 450 | --------------------------------------------------------------------------------