├── website ├── stylesheet │ ├── ie.css │ └── pygment_trac.css ├── image │ ├── hr.gif │ ├── bullet.png │ └── nav-bg.gif ├── font │ ├── OpenSans-Bold-webfont.eot │ ├── OpenSans-Bold-webfont.ttf │ ├── OpenSans-Bold-webfont.woff │ ├── OpenSans-Italic-webfont.eot │ ├── OpenSans-Italic-webfont.ttf │ ├── OpenSans-Italic-webfont.woff │ ├── OpenSans-Light-webfont.eot │ ├── OpenSans-Light-webfont.ttf │ ├── OpenSans-Light-webfont.woff │ ├── OpenSans-Regular-webfont.eot │ ├── OpenSans-Regular-webfont.ttf │ ├── OpenSans-Regular-webfont.woff │ ├── OpenSans-Semibold-webfont.eot │ ├── OpenSans-Semibold-webfont.ttf │ ├── OpenSans-Semibold-webfont.woff │ ├── OpenSans-BoldItalic-webfont.eot │ ├── OpenSans-BoldItalic-webfont.ttf │ ├── OpenSans-BoldItalic-webfont.woff │ ├── OpenSans-LightItalic-webfont.eot │ ├── OpenSans-LightItalic-webfont.ttf │ ├── OpenSans-LightItalic-webfont.woff │ ├── OpenSans-SemiboldItalic-webfont.eot │ ├── OpenSans-SemiboldItalic-webfont.ttf │ └── OpenSans-SemiboldItalic-webfont.woff └── javascript │ └── jquery-maven-artifact.min.js ├── pocketknife-compiler ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── javax.annotation.processing.Processor │ │ └── java │ │ └── pocketknife │ │ └── internal │ │ └── codegen │ │ ├── FieldBinding.java │ │ ├── InvalidTypeException.java │ │ ├── BaseGenerator.java │ │ ├── MethodBinding.java │ │ ├── Access.java │ │ ├── KeySpec.java │ │ ├── IntentFieldBinding.java │ │ ├── BundleFieldBinding.java │ │ ├── builder │ │ ├── BuilderGenerator.java │ │ ├── BundleMethodBinding.java │ │ ├── FragmentMethodBinding.java │ │ └── IntentMethodBinding.java │ │ ├── BaseProcessor.java │ │ ├── PocketKnifeProcessor.java │ │ └── binding │ │ ├── IntentBindingProcessor.java │ │ ├── IntentBindingAdapterGenerator.java │ │ └── BindingProcessor.java └── pom.xml ├── pocketknife-sample ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── pocketknife │ │ │ │ ├── GrandchildActivity.java │ │ │ │ ├── GrandchildFragment.java │ │ │ │ ├── SimpleApp.java │ │ │ │ ├── ChildFragment.java │ │ │ │ ├── NoBindingActivity.java │ │ │ │ ├── FooSerializerClass.java │ │ │ │ ├── Foo.java │ │ │ │ ├── StringSerializerClass.java │ │ │ │ ├── KeyGenActivity.java │ │ │ │ ├── NotRequiredArgFragment.java │ │ │ │ ├── NotRequiredSaveStateActivity.java │ │ │ │ ├── Builder.java │ │ │ │ ├── KeyGenFragment.java │ │ │ │ ├── ParentActivity.java │ │ │ │ ├── ChildActivity.java │ │ │ │ ├── SerializerBuilder.java │ │ │ │ ├── ParentFragment.java │ │ │ │ ├── MultiAnnotationActivity.java │ │ │ │ ├── Bundles.java │ │ │ │ ├── SimpleFragmentActivity.java │ │ │ │ ├── FooSerializer.java │ │ │ │ ├── MultiAnnotationFragment.java │ │ │ │ ├── MyParcelable.java │ │ │ │ ├── StringSerializer.java │ │ │ │ ├── BeanAccess.java │ │ │ │ ├── Intents.java │ │ │ │ ├── SaveStateActivity.java │ │ │ │ ├── SimpleActivity.java │ │ │ │ └── SimpleFragment.java │ │ ├── res │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ └── layout │ │ │ │ ├── simple_activity.xml │ │ │ │ ├── simple_fragment.xml │ │ │ │ └── simple_list_item.xml │ │ └── AndroidManifest.xml │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── pocketknife │ │ ├── NoBindingActivityTest.java │ │ ├── BaseTest.java │ │ ├── KeyGenTest.java │ │ ├── BeanAccessTest.java │ │ ├── MultiAnnotationTest.java │ │ ├── StringSerializerClassTest.java │ │ ├── InheritanceTest.java │ │ ├── BuilderTest.java │ │ ├── FooSerializerClassTest.java │ │ ├── SaveStateActivityTest.java │ │ ├── BundleBuilderTest.java │ │ └── SimpleActivityTest.java └── pom.xml ├── pocketknife ├── src │ └── main │ │ └── java │ │ └── pocketknife │ │ ├── internal │ │ ├── IntentBinding.java │ │ ├── BundleBinding.java │ │ ├── GeneratedAdapters.java │ │ └── Memoizer.java │ │ ├── PocketKnifeIntentSerializer.java │ │ ├── PocketKnifeBundleSerializer.java │ │ ├── BundleSerializer.java │ │ ├── IntentSerializer.java │ │ ├── SaveState.java │ │ ├── BindExtra.java │ │ ├── BindArgument.java │ │ ├── Data.java │ │ ├── NotRequired.java │ │ ├── Key.java │ │ ├── BundleBuilder.java │ │ ├── FragmentBuilder.java │ │ └── IntentBuilder.java └── pom.xml ├── .gitignore ├── RELEASE_CHECKLIST.md ├── .travis.yml ├── deploy_website.sh ├── CHANGELOG.md ├── README.md ├── checkstyle.xml └── pom.xml /website/stylesheet/ie.css: -------------------------------------------------------------------------------- 1 | nav { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /website/image/hr.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/image/hr.gif -------------------------------------------------------------------------------- /website/image/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/image/bullet.png -------------------------------------------------------------------------------- /website/image/nav-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/image/nav-bg.gif -------------------------------------------------------------------------------- /website/font/OpenSans-Bold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Bold-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-Bold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Bold-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-Bold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Bold-webfont.woff -------------------------------------------------------------------------------- /website/font/OpenSans-Italic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Italic-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-Italic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Italic-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-Italic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Italic-webfont.woff -------------------------------------------------------------------------------- /website/font/OpenSans-Light-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Light-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-Light-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Light-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-Light-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Light-webfont.woff -------------------------------------------------------------------------------- /website/font/OpenSans-Regular-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Regular-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-Regular-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Regular-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-Regular-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Regular-webfont.woff -------------------------------------------------------------------------------- /website/font/OpenSans-Semibold-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Semibold-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-Semibold-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Semibold-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-Semibold-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-Semibold-webfont.woff -------------------------------------------------------------------------------- /website/font/OpenSans-BoldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-BoldItalic-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-BoldItalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-BoldItalic-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-BoldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-BoldItalic-webfont.woff -------------------------------------------------------------------------------- /website/font/OpenSans-LightItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-LightItalic-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-LightItalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-LightItalic-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-LightItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-LightItalic-webfont.woff -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | pocketknife.internal.codegen.PocketKnifeProcessor 2 | -------------------------------------------------------------------------------- /website/font/OpenSans-SemiboldItalic-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-SemiboldItalic-webfont.eot -------------------------------------------------------------------------------- /website/font/OpenSans-SemiboldItalic-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-SemiboldItalic-webfont.ttf -------------------------------------------------------------------------------- /website/font/OpenSans-SemiboldItalic-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hansenji/pocketknife/HEAD/website/font/OpenSans-SemiboldItalic-webfont.woff -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/GrandchildActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | public class GrandchildActivity extends ChildActivity { 4 | } 5 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/internal/IntentBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal; 2 | 3 | import android.content.Intent; 4 | 5 | public interface IntentBinding { 6 | void bindExtras(T target, Intent intent); 7 | } 8 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pocket Knife 5 | Save state was called %d times. 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .settings 4 | eclipsebin 5 | 6 | bin 7 | gen 8 | build 9 | out 10 | lib 11 | 12 | target 13 | pom.xml.* 14 | release.properties 15 | 16 | .idea 17 | *.iml 18 | classes 19 | 20 | obj 21 | 22 | .DS_Store 23 | 24 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/GrandchildFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | public class GrandchildFragment extends ChildFragment { 4 | public static GrandchildFragment newInstance() { 5 | return new GrandchildFragment(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /RELEASE_CHECKLIST.md: -------------------------------------------------------------------------------- 1 | RELEASE CHECKLIST 2 | ================= 3 | 4 | 1. All tests must pass 5 | 2. Make sure a gradle based android project builds with `pocketknife-compiler` as a `provided` dependency 6 | 3. Make sure a gradle based kotlin android project builds with `pocketknife-compiler` as a `kapt` dependency -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/PocketKnifeIntentSerializer.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import android.content.Intent; 4 | 5 | public interface PocketKnifeIntentSerializer { 6 | void put(Intent intent, T target, String keyPrefix); 7 | T get(Intent intent, T target, String keyPrefix); 8 | } 9 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/PocketKnifeBundleSerializer.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import android.os.Bundle; 4 | 5 | public interface PocketKnifeBundleSerializer { 6 | 7 | void put(Bundle bundle, T target, String keyPrefix); 8 | 9 | T get(Bundle bundle, T target, String keyPrefix); 10 | } 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: 4 | - oraclejdk7 5 | - oraclejdk8 6 | 7 | android: 8 | components: 9 | - build-tools-20.0.0 10 | - android-16 11 | licenses: 12 | - android-sdk-license-5be876d5 13 | 14 | branches: 15 | except: 16 | - gh-pages 17 | 18 | notifications: 19 | email: false 20 | 21 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/res/layout/simple_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/SimpleApp.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.app.Application; 4 | import pocketknife.PocketKnife; 5 | 6 | public class SimpleApp extends Application { 7 | @Override public void onCreate() { 8 | super.onCreate(); 9 | PocketKnife.setDebug(BuildConfig.DEBUG); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/internal/BundleBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal; 2 | 3 | import android.os.Bundle; 4 | 5 | 6 | 7 | public interface BundleBinding { 8 | void saveInstanceState(T target, Bundle bundle); 9 | 10 | void restoreInstanceState(T target, Bundle bundle); 11 | 12 | void bindArguments(T target, Bundle bundle); 13 | } 14 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/ChildFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import pocketknife.BindArgument; 4 | import pocketknife.SaveState; 5 | 6 | public class ChildFragment extends ParentFragment { 7 | 8 | public static final String CHILD_ARG = "CHILD_ARG"; 9 | 10 | @BindArgument(CHILD_ARG) 11 | int childArg; 12 | 13 | @SaveState 14 | int cfInt; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/BundleSerializer.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.FIELD; 7 | import static java.lang.annotation.ElementType.PARAMETER; 8 | import static java.lang.annotation.RetentionPolicy.CLASS; 9 | 10 | @Retention(CLASS) 11 | @Target({ FIELD, PARAMETER }) 12 | public @interface BundleSerializer { 13 | Class value(); 14 | } 15 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/IntentSerializer.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.FIELD; 7 | import static java.lang.annotation.ElementType.PARAMETER; 8 | import static java.lang.annotation.RetentionPolicy.CLASS; 9 | 10 | @Retention(CLASS) 11 | @Target({ FIELD, PARAMETER }) 12 | public @interface IntentSerializer { 13 | Class value(); 14 | } 15 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/SaveState.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.FIELD; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Save and restore field. 11 | *
12 |  *     
13 |  *         {@literal @}SaveState int i;
14 |  *     
15 |  * 
16 | */ 17 | @Retention(CLASS) 18 | @Target(FIELD) 19 | public @interface SaveState { 20 | } 21 | 22 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/NoBindingActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import pocketknife.PocketKnife; 6 | 7 | public class NoBindingActivity extends FragmentActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.simple_activity); 13 | 14 | PocketKnife.bindExtras(this); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/internal/GeneratedAdapters.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal; 2 | 3 | public class GeneratedAdapters { 4 | public static final String ANDROID_PREFIX = "android."; 5 | public static final String JAVA_PREFIX = "java."; 6 | 7 | private static final String SEPARATOR = "$$"; 8 | public static final String BUNDLE_ADAPTER_SUFFIX = SEPARATOR + "BundleAdapter"; 9 | public static final String INTENT_ADAPTER_SUFFIX = SEPARATOR + "IntentAdapter"; 10 | 11 | private GeneratedAdapters() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/res/layout/simple_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/BindExtra.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.FIELD; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Bind {@link android.content.Intent} extra for key. 11 | *
12 |  *     
13 |  *         {@literal @}BindExtra("extra_key") int i;
14 |  *     
15 |  * 
16 | */ 17 | @Retention(CLASS) 18 | @Target(FIELD) 19 | public @interface BindExtra { 20 | String value() default ""; 21 | } 22 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/BindArgument.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.FIELD; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Bind {@link android.os.Bundle} value for key. 11 | *
12 |  *     
13 |  *         {@literal @}BindArgument("Bundle_key") int i;
14 |  *     
15 |  * 
16 | */ 17 | @Retention(CLASS) 18 | @Target(FIELD) 19 | public @interface BindArgument { 20 | String value() default ""; 21 | } 22 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/NoBindingActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.robolectric.Robolectric; 6 | import org.robolectric.RobolectricTestRunner; 7 | import org.robolectric.annotation.Config; 8 | 9 | @RunWith(RobolectricTestRunner.class) 10 | @Config(manifest = "src/main/AndroidManifest.xml") 11 | public class NoBindingActivityTest { 12 | 13 | @Test 14 | public void testNoBinding() { 15 | Robolectric.buildActivity(NoBindingActivity.class).create().get(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/FieldBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import com.google.common.base.CaseFormat; 4 | 5 | public abstract class FieldBinding { 6 | public abstract String getName(); 7 | public abstract Access getAccess(); 8 | 9 | /** A description of the binding in human readable form (e.g., "field 'foo'"). */ 10 | public abstract String getDescription(); 11 | 12 | protected String generateKey(String prefix, String name) { 13 | return prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, name); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/InvalidTypeException.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import javax.lang.model.type.TypeMirror; 4 | 5 | public class InvalidTypeException extends Exception { 6 | 7 | public enum Container { BUNDLE, INTENT } 8 | 9 | public InvalidTypeException(TypeMirror type) { 10 | super(String.format("Invalid type %s", type.toString())); 11 | } 12 | 13 | public InvalidTypeException(Container container, TypeMirror type) { 14 | super(String.format("Invalid type %s for %s", type.toString(), container.toString())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/FooSerializerClass.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import pocketknife.BindArgument; 4 | import pocketknife.BindExtra; 5 | import pocketknife.BundleSerializer; 6 | import pocketknife.IntentSerializer; 7 | import pocketknife.SaveState; 8 | 9 | public class FooSerializerClass { 10 | @SaveState 11 | @BundleSerializer(FooSerializer.class) 12 | Foo saveFoo; 13 | 14 | @BindArgument 15 | @BundleSerializer(FooSerializer.class) 16 | Foo argFoo; 17 | 18 | @BindExtra 19 | @IntentSerializer(FooSerializer.class) 20 | Foo extraFoo; 21 | } 22 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/Foo.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | public class Foo { 4 | private String bar; 5 | private int baz; 6 | 7 | public Foo() { 8 | 9 | } 10 | 11 | public Foo(String bar, int baz) { 12 | this.bar = bar; 13 | this.baz = baz; 14 | } 15 | 16 | public String getBar() { 17 | return bar; 18 | } 19 | 20 | public void setBar(String bar) { 21 | this.bar = bar; 22 | } 23 | 24 | public int getBaz() { 25 | return baz; 26 | } 27 | 28 | public void setBaz(int baz) { 29 | this.baz = baz; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/Data.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.PARAMETER; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Specifies the parameter that is to be set to the data field of an {@link android.content.Intent}. 11 | * Must be assigned to either a {@link java.lang.String} or {@link android.net.Uri} on a method annotated with 12 | * {@literal @}{@link pocketknife.IntentBuilder} 13 | */ 14 | @Retention(CLASS) 15 | @Target(PARAMETER) 16 | public @interface Data { 17 | } 18 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/StringSerializerClass.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import pocketknife.BindArgument; 4 | import pocketknife.BindExtra; 5 | import pocketknife.BundleSerializer; 6 | import pocketknife.IntentSerializer; 7 | import pocketknife.SaveState; 8 | 9 | public class StringSerializerClass { 10 | @SaveState 11 | @BundleSerializer(StringSerializer.class) 12 | String saveString; 13 | 14 | @BindExtra 15 | @IntentSerializer(StringSerializer.class) 16 | String extraString; 17 | 18 | @BindArgument 19 | @BundleSerializer(StringSerializer.class) 20 | String argString; 21 | } 22 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/KeyGenActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import pocketknife.BindExtra; 6 | import pocketknife.PocketKnife; 7 | 8 | public class KeyGenActivity extends FragmentActivity { 9 | 10 | @BindExtra 11 | String string; 12 | @BindExtra 13 | int blah; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.simple_activity); 19 | 20 | PocketKnife.bindExtras(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/BaseGenerator.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Locale; 6 | 7 | public class BaseGenerator { 8 | 9 | protected final TypeUtil typeUtil; 10 | 11 | public BaseGenerator(TypeUtil typeUtil) { 12 | this.typeUtil = typeUtil; 13 | } 14 | 15 | protected String getGeneratedComment(Class generator) { 16 | return String.format("Generated by %1$s on %2$s - DO NOT MODIFY%n", generator.getSimpleName(), 17 | new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ", Locale.getDefault()).format(new Date())); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/NotRequiredArgFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import pocketknife.BindArgument; 6 | import pocketknife.NotRequired; 7 | import pocketknife.PocketKnife; 8 | 9 | public class NotRequiredArgFragment extends Fragment { 10 | 11 | @BindArgument("KEY") 12 | @NotRequired 13 | int i = 1; 14 | 15 | @BindArgument("STRING") 16 | @NotRequired 17 | String s = "NOT_REQUIRED"; 18 | 19 | @Override 20 | public void onActivityCreated(Bundle savedInstanceState) { 21 | super.onActivityCreated(savedInstanceState); 22 | 23 | PocketKnife.bindArguments(this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/res/layout/simple_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 12 | 17 | 22 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/NotRequired.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import android.os.Build; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.FIELD; 9 | import static java.lang.annotation.RetentionPolicy.CLASS; 10 | 11 | /** 12 | * Denote that the variable specified by the binding is not required to be present. 13 | * 14 | *

15 |  * {@literal @}NotRequired @SaveState int i;
16 |  * {@literal @}NotRequired(Build.VERSION_CODES.HONEYCOMB_MR1) @SaveState String s = "Default Value";
17 |  * {@literal @}NotRequired @BindArgument("BundleKey") int i;
18 |  * 
19 | */ 20 | @Retention(CLASS) 21 | @Target(FIELD) 22 | public @interface NotRequired { 23 | int value() default Build.VERSION_CODES.FROYO; // for string/charSequence defaults minSdk must be Build.VERSION_CODES.HONEYCOMB_MR1 24 | } 25 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/NotRequiredSaveStateActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import pocketknife.NotRequired; 6 | import pocketknife.PocketKnife; 7 | import pocketknife.SaveState; 8 | 9 | public class NotRequiredSaveStateActivity extends FragmentActivity { 10 | 11 | @SaveState 12 | @NotRequired 13 | int i = -1; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | setContentView(R.layout.simple_activity); 19 | 20 | PocketKnife.restoreInstanceState(this, savedInstanceState); 21 | } 22 | 23 | @Override 24 | protected void onSaveInstanceState(Bundle outState) { 25 | super.onSaveInstanceState(outState); 26 | PocketKnife.saveInstanceState(this, outState); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/Key.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.PARAMETER; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Specifies the key that is to be used for the {@link android.content.Intent} extra or the {@link android.os.Bundle} argument. 11 | * See {@literal @}{@link pocketknife.IntentBuilder}, {@literal @}{@link pocketknife.BundleBuilder}, or {@literal @}{@link pocketknife.FragmentBuilder} 12 | * 13 | *

14 |  *     {@literal @}{@link pocketknife.IntentBuilder}(action = {@link android.content.Intent.ACTION_SEND})
15 |  *     Intent buildIntent({@literal @}Key({@link android.content.Intent.EXTRA_EMAIL}) String email);
16 |  * 
17 | */ 18 | @Retention(CLASS) 19 | @Target(PARAMETER) 20 | public @interface Key { 21 | String value(); 22 | } 23 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/BundleBuilder.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.METHOD; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Denotes an interface method that will return an {@link android.content.Bundle} with the variables as arguments. 11 | *

12 |  *     {@literal @}BundleBuilder
13 |  *     Bundle buildBundle(int i);
14 |  * 
15 | * 16 | * Generated code will look something like this 17 | *

18 |  *     public static final String ARG_I = "ARG_I";
19 |  *
20 |  *     Bundle buildBundle(int i) {
21 |  *         Bundle bundle = new Bundle();
22 |  *         intent.putExtra(ARG_I, i);
23 |  *         return bundle;
24 |  *     }
25 |  * 
26 | */ 27 | @Retention(CLASS) 28 | @Target(METHOD) 29 | public @interface BundleBuilder { 30 | } 31 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/Builder.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import pocketknife.BundleBuilder; 7 | import pocketknife.FragmentBuilder; 8 | import pocketknife.IntentBuilder; 9 | import pocketknife.Key; 10 | 11 | public interface Builder { 12 | 13 | @BundleBuilder 14 | Bundle getBundle(int arg, @Key("THIS IS A TEST")int test); 15 | 16 | @BundleBuilder 17 | Bundle getBundle(@Key("Testing is awesome") int arg); 18 | 19 | @IntentBuilder(action = Intent.ACTION_DEFAULT) 20 | Intent getIntent(int extra, @Key(Intent.EXTRA_TEXT) String text); 21 | 22 | @IntentBuilder(action = Intent.ACTION_DEFAULT) 23 | Intent getIntent(String text); 24 | 25 | @FragmentBuilder 26 | Fragment getFragment(int arg); 27 | 28 | @FragmentBuilder 29 | Fragment getFragment(@Key("Fragment Test") int arg, int arg2); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/MethodBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import com.squareup.javapoet.MethodSpec; 4 | 5 | import java.util.LinkedHashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | public abstract class MethodBinding { 10 | 11 | public abstract List getFields(); 12 | 13 | public abstract Set getKeys(); 14 | 15 | public abstract MethodSpec generateMethodSpec(TypeUtil typeUtil); 16 | 17 | protected String getReturnVarName(String returnVarNameRoot) { 18 | Set fieldNames = new LinkedHashSet(); 19 | for (FieldBinding fieldBinding : getFields()) { 20 | fieldNames.add(fieldBinding.getName()); 21 | } 22 | 23 | String returnVarName = returnVarNameRoot; 24 | int count = 0; 25 | while (fieldNames.contains(returnVarName)) { 26 | returnVarName = returnVarNameRoot + ++count; 27 | } 28 | return returnVarName; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/FragmentBuilder.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.METHOD; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Denotes an interface method that will return a fragment with the variables as arguments. 11 | * 12 | *

13 |  *     {@literal @}FragmentBuilder
14 |  *     Fragment buildFragment(int i);
15 |  * 
16 | * 17 | * Generated code will look something like this 18 | *

19 |  *     public static final String ARG_I = "ARG_I";
20 |  *
21 |  *     Fragment buildFragment(int i) {
22 |  *         Fragment fragment = new Fragment();
23 |  *         Bundle args = new Bundle();
24 |  *         args.putInt(ARG_I, i);
25 |  *         fragment.setArguments(args);
26 |  *         return fragment;
27 |  *     }
28 |  * 
29 | */ 30 | @Retention(CLASS) 31 | @Target(METHOD) 32 | public @interface FragmentBuilder { 33 | } 34 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/KeyGenFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import pocketknife.BindArgument; 9 | import pocketknife.PocketKnife; 10 | 11 | public class KeyGenFragment extends Fragment { 12 | 13 | @BindArgument 14 | String aString; 15 | @BindArgument 16 | int anInt; 17 | 18 | public static KeyGenFragment newInstance() { 19 | return new KeyGenFragment(); 20 | } 21 | 22 | @Override 23 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 24 | return inflater.inflate(R.layout.simple_fragment, container, false); 25 | } 26 | 27 | @Override 28 | public void onActivityCreated(Bundle savedInstanceState) { 29 | super.onActivityCreated(savedInstanceState); 30 | PocketKnife.bindArguments(this, getArguments()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/ParentActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import pocketknife.BindExtra; 6 | import pocketknife.PocketKnife; 7 | import pocketknife.SaveState; 8 | 9 | public class ParentActivity extends FragmentActivity { 10 | 11 | public static final String PARENT_EXTRA = "PARENT_EXTRA"; 12 | 13 | @BindExtra(PARENT_EXTRA) 14 | int parentExtra; 15 | 16 | @SaveState 17 | int paInt; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | 23 | setContentView(R.layout.simple_activity); 24 | PocketKnife.bindExtras(this); 25 | 26 | PocketKnife.restoreInstanceState(this, savedInstanceState); 27 | } 28 | 29 | @Override 30 | protected void onSaveInstanceState(Bundle outState) { 31 | super.onSaveInstanceState(outState); 32 | PocketKnife.saveInstanceState(this, outState); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /deploy_website.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | REPO="git@github.com:hansenji/pocketknife.git" 6 | GROUP_ID="com.vikingsen" 7 | ARTIFACT_ID="pocketknife-core" 8 | 9 | DIR=temp-clone 10 | 11 | # Delete any existing temporary website clone 12 | rm -rf $DIR 13 | 14 | # Clone the current repo into temp folder 15 | git clone $REPO $DIR 16 | 17 | # Move working directory into temp folder 18 | cd $DIR 19 | 20 | # Checkout and track the gh-pages branch 21 | git checkout -t origin/gh-pages 22 | 23 | # Delete everything 24 | rm -rf * 25 | 26 | # Copy website files from real repo 27 | cp -R ../website/* . 28 | 29 | # Download the latest javadoc 30 | curl -L "http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip 31 | mkdir javadoc 32 | unzip javadoc.zip -d javadoc 33 | rm javadoc.zip 34 | 35 | # Stage all files in git and create a commit 36 | git add . 37 | git add -u 38 | git commit -m "Website at $(date)" 39 | 40 | # Push the new files up to GitHub 41 | git push origin gh-pages 42 | 43 | # Delete our temp folder 44 | cd .. 45 | rm -rf $DIR 46 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/ChildActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import pocketknife.BindExtra; 5 | import pocketknife.SaveState; 6 | 7 | public class ChildActivity extends ParentActivity { 8 | 9 | public static final String CHILD_EXTRA = "CHILD_EXTRA"; 10 | public static final String FRAGMENT_ARGS = "FRAGMENT_ARGS"; 11 | 12 | @BindExtra(CHILD_EXTRA) 13 | int childExtra; 14 | 15 | @BindExtra(FRAGMENT_ARGS) 16 | Bundle args; 17 | 18 | @SaveState 19 | int caInt; 20 | 21 | GrandchildFragment fragment; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | if (savedInstanceState == null) { 28 | fragment = GrandchildFragment.newInstance(); 29 | fragment.setArguments(args); 30 | getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit(); 31 | } else { 32 | fragment = (GrandchildFragment) getSupportFragmentManager().findFragmentById(R.id.container); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/SerializerBuilder.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import pocketknife.BundleBuilder; 7 | import pocketknife.BundleSerializer; 8 | import pocketknife.FragmentBuilder; 9 | import pocketknife.IntentBuilder; 10 | import pocketknife.IntentSerializer; 11 | 12 | public interface SerializerBuilder { 13 | 14 | @BundleBuilder 15 | Bundle buildBundle(@BundleSerializer(StringSerializer.class) String argString); 16 | 17 | @IntentBuilder(action = "test") 18 | Intent buildIntent(@IntentSerializer(StringSerializer.class) String extraString); 19 | 20 | @FragmentBuilder 21 | Fragment buildFragment(@BundleSerializer(StringSerializer.class) String argString); 22 | 23 | @BundleBuilder 24 | Bundle buildBundle(@BundleSerializer(FooSerializer.class) Foo argFoo); 25 | 26 | @IntentBuilder(action = "test") 27 | Intent buildIntent(@IntentSerializer(FooSerializer.class)Foo extraFoo); 28 | 29 | @FragmentBuilder 30 | Fragment buildFragment(@BundleSerializer(FooSerializer.class) Foo argFoo); 31 | } 32 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/ParentFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import pocketknife.BindArgument; 9 | import pocketknife.PocketKnife; 10 | import pocketknife.SaveState; 11 | 12 | public class ParentFragment extends Fragment { 13 | 14 | public static final String PARENT_ARG = "PARENT_ARG"; 15 | 16 | @BindArgument(PARENT_ARG) 17 | int parentArg; 18 | 19 | @SaveState 20 | int pfInt; 21 | 22 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 23 | return inflater.inflate(R.layout.simple_fragment, container, false); 24 | } 25 | 26 | @Override 27 | public void onActivityCreated(Bundle savedInstanceState) { 28 | super.onActivityCreated(savedInstanceState); 29 | PocketKnife.bindArguments(this); 30 | PocketKnife.restoreInstanceState(this, savedInstanceState); 31 | } 32 | 33 | @Override 34 | public void onSaveInstanceState(Bundle outState) { 35 | super.onSaveInstanceState(outState); 36 | PocketKnife.saveInstanceState(this, outState); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/MultiAnnotationActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import pocketknife.BindExtra; 6 | import pocketknife.PocketKnife; 7 | import pocketknife.SaveState; 8 | 9 | public class MultiAnnotationActivity extends FragmentActivity { 10 | public static final String EXTRA_INT = "EXTRA_INT"; 11 | 12 | @BindExtra(EXTRA_INT) 13 | @SaveState 14 | int i; 15 | 16 | MultiAnnotationFragment fragment; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | 22 | setContentView(R.layout.simple_activity); 23 | 24 | 25 | PocketKnife.bindExtras(this); 26 | PocketKnife.restoreInstanceState(this, savedInstanceState); 27 | } 28 | 29 | @Override 30 | protected void onSaveInstanceState(Bundle outState) { 31 | super.onSaveInstanceState(outState); 32 | PocketKnife.saveInstanceState(this, outState); 33 | } 34 | 35 | public void replaceFragment(MultiAnnotationFragment fragment) { 36 | this.fragment = fragment; 37 | getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/Bundles.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import pocketknife.BundleBuilder; 5 | 6 | import java.io.Serializable; 7 | import java.util.ArrayList; 8 | 9 | public interface Bundles { 10 | 11 | @BundleBuilder 12 | Bundle getEmptyBundle(); 13 | 14 | @BundleBuilder 15 | Bundle getSingleArgBundle(int i); 16 | 17 | @BundleBuilder 18 | Bundle getTwinArgsBundle(int i, int j); 19 | 20 | @BundleBuilder 21 | Bundle getNameCollisionBundle(int bundle, int bundle1, int bundle2); 22 | 23 | @BundleBuilder 24 | Bundle getBundle(boolean aBoolean, boolean[] booleans, Bundle bundle, byte aByte, byte[] bytes, char aChar, char[] chars, CharSequence charSequence, 25 | CharSequence[] charSequences, ArrayList charSequenceArrayList, double aDouble, double[] doubles, float aFloat, 26 | float[] floats, int anInt, int[] ints, ArrayList integerArrayList, long aLong, long[] longs, MyParcelable parcelable, 27 | MyParcelable[] parcelables, ArrayList parcelableArrayList, Serializable serializable, short aShort, short[] shorts, 28 | String string, String[] strings, ArrayList stringArrayList); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/SimpleFragmentActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentActivity; 6 | import pocketknife.BindExtra; 7 | import pocketknife.NotRequired; 8 | import pocketknife.PocketKnife; 9 | import pocketknife.SaveState; 10 | 11 | public class SimpleFragmentActivity extends FragmentActivity { 12 | 13 | @BindExtra("INT") 14 | @NotRequired 15 | int intExtra = 2; 16 | @BindExtra("STRING") 17 | @NotRequired 18 | String stringExtra = "NOT_REQUIRED"; 19 | 20 | @SaveState 21 | int i = 1; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | setContentView(R.layout.simple_activity); 28 | PocketKnife.bindExtras(this); 29 | PocketKnife.restoreInstanceState(this, savedInstanceState); 30 | } 31 | 32 | @Override 33 | protected void onSaveInstanceState(Bundle outState) { 34 | super.onSaveInstanceState(outState); 35 | PocketKnife.saveInstanceState(this, outState); 36 | } 37 | 38 | public void replaceFragment(Fragment fragment) { 39 | getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 11 | 12 | 15 | 16 | 19 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/FooSerializer.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import pocketknife.PocketKnifeBundleSerializer; 6 | import pocketknife.PocketKnifeIntentSerializer; 7 | 8 | public class FooSerializer implements PocketKnifeBundleSerializer, PocketKnifeIntentSerializer { 9 | private static final String BAR = ".BAR"; 10 | private static final String BAZ = ".BAZ"; 11 | 12 | @Override 13 | public void put(Bundle bundle, Foo foo, String keyPrefix) { 14 | bundle.putString(keyPrefix + BAR, foo.getBar()); 15 | bundle.putInt(keyPrefix + BAZ, foo.getBaz()); 16 | } 17 | 18 | @Override 19 | public Foo get(Bundle bundle, Foo foo, String keyPrefix) { 20 | foo.setBar(bundle.getString(keyPrefix + BAR)); 21 | foo.setBaz(bundle.getInt(keyPrefix + BAZ)); 22 | return foo; 23 | } 24 | 25 | @Override 26 | public void put(Intent intent, Foo foo, String keyPrefix) { 27 | intent.putExtra(keyPrefix + BAR, foo.getBar()); 28 | intent.putExtra(keyPrefix + BAZ, foo.getBaz()); 29 | } 30 | 31 | @Override 32 | public Foo get(Intent intent, Foo foo, String keyPrefix) { 33 | foo.setBar(intent.getStringExtra(keyPrefix + BAR)); 34 | foo.setBaz(intent.getIntExtra(keyPrefix + BAZ, 0)); 35 | return foo; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/MultiAnnotationFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.Fragment; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import pocketknife.BindArgument; 9 | import pocketknife.PocketKnife; 10 | import pocketknife.SaveState; 11 | 12 | public class MultiAnnotationFragment extends Fragment { 13 | 14 | public static final String ARG_INT = "ARG_INT"; 15 | 16 | @BindArgument(ARG_INT) 17 | @SaveState 18 | int i; 19 | 20 | public static MultiAnnotationFragment newInstance() { 21 | return new MultiAnnotationFragment(); 22 | } 23 | 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 26 | return inflater.inflate(R.layout.simple_fragment, container, false); 27 | } 28 | 29 | @Override 30 | public void onActivityCreated(Bundle savedInstanceState) { 31 | super.onActivityCreated(savedInstanceState); 32 | PocketKnife.bindArguments(this, getArguments()); 33 | PocketKnife.restoreInstanceState(this, savedInstanceState); 34 | } 35 | 36 | @Override 37 | public void onSaveInstanceState(Bundle outState) { 38 | super.onSaveInstanceState(outState); 39 | PocketKnife.saveInstanceState(this, outState); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/IntentBuilder.java: -------------------------------------------------------------------------------- 1 | package pocketknife; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.Target; 5 | 6 | import static java.lang.annotation.ElementType.METHOD; 7 | import static java.lang.annotation.RetentionPolicy.CLASS; 8 | 9 | /** 10 | * Denotes an interface method that will return an {@link android.content.Intent} with the variables as extras. 11 | * {@literal @}{@link pocketknife.Data} is used to specify a parameter that will be assigned 12 | * to the data element of the intent. 13 | * 14 | *

15 |  *     {@literal @}IntentBuilder(action = Intent.ACTION_VIEW)
16 |  *     Intent buildIntent({@literal @}Data String data, int i);
17 |  * 
18 | * 19 | * Generated code will look something like this 20 | *

21 |  *     public static final String EXTRA_I = "EXTRA_I";
22 |  *
23 |  *     Intent buildIntent(String data, int i) {
24 |  *         Intent intent = new Intent();
25 |  *         intent.setAction(Intent.ACTION_VIEW);
26 |  *         intent.setData(data);
27 |  *         intent.putExtra(EXTRA_I, i);
28 |  *         return intent;
29 |  *     }
30 |  * 
31 | */ 32 | @Retention(CLASS) 33 | @Target(METHOD) 34 | public @interface IntentBuilder { 35 | int flags() default 0; 36 | Class cls() default Void.class; 37 | String action() default ""; 38 | String[] categories() default { }; 39 | String type() default ""; 40 | } -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/MyParcelable.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class MyParcelable implements Parcelable { 7 | public static final Creator CREATOR = new Creator() { 8 | @Override 9 | public MyParcelable createFromParcel(Parcel in) { 10 | return new MyParcelable(in); 11 | } 12 | 13 | @Override 14 | public MyParcelable[] newArray(int size) { 15 | return new MyParcelable[size]; 16 | } 17 | }; 18 | 19 | private int data; 20 | 21 | public MyParcelable(int data) { 22 | this.data = data; 23 | } 24 | 25 | private MyParcelable(Parcel in) { 26 | data = in.readInt(); 27 | } 28 | 29 | @Override 30 | public int hashCode() { 31 | return Integer.valueOf(data).hashCode(); 32 | } 33 | 34 | @Override 35 | public boolean equals(Object obj) { 36 | return obj instanceof MyParcelable && ((MyParcelable) obj).data == this.data; 37 | } 38 | 39 | @Override 40 | public int describeContents() { 41 | return 0; 42 | } 43 | 44 | @Override 45 | public void writeToParcel(Parcel out, int flags) { 46 | out.writeInt(data); 47 | } 48 | 49 | public int getData() { 50 | return data; 51 | } 52 | 53 | public void setData(int data) { 54 | this.data = data; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/StringSerializer.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import pocketknife.PocketKnifeBundleSerializer; 6 | import pocketknife.PocketKnifeIntentSerializer; 7 | 8 | public class StringSerializer implements PocketKnifeBundleSerializer, PocketKnifeIntentSerializer { 9 | 10 | @Override 11 | public void put(Bundle bundle, String target, String keyPrefix) { 12 | char[] charArray = (keyPrefix + target).toCharArray(); 13 | bundle.putCharArray(keyPrefix, charArray); 14 | } 15 | 16 | @Override 17 | public String get(Bundle bundle, String data, String keyPrefix) { 18 | char[] inArray = bundle.getCharArray(keyPrefix); 19 | char[] outArray = new char[inArray.length - keyPrefix.length()]; 20 | System.arraycopy(inArray, keyPrefix.length(), outArray, 0, outArray.length); 21 | return new String(outArray); 22 | } 23 | 24 | @Override 25 | public void put(Intent intent, String data, String keyPrefix) { 26 | char[] charArray = (keyPrefix + data).toCharArray(); 27 | intent.putExtra(keyPrefix, charArray); 28 | } 29 | 30 | @Override 31 | public String get(Intent intent, String data, String keyPrefix) { 32 | char[] inArray = intent.getCharArrayExtra(keyPrefix); 33 | char[] outArray = new char[inArray.length - keyPrefix.length()]; 34 | System.arraycopy(inArray, keyPrefix.length(), outArray, 0, outArray.length); 35 | return new String(outArray); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/Access.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | public class Access { 4 | public enum Type { 5 | METHOD, 6 | FIELD 7 | } 8 | 9 | private Type type; 10 | private String getter; 11 | private String setter; 12 | 13 | public Access(Type type, String getter, String setter) { 14 | this.type = type; 15 | this.getter = getter; 16 | this.setter = setter; 17 | } 18 | 19 | public Type getType() { 20 | return type; 21 | } 22 | 23 | public void setType(Type type) { 24 | this.type = type; 25 | } 26 | 27 | public String getGetter() { 28 | return getter; 29 | } 30 | 31 | public void setGetter(String getter) { 32 | this.getter = getter; 33 | } 34 | 35 | public String getSetter() { 36 | return setter; 37 | } 38 | 39 | public void setSetter(String setter) { 40 | this.setter = setter; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (o == null || getClass() != o.getClass()) { 49 | return false; 50 | } 51 | 52 | Access access = (Access) o; 53 | 54 | if (type != access.type) { 55 | return false; 56 | } 57 | return getter.equals(access.getter) && setter.equals(access.setter); 58 | 59 | } 60 | 61 | @Override 62 | public int hashCode() { 63 | int result = type.hashCode(); 64 | result = 31 * result + getter.hashCode(); 65 | result = 31 * result + setter.hashCode(); 66 | return result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /website/javascript/jquery-maven-artifact.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery Maven Artifact Plugin 3 | * 4 | * Version: 1.0.1 5 | * Author: Jake Wharton 6 | * License: Apache 2.0 7 | */ 8 | (function($){function downloadUrl(groupId,artifactId,version,type){var groupPath=groupId.replace(".","/");var artifactPath=artifactId.replace(".","/");return"http://repo1.maven.org/maven2/"+groupPath+"/"+artifactPath+"/"+version+"/"+artifactId+"-"+version+type}$.fn.artifactVersion=function(groupId,artifactId,callback){if(typeof groupId!=="string"||typeof artifactId!=="string"){console.log("Error: groupId and artifactId are required.");return}if(typeof callback==="undefined"){console.log("Error: callback function required.");return}var url='http://search.maven.org/solrsearch/select/?q=g:"'+groupId+'"+AND+a:"'+artifactId+'"&wt=json&json.wrf=?';$.getJSON(url,function(response){var versions=response.response.docs;if(versions.length==0){return}var version=versions[0].latestVersion;var versionUrl=downloadUrl(groupId,artifactId,version,".jar");callback(version,versionUrl)})};$.fn.artifactVersions=function(groupId,artifactId,callback){if(typeof groupId!=="string"||typeof artifactId!=="string"){console.log("Error: groupId and artifactId are required.");return}if(typeof callback==="undefined"){console.log("Error: callback function required.");return}var url='http://search.maven.org/solrsearch/select/?q=g:"'+groupId+'"+AND+a:"'+artifactId+'"&wt=json&rows=10&core=gav&json.wrf=?';$.getJSON(url,function(response){var versions=response.response.docs;if(versions.length==0){return}versions.sort(function(o1,o2){return o1.v>o2.v?-1:1});var newVersions=[];for(var i=0;i { 7 | private String name; 8 | private String value; 9 | 10 | public KeySpec(String name, String value) { 11 | this.name = name; 12 | this.value = value; 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public String getValue() { 20 | return value; 21 | } 22 | 23 | @Override 24 | public boolean equals(Object o) { 25 | if (this == o) return true; 26 | 27 | if (o == null || getClass() != o.getClass()) return false; 28 | 29 | KeySpec keySpec = (KeySpec) o; 30 | 31 | return new EqualsBuilder() 32 | .append(name, keySpec.name) 33 | .append(value, keySpec.value) 34 | .isEquals(); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return new HashCodeBuilder(17, 37) 40 | .append(name) 41 | .append(value) 42 | .toHashCode(); 43 | } 44 | 45 | @Override 46 | public int compareTo(KeySpec o) { 47 | int i; 48 | if (name == null) { 49 | if (o.name != null) { 50 | i = -1; 51 | } else { 52 | i = 0; 53 | } 54 | } else if (o.name == null) { 55 | i = 1; 56 | } else { 57 | i = name.compareTo(o.name); 58 | } 59 | if (i == 0) { 60 | return value.compareTo(o.value); 61 | } 62 | return i; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/BeanAccess.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import pocketknife.BindArgument; 4 | import pocketknife.BindExtra; 5 | import pocketknife.BundleSerializer; 6 | import pocketknife.IntentSerializer; 7 | import pocketknife.SaveState; 8 | 9 | public class BeanAccess { 10 | public static final String ARG_KEY_1 = "ARG_KEY_1"; 11 | public static final String ARG_KEY_2 = "ARG_KEY_2"; 12 | public static final String INTENT_KEY = "INTENT_KEY"; 13 | 14 | @SaveState 15 | private String saveString; 16 | 17 | @BindExtra 18 | private String extraString; 19 | 20 | @BindArgument 21 | private String argString; 22 | 23 | @BindArgument(ARG_KEY_1) 24 | @BundleSerializer(StringSerializer.class) 25 | private String bsString; 26 | 27 | @BindExtra(INTENT_KEY) 28 | @IntentSerializer(StringSerializer.class) 29 | private String isString; 30 | 31 | @BindArgument(ARG_KEY_2) 32 | String pString; 33 | 34 | public String getSaveString() { 35 | return saveString; 36 | } 37 | 38 | public void setSaveString(String saveString) { 39 | this.saveString = saveString; 40 | } 41 | 42 | public String getExtraString() { 43 | return extraString; 44 | } 45 | 46 | public void setExtraString(String extraString) { 47 | this.extraString = extraString; 48 | } 49 | 50 | public String getArgString() { 51 | return argString; 52 | } 53 | 54 | public void setArgString(String argString) { 55 | this.argString = argString; 56 | } 57 | 58 | public String getBsString() { 59 | return bsString; 60 | } 61 | 62 | public void setBsString(String bsString) { 63 | this.bsString = bsString; 64 | } 65 | 66 | public String getIsString() { 67 | return isString; 68 | } 69 | 70 | public void setIsString(String isString) { 71 | this.isString = isString; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/BaseTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | 5 | import java.util.ArrayList; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.assertNotNull; 9 | import static org.junit.Assert.assertNull; 10 | 11 | public class BaseTest { 12 | protected static final String BUNDLE_INT_ARG = "BUNDLE_INT_ARG"; 13 | protected static final String BUNDLE_SERIALIZABLE_ARG = "BUNDLE_SERIALIZABLE_ARG"; 14 | 15 | protected static void assertBundleEquals(Bundle expected, Bundle actual) { 16 | if (expected == null) { 17 | assertNull(actual); 18 | } else { 19 | assertNotNull(actual); 20 | assertEquals(expected.getInt(BUNDLE_INT_ARG), actual.getInt(BUNDLE_INT_ARG)); 21 | assertEquals(expected.getSerializable(BUNDLE_SERIALIZABLE_ARG), actual.getSerializable(BUNDLE_SERIALIZABLE_ARG)); 22 | } 23 | } 24 | 25 | protected static void assertBooleanArrayEquals(boolean[] expected, boolean[] actual) { 26 | if (expected == null) { 27 | assertNull(actual); 28 | } else { 29 | assertNotNull(actual); 30 | assertEquals(expected.length, actual.length); 31 | int count = expected.length; 32 | for (int i = 0; i < count; i++) { 33 | assertEquals("Arrays not equals for item: " + i, expected[i], actual[i]); 34 | } 35 | } 36 | } 37 | 38 | protected static void assertArrayListEquals(ArrayList expected, ArrayList actual) { 39 | if (expected == null) { 40 | assertNull(actual); 41 | } else { 42 | assertNotNull(actual); 43 | assertEquals(expected.size(), actual.size()); 44 | int count = expected.size(); 45 | for (int i = 0; i < count; i++) { 46 | assertEquals("ArrayLists not equals for item: " + i, expected.get(i), actual.get(i)); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/KeyGenTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.robolectric.Robolectric; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.RuntimeEnvironment; 10 | import org.robolectric.annotation.Config; 11 | import org.robolectric.util.ActivityController; 12 | 13 | import java.util.Random; 14 | import java.util.UUID; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | 18 | @RunWith(RobolectricTestRunner.class) 19 | @Config(manifest = "src/main/AndroidManifest.xml") 20 | public class KeyGenTest extends BaseTest { 21 | 22 | @Test 23 | public void testExtraKeyGeneration() { 24 | String s = UUID.randomUUID().toString(); 25 | int i = new Random(42).nextInt(); 26 | Intent intent = new Intent(RuntimeEnvironment.application, KeyGenActivity.class); 27 | intent.putExtra("EXTRA_STRING", s); 28 | intent.putExtra("EXTRA_BLAH", i); 29 | 30 | KeyGenActivity keyGenActivity = Robolectric.buildActivity(KeyGenActivity.class).withIntent(intent).create().start().restart().visible().get(); 31 | 32 | assertEquals(s, keyGenActivity.string); 33 | assertEquals(i, keyGenActivity.blah); 34 | } 35 | 36 | @Test 37 | public void testArgKeyGeneration() { 38 | String s = UUID.randomUUID().toString(); 39 | int i = new Random(42).nextInt(); 40 | 41 | Bundle bundle = new Bundle(); 42 | bundle.putString("ARG_A_STRING", s); 43 | bundle.putInt("ARG_AN_INT", i); 44 | 45 | ActivityController initialController = Robolectric.buildActivity(SimpleFragmentActivity.class).create(); 46 | SimpleFragmentActivity simpleActivity = initialController.start().restart().visible().get(); 47 | KeyGenFragment fragment = KeyGenFragment.newInstance(); 48 | fragment.setArguments(bundle); 49 | simpleActivity.replaceFragment(fragment); 50 | 51 | assertEquals(s, fragment.aString); 52 | assertEquals(i, fragment.anInt); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /pocketknife-compiler/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.vikingsen 7 | pocketknife-parent 8 | 3.2.2-SNAPSHOT 9 | 10 | 11 | pocketknife-compiler 12 | Pocket Knife Compiler 13 | 14 | 15 | 16 | com.vikingsen 17 | pocketknife-core 18 | ${project.version} 19 | 20 | 21 | 22 | com.squareup 23 | javapoet 24 | 25 | 26 | com.google.guava 27 | guava 28 | 29 | 30 | com.google.android 31 | android 32 | provided 33 | 34 | 35 | org.apache.commons 36 | commons-lang3 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-compiler-plugin 45 | 46 | -proc:none 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-javadoc-plugin 53 | 54 | pocketknife.internal:pocketknife.internal.* 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /pocketknife/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | com.vikingsen 8 | pocketknife-parent 9 | 3.2.2-SNAPSHOT 10 | 11 | 12 | pocketknife-core 13 | Pocket Knife 14 | 15 | 16 | 17 | junit 18 | junit 19 | test 20 | 21 | 22 | org.easytesting 23 | fest-assert-core 24 | test 25 | 26 | 27 | org.robolectric 28 | robolectric 29 | test 30 | 31 | 32 | com.google.testing.compile 33 | compile-testing 34 | test 35 | 36 | 37 | 38 | com.google.android 39 | android 40 | provided 41 | 42 | 43 | com.google.android 44 | support-v4 45 | provided 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.apache.maven.plugins 53 | maven-compiler-plugin 54 | 55 | -proc:none 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-javadoc-plugin 62 | 63 | pocketknife.internal:pocketknife.internal.* 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | Version 3.2.1 *(2016-04-03)* 4 | ---------------------------- 5 | 6 | * Removed dependency on JSR 250 (@Generated) 7 | 8 | Version 3.2.0 *(2016-02-07)* 9 | ---------------------------- 10 | 11 | * Added Java Bean accessor/mutator support 12 | * Added Kotlin support (Must use [kapt](http://blog.jetbrains.com/kotlin/2015/06/better-annotation-processing-supporting-stubs-in-kapt/)) 13 | 14 | Version 3.1.0 *(2015-08-22)* 15 | ---------------------------- 16 | 17 | * Resolved #10 Added @BundleSerializer and @IntentSerializer to allow users to save nonstandard Bundle and Intent types respectively. 18 | 19 | Version 3.0.0 *(2015-07-07)* 20 | ---------------------------- 21 | 22 | * Resolved #6 Changed inject to bind to match Android data bind. 23 | 24 | Version 2.6.1 *(2015-06-24)* 25 | ------------------ 26 | 27 | * Fixed #5 `@Key` variable naming conflict. 28 | 29 | Version 2.6.0 *(2015-06-17)* 30 | ---------------------------- 31 | 32 | * Fixed #4 added @Key annotation to specify Extra/Argument keys in builders. 33 | 34 | Version 2.5.1 *(2015-05-26)* 35 | ---------------------------- 36 | 37 | * Fixed ClassDefNotFound build exception of android classes 38 | 39 | Version 2.5.0 *(2015-05-25)* 40 | ---------------------------- 41 | 42 | * Added @FragmentBuilder 43 | * Allow all @*Builder annotations to be apart of the same interface 44 | * Moved @IntentBuilder path parameter to its own annotation @Data 45 | 46 | Version 2.0.1 *(2015-04-29)* 47 | ---------------------------- 48 | 49 | * Fixed bug where argument and extra keys couldn't contain '.' (Every builtin extra or argument key in android) 50 | 51 | Version 2.0.0 *(2015-04-01)* 52 | ---------------------------- 53 | 54 | * Generate default extra and argument keys based on field names 55 | * Added Intent and Bundle Builders 56 | 57 | Version 1.1.3 *(2015-03-18)* 58 | ---------------------------- 59 | 60 | * Fixed bug with injection default values 61 | 62 | Version 1.1.2 *(2015-03-04)* 63 | ---------------------------- 64 | 65 | * Fixed bug with annotating the same field with @SaveState and @InjectArgument 66 | 67 | Version 1.1.1 *(2015-02-26)* 68 | ---------------------------- 69 | 70 | * Fixed Inheritance bug (No adapter for child class) 71 | 72 | Version 1.1.0 *(2015-02-01)* 73 | ---------------------------- 74 | 75 | * Made IntentBinding and BundleBinding an interface. Not compatible with version 1.0.x 76 | * Works with class inheritance 77 | 78 | Version 1.0.1 *(2015-01-20)* 79 | ---------------------------- 80 | 81 | * Fixed AnnotationProcessor not running on @InjectExtra annotations 82 | 83 | Version 1.0.0 *(2014-11-19)* 84 | ---------------------------- 85 | 86 | * Initial Release 87 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/BeanAccessTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.robolectric.RobolectricTestRunner; 8 | import org.robolectric.annotation.Config; 9 | import pocketknife.PocketKnife; 10 | 11 | import java.util.UUID; 12 | 13 | import static org.junit.Assert.assertEquals; 14 | import static org.junit.Assert.assertNotEquals; 15 | 16 | @RunWith(RobolectricTestRunner.class) 17 | @Config(manifest = "src/main/AndroidManifest.xml") 18 | public class BeanAccessTest { 19 | 20 | @Test 21 | public void testSaveState() { 22 | String s = UUID.randomUUID().toString(); 23 | 24 | BeanAccess beanAccess1 = new BeanAccess(); 25 | beanAccess1.setSaveString(s); 26 | 27 | Bundle bundle = new Bundle(); 28 | PocketKnife.saveInstanceState(beanAccess1, bundle); 29 | BeanAccess beanAccess2 = new BeanAccess(); 30 | beanAccess2.setSaveString(""); 31 | assertNotEquals(s, beanAccess2.getSaveString()); 32 | PocketKnife.restoreInstanceState(beanAccess2, bundle); 33 | assertEquals(s, beanAccess2.getSaveString()); 34 | } 35 | 36 | @Test 37 | public void testBindExtras() { 38 | String s1 = UUID.randomUUID().toString(); 39 | String s2 = UUID.randomUUID().toString(); 40 | BeanAccess beanAccess = new BeanAccess(); 41 | Intent intent = new Intent(); 42 | intent.putExtra("EXTRA_EXTRA_STRING", s1); 43 | new StringSerializer().put(intent, s2, BeanAccess.INTENT_KEY); 44 | 45 | assertNotEquals(s1, beanAccess.getExtraString()); 46 | assertNotEquals(s2, beanAccess.getIsString()); 47 | PocketKnife.bindExtras(beanAccess, intent); 48 | assertEquals(s1, beanAccess.getExtraString()); 49 | assertEquals(s2, beanAccess.getIsString()); 50 | } 51 | 52 | @Test 53 | public void testBindArgs() { 54 | String s1 = UUID.randomUUID().toString(); 55 | String s2 = UUID.randomUUID().toString(); 56 | String s3 = UUID.randomUUID().toString(); 57 | BeanAccess beanAccess = new BeanAccess(); 58 | Bundle bundle = new Bundle(); 59 | bundle.putString("ARG_ARG_STRING", s1); 60 | new StringSerializer().put(bundle, s2, BeanAccess.ARG_KEY_1); 61 | bundle.putString(BeanAccess.ARG_KEY_2, s3); 62 | 63 | assertNotEquals(s1, beanAccess.getArgString()); 64 | assertNotEquals(s2, beanAccess.getBsString()); 65 | assertNotEquals(s3, beanAccess.pString); 66 | PocketKnife.bindArguments(beanAccess, bundle); 67 | assertEquals(s1, beanAccess.getArgString()); 68 | assertEquals(s2, beanAccess.getBsString()); 69 | assertEquals(s3, beanAccess.pString); 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/Intents.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import pocketknife.Data; 7 | import pocketknife.IntentBuilder; 8 | 9 | import java.io.Serializable; 10 | import java.util.ArrayList; 11 | 12 | public interface Intents { 13 | 14 | @IntentBuilder(action = "TEST") 15 | Intent getActionIntent(); 16 | 17 | @IntentBuilder(cls = SimpleActivity.class) 18 | Intent getClassIntent(); 19 | 20 | @IntentBuilder(action = "TEST", flags = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NEW_TASK) 21 | Intent getFlagIntent(); 22 | 23 | @IntentBuilder(action = "TEST") 24 | Intent getDataIntent(@Data String data); 25 | 26 | @IntentBuilder(action = "TEST") 27 | Intent getDataUriIntent(@Data Uri data); 28 | 29 | @IntentBuilder(action = "TEST", type = "application/html") 30 | Intent getTypeIntent(); 31 | 32 | @IntentBuilder(action = "TEST", categories = "ONE") 33 | Intent getSingleCategory(); 34 | 35 | @IntentBuilder(action = "TEST", categories = {"ONE", "TWO", "THREE", "FOUR" }) 36 | Intent getCategories(); 37 | 38 | @IntentBuilder(action = "TEST", 39 | categories = {"ONE", "TWO", "THREE", "FOUR" }, 40 | cls = SimpleActivity.class, 41 | flags = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NEW_TASK, 42 | type = "application/html") 43 | Intent getAllPlusExtra(@Data String data, int i); 44 | 45 | @IntentBuilder(action = "TEST", 46 | categories = {"ONE", "TWO", "THREE", "FOUR" }, 47 | cls = SimpleActivity.class, 48 | flags = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET | Intent.FLAG_ACTIVITY_NEW_TASK, 49 | type = "application/html") 50 | Intent getAllDataUriPlusExtra(@Data Uri data, int i); 51 | 52 | @IntentBuilder(action = "TEST") 53 | Intent getExtra(int i); 54 | 55 | @IntentBuilder(action = "TEST") 56 | Intent getExtras(int i, int j); 57 | 58 | @IntentBuilder(action = "TEST") 59 | Intent getNameCollisionExtra(int intent); 60 | 61 | @IntentBuilder(action = "TEST") 62 | Intent getExtras(boolean aBoolean, boolean[] booleans, Bundle bundle, byte aByte, byte[] bytes, char aChar, char[] chars, CharSequence charSequence, 63 | CharSequence[] charSequences, ArrayList charSequenceArrayList, double aDouble, double[] doubles, float aFloat, 64 | float[] floats, int anInt, int[] ints, ArrayList integerArrayList, long aLong, long[] longs, MyParcelable parcelable, 65 | MyParcelable[] parcelables, ArrayList parcelableArrayList, Serializable serializable, short aShort, short[] shorts, 66 | String string, String[] strings, ArrayList stringArrayList); 67 | } -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/MultiAnnotationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.robolectric.Robolectric; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.RuntimeEnvironment; 10 | import org.robolectric.annotation.Config; 11 | import org.robolectric.util.ActivityController; 12 | 13 | import java.util.Random; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | 17 | 18 | @RunWith(RobolectricTestRunner.class) 19 | @Config(manifest = "src/main/AndroidManifest.xml") 20 | public class MultiAnnotationTest { 21 | 22 | @Test 23 | public void testMultiAnnotationActivity() throws Exception { 24 | Random random = new Random(); 25 | 26 | Intent intent = new Intent(RuntimeEnvironment.application, MultiAnnotationActivity.class); 27 | int extraInt = random.nextInt(); 28 | intent.putExtra(MultiAnnotationActivity.EXTRA_INT, extraInt); 29 | 30 | ActivityController saveController = Robolectric.buildActivity(MultiAnnotationActivity.class).withIntent(intent).create(); 31 | MultiAnnotationActivity activity = saveController.start().restart().visible().get(); 32 | 33 | assertEquals(extraInt, activity.i); 34 | 35 | int activityInt = random.nextInt(); 36 | activity.i = activityInt; 37 | 38 | 39 | Bundle saveState = new Bundle(); 40 | saveController.saveInstanceState(saveState); 41 | 42 | ActivityController restoreController = Robolectric.buildActivity(MultiAnnotationActivity.class).withIntent(intent) 43 | .create(saveState); 44 | activity = restoreController.start().restart().visible().get(); 45 | 46 | assertEquals(activityInt, activity.i); 47 | } 48 | 49 | @Test 50 | public void testMultiAnnotationFragment() throws Exception { 51 | Random random = new Random(); 52 | 53 | Bundle args = new Bundle(); 54 | int argInt = random.nextInt(); 55 | args.putInt(MultiAnnotationFragment.ARG_INT, argInt); 56 | 57 | MultiAnnotationFragment saveFragment = MultiAnnotationFragment.newInstance(); 58 | saveFragment.setArguments(args); 59 | 60 | saveFragment.onActivityCreated(null); 61 | 62 | assertEquals(argInt, saveFragment.i); 63 | 64 | int fragmentInt = random.nextInt(); 65 | saveFragment.i = fragmentInt; 66 | 67 | Bundle saveState = new Bundle(); 68 | saveFragment.onSaveInstanceState(saveState); 69 | 70 | MultiAnnotationFragment restoreFragment = MultiAnnotationFragment.newInstance(); 71 | restoreFragment.setArguments(args); 72 | restoreFragment.onActivityCreated(saveState); 73 | 74 | assertEquals(fragmentInt, restoreFragment.i); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/SaveStateActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import android.support.v4.app.FragmentActivity; 5 | import pocketknife.PocketKnife; 6 | import pocketknife.SaveState; 7 | 8 | import java.io.Serializable; 9 | import java.util.ArrayList; 10 | 11 | public class SaveStateActivity extends FragmentActivity { 12 | 13 | // boolean dv 14 | @SaveState 15 | boolean aBoolean; 16 | // boolean[] 17 | @SaveState 18 | boolean[] booleans; 19 | // Bundle 20 | @SaveState 21 | Bundle bundle; 22 | // byte dv 23 | @SaveState 24 | byte aByte; 25 | // byte[] 26 | @SaveState 27 | byte[] bytes; 28 | // char dv 29 | @SaveState 30 | char aChar; 31 | // char[] 32 | @SaveState 33 | char[] chars; 34 | // CharSequence 35 | @SaveState 36 | CharSequence charSequence; 37 | // CharSequence[] 38 | @SaveState 39 | CharSequence[] charSequences; 40 | // ArrayList 41 | @SaveState 42 | ArrayList charSequenceArrayList; 43 | // double dv 44 | @SaveState 45 | double aDouble; 46 | // double[] 47 | @SaveState 48 | double[] doubles; 49 | // float dv 50 | @SaveState 51 | float aFloat; 52 | // float[] 53 | @SaveState 54 | float[] floats; 55 | // int dv 56 | @SaveState 57 | int anInt; 58 | // int[] 59 | @SaveState 60 | int[] ints; 61 | // ArrayList 62 | @SaveState 63 | ArrayList integerArrayList; 64 | // long dv 65 | @SaveState 66 | long aLong; 67 | // long[] 68 | @SaveState 69 | long[] longs; 70 | // Parcelable 71 | @SaveState 72 | MyParcelable parcelable; 73 | // Parcelable[] 74 | @SaveState 75 | MyParcelable[] parcelables; 76 | // ArrayList 77 | @SaveState 78 | ArrayList parcelableArrayList; 79 | // Serializable 80 | @SaveState 81 | Serializable serializable; 82 | // short dv 83 | @SaveState 84 | short aShort; 85 | // short[] 86 | @SaveState 87 | short[] shorts; 88 | // String 89 | @SaveState 90 | String string; 91 | // String[] 92 | @SaveState 93 | String[] strings; 94 | // ArrayList 95 | @SaveState 96 | ArrayList stringArrayList; 97 | 98 | @Override 99 | protected void onCreate(Bundle savedInstanceState) { 100 | super.onCreate(savedInstanceState); 101 | setContentView(R.layout.simple_activity); 102 | 103 | PocketKnife.restoreInstanceState(this, savedInstanceState); 104 | } 105 | 106 | @Override 107 | protected void onSaveInstanceState(Bundle outState) { 108 | super.onSaveInstanceState(outState); 109 | PocketKnife.saveInstanceState(this, outState); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/hansenji/pocketknife.svg?branch=master)](https://travis-ci.org/hansenji/pocketknife) 2 | 3 | ### Deprecated Look into using [Kotlin][7] and [Android Extras Delegates][6] 4 | 5 | Pocket Knife 6 | ============ 7 | 8 | Intent and Bundle utility library for Android which uses annotation processing to generate boilerplate code 9 | for you. This library is based on [ButterKnife][2] and [Dagger][1] 10 | 11 | ### NOTE 12 | Version 2.0 generated code is not compatible with version 1.0. 13 | Any libraries that use Pocket Knife will need to be updated to 2.0. 14 | 15 | ```java 16 | class ExampleActivity extends Activity { 17 | @BindExtra 18 | int id; 19 | 20 | @SaveState 21 | int listPosition; 22 | 23 | public void onCreate(Bundle savedInstanceState) { 24 | ... 25 | PocketKnife.bindExtras(this); 26 | PocketKnife.restoreInstanceState(this, savedInstanceState); 27 | ... 28 | } 29 | 30 | public void onSaveInstanceState(Bundle outState) { 31 | ... 32 | PocketKnife.saveInstanceState(this, outState); 33 | ... 34 | } 35 | } 36 | ``` 37 | 38 | For documentation and additional information see [the website][5]. 39 | 40 | Download 41 | -------- 42 | 43 | Download the latest JARs [core][3] and [compiler][4] or grab via Maven: 44 | ```xml 45 | 46 | com.vikingsen 47 | pocketknife-core 48 | 3.2.0 49 | 50 | 51 | com.vikingsen 52 | pocketknife-compiler 53 | 3.2.0 54 | provided 55 | 56 | ``` 57 | or Gradle: 58 | ```groovy 59 | compile 'com.vikingsen:pocketknife-core:3.2.0' 60 | provided 'com.vikingsen:pocketknife-compiler:3.2.0' 61 | ``` 62 | 63 | 64 | License 65 | ------- 66 | 67 | Copyright 2014-2016 Jordan Hansen 68 | 69 | Licensed under the Apache License, Version 2.0 (the "License"); 70 | you may not use this file except in compliance with the License. 71 | You may obtain a copy of the License at 72 | 73 | http://www.apache.org/licenses/LICENSE-2.0 74 | 75 | Unless required by applicable law or agreed to in writing, software 76 | distributed under the License is distributed on an "AS IS" BASIS, 77 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 78 | See the License for the specific language governing permissions and 79 | limitations under the License. 80 | 81 | 82 | 83 | [1]: http://square.github.com/dagger/ 84 | [2]: http://jakewharton.github.com/butterknife/ 85 | [3]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.vikingsen&a=pocketknife-core&v=LATEST 86 | [4]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.vikingsen&a=pocketknife-core&v=LATEST 87 | [5]: http://hansenji.github.io/pocketknife 88 | [6]: https://github.com/Takhion/android-extras-delegates 89 | [7]: https://kotlinlang.org/ 90 | -------------------------------------------------------------------------------- /pocketknife-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | com.vikingsen 8 | pocketknife-parent 9 | 3.2.2-SNAPSHOT 10 | 11 | 12 | pocketknife-sample 13 | Pocket Knife Sample 14 | apk 15 | 16 | 17 | 18 | android-support 19 | file://${env.ANDROID_HOME}/extras/android/m2repository 20 | 21 | 22 | 23 | 24 | 25 | junit 26 | junit 27 | test 28 | 29 | 30 | 31 | com.vikingsen 32 | pocketknife-core 33 | ${project.version} 34 | 35 | 36 | com.vikingsen 37 | pocketknife-compiler 38 | ${project.version} 39 | provided 40 | true 41 | 42 | 43 | 44 | org.easytesting 45 | fest-assert-core 46 | test 47 | 48 | 49 | org.robolectric 50 | robolectric 51 | test 52 | 53 | 54 | 55 | com.google.android 56 | android 57 | provided 58 | 59 | 60 | 61 | com.google.android 62 | support-v4 63 | provided 64 | 65 | 66 | 67 | 68 | 69 | 70 | com.simpligility.maven.plugins 71 | android-maven-plugin 72 | true 73 | 74 | 75 | org.jacoco 76 | jacoco-maven-plugin 77 | true 78 | 79 | 80 | org.apache.maven.plugins 81 | maven-surefire-plugin 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/IntentFieldBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import javax.lang.model.type.TypeMirror; 4 | 5 | public class IntentFieldBinding extends FieldBinding { 6 | public static final String KEY_PREFIX = "EXTRA_"; 7 | 8 | private final String name; 9 | private final Access access; 10 | private final TypeMirror type; 11 | private final String intentType; 12 | private final KeySpec key; 13 | private final TypeMirror intentSerializer; 14 | 15 | // Builder Only 16 | private final boolean arrayList; 17 | 18 | // Binder Only 19 | private final boolean required; 20 | private final boolean needsToBeCast; 21 | private final boolean hasDefault; 22 | 23 | public IntentFieldBinding(String name, Access access, TypeMirror type, String intentType, KeySpec key, boolean arrayList, TypeMirror intentSerializer) { 24 | this.name = name; 25 | this.access = access; 26 | this.type = type; 27 | this.intentType = intentType; 28 | this.key = key; 29 | this.intentSerializer = intentSerializer; 30 | this.arrayList = arrayList; 31 | 32 | this.required = false; 33 | this.needsToBeCast = false; 34 | this.hasDefault = false; 35 | } 36 | 37 | public IntentFieldBinding(String name, Access access, TypeMirror type, String intentType, KeySpec key, Boolean needsToBeCast, boolean hasDefault, 38 | boolean required, TypeMirror intentSerializer) { 39 | this.name = name; 40 | this.access = access; 41 | this.type = type; 42 | this.intentType = intentType; 43 | this.key = key; 44 | this.needsToBeCast = needsToBeCast; 45 | this.hasDefault = hasDefault; 46 | this.required = required; 47 | this.intentSerializer = intentSerializer; 48 | 49 | this.arrayList = false; 50 | } 51 | 52 | @Override 53 | public String getDescription() { 54 | return "Field '" + type + " " + name + "'"; 55 | } 56 | 57 | public String getName() { 58 | return name; 59 | } 60 | 61 | public Access getAccess() { 62 | return access; 63 | } 64 | 65 | public TypeMirror getType() { 66 | return type; 67 | } 68 | 69 | public KeySpec getKey() { 70 | return key; 71 | } 72 | 73 | public boolean isArrayList() { 74 | return arrayList; 75 | } 76 | 77 | public String getIntentType() { 78 | return intentType; 79 | } 80 | 81 | public boolean isRequired() { 82 | return required; 83 | } 84 | 85 | public boolean needsToBeCast() { 86 | return needsToBeCast; 87 | } 88 | 89 | public boolean hasDefault() { 90 | return hasDefault; 91 | } 92 | 93 | public TypeMirror getIntentSerializer() { 94 | return intentSerializer; 95 | } 96 | 97 | @Override 98 | public boolean equals(Object obj) { 99 | return obj instanceof IntentFieldBinding && this.name.equals(((IntentFieldBinding) obj).name); 100 | } 101 | 102 | @Override 103 | public int hashCode() { 104 | return name.hashCode(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/BundleFieldBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import javax.lang.model.type.TypeMirror; 4 | 5 | public class BundleFieldBinding extends FieldBinding { 6 | public static final String ARGUMENT_KEY_PREFIX = "ARG_"; 7 | public static final String SAVE_STATE_KEY_PREFIX = "BUNDLE_"; 8 | 9 | 10 | public enum AnnotationType { 11 | ARGUMENT, BUILDER, SAVE_STATE 12 | } 13 | 14 | private final AnnotationType annotationType; 15 | private final String name; 16 | private final Access access; 17 | private final TypeMirror type; 18 | private final String bundleType; 19 | private final KeySpec key; 20 | private final TypeMirror bundleSerializer; 21 | 22 | // Binder Only 23 | private final boolean needsToBeCast; 24 | private final boolean canHaveDefault; 25 | private final boolean required; 26 | 27 | public BundleFieldBinding(String name, Access access, TypeMirror type, String bundleType, KeySpec key, TypeMirror bundleSerializer) { 28 | this(AnnotationType.BUILDER, name, access, type, bundleType, key, false, false, false, bundleSerializer); 29 | } 30 | 31 | public BundleFieldBinding(AnnotationType annotationType, String name, Access access, TypeMirror type, String bundleType, KeySpec key, boolean needsToBeCast, 32 | boolean canHaveDefault, boolean required, TypeMirror bundleSerializer) { 33 | this.annotationType = annotationType; 34 | this.name = name; 35 | this.access = access; 36 | this.type = type; 37 | this.bundleType = bundleType; 38 | this.needsToBeCast = needsToBeCast; 39 | this.key = key; 40 | this.canHaveDefault = canHaveDefault; 41 | this.required = required; 42 | this.bundleSerializer = bundleSerializer; 43 | } 44 | 45 | @Override 46 | public String getDescription() { 47 | return "Field '" + type + " " + name + "'"; 48 | } 49 | 50 | public String getName() { 51 | return name; 52 | } 53 | 54 | public Access getAccess() { 55 | return access; 56 | } 57 | 58 | public TypeMirror getType() { 59 | return type; 60 | } 61 | 62 | public String getBundleType() { 63 | return bundleType; 64 | } 65 | 66 | public KeySpec getKey() { 67 | return key; 68 | } 69 | 70 | public AnnotationType getAnnotationType() { 71 | return annotationType; 72 | } 73 | 74 | public boolean isRequired() { 75 | return required; 76 | } 77 | 78 | public boolean canHaveDefault() { 79 | return canHaveDefault; 80 | } 81 | 82 | public boolean needsToBeCast() { 83 | return needsToBeCast; 84 | } 85 | 86 | public TypeMirror getBundleSerializer() { 87 | return bundleSerializer; 88 | } 89 | 90 | @Override 91 | public boolean equals(Object obj) { 92 | return obj instanceof BundleFieldBinding 93 | && this.annotationType == ((BundleFieldBinding) obj).annotationType 94 | && this.name.equals(((BundleFieldBinding) obj).name); 95 | } 96 | 97 | @Override 98 | public int hashCode() { 99 | return (annotationType + name).hashCode(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/StringSerializerClassTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.RuntimeEnvironment; 10 | import org.robolectric.annotation.Config; 11 | import pocketknife.PocketKnife; 12 | 13 | import java.util.Set; 14 | import java.util.UUID; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertNotEquals; 18 | 19 | @RunWith(RobolectricTestRunner.class) 20 | @Config(manifest = "src/main/AndroidManifest.xml") 21 | public class StringSerializerClassTest { 22 | 23 | @Test 24 | public void testSaveState() { 25 | String s = UUID.randomUUID().toString(); 26 | StringSerializerClass stringSerializerClass1 = new StringSerializerClass(); 27 | stringSerializerClass1.saveString = s; 28 | 29 | Bundle bundle = new Bundle(); 30 | PocketKnife.saveInstanceState(stringSerializerClass1, bundle); 31 | Set keys = bundle.keySet(); 32 | for (String key : keys) { 33 | assertNotEquals(s, bundle.getString(key)); 34 | } 35 | StringSerializerClass stringSerializerClass2 = new StringSerializerClass(); 36 | PocketKnife.restoreInstanceState(stringSerializerClass2, bundle); 37 | assertEquals(s, stringSerializerClass2.saveString); 38 | } 39 | 40 | @Test 41 | public void testBuildAndBindArgument() { 42 | String s = UUID.randomUUID().toString(); 43 | StringSerializerClass stringSerializerClass = new StringSerializerClass(); 44 | SerializerBuilder builder = new PocketKnifeSerializerBuilder(RuntimeEnvironment.application); 45 | Bundle bundle = builder.buildBundle(s); 46 | 47 | assertNotEquals(s, bundle.getString(PocketKnifeSerializerBuilder.ARG_ARG_STRING)); 48 | PocketKnife.bindArguments(stringSerializerClass, bundle); 49 | assertEquals(s, stringSerializerClass.argString); 50 | } 51 | 52 | @Test 53 | public void testBuildAndBindExtras() { 54 | String s = UUID.randomUUID().toString(); 55 | StringSerializerClass stringSerializerClass = new StringSerializerClass(); 56 | SerializerBuilder builder = new PocketKnifeSerializerBuilder(RuntimeEnvironment.application); 57 | Intent intent = builder.buildIntent(s); 58 | 59 | assertNotEquals(s, intent.getStringExtra(PocketKnifeSerializerBuilder.EXTRA_EXTRA_STRING)); 60 | PocketKnife.bindExtras(stringSerializerClass, intent); 61 | assertEquals(s, stringSerializerClass.extraString); 62 | } 63 | 64 | @Test 65 | public void testBuildFragmentAndBindArgument() { 66 | String s = UUID.randomUUID().toString(); 67 | StringSerializerClass stringSerializerClass = new StringSerializerClass(); 68 | SerializerBuilder builder = new PocketKnifeSerializerBuilder(RuntimeEnvironment.application); 69 | Fragment fragment = builder.buildFragment(s); 70 | Bundle bundle = fragment.getArguments(); 71 | 72 | assertNotEquals(s, bundle.getString(PocketKnifeSerializerBuilder.ARG_ARG_STRING)); 73 | PocketKnife.bindArguments(stringSerializerClass, bundle); 74 | assertEquals(s, stringSerializerClass.argString); 75 | } 76 | } -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/InheritanceTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.robolectric.Robolectric; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.RuntimeEnvironment; 10 | import org.robolectric.annotation.Config; 11 | import org.robolectric.util.ActivityController; 12 | 13 | import java.util.Random; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | 17 | @RunWith(RobolectricTestRunner.class) 18 | @Config(manifest = "src/main/AndroidManifest.xml") 19 | public class InheritanceTest { 20 | 21 | @Test 22 | public void testInheritance() throws Exception { 23 | Random random = new Random(42); 24 | 25 | Bundle args = new Bundle(); 26 | int parentArg = random.nextInt(); 27 | int childArg = random.nextInt(); 28 | args.putInt(ChildFragment.CHILD_ARG, childArg); 29 | args.putInt(ParentFragment.PARENT_ARG, parentArg); 30 | 31 | Intent intent = new Intent(RuntimeEnvironment.application, GrandchildActivity.class); 32 | int parentExtra = random.nextInt(); 33 | int childExtra = random.nextInt(); 34 | intent.putExtra(ParentActivity.PARENT_EXTRA, parentExtra); 35 | intent.putExtra(ChildActivity.CHILD_EXTRA, childExtra); 36 | intent.putExtra(ChildActivity.FRAGMENT_ARGS, args); 37 | 38 | ActivityController initialController = Robolectric.buildActivity(GrandchildActivity.class).withIntent(intent).create(); 39 | GrandchildActivity originalActivity = initialController.start().restart().visible().get(); 40 | 41 | assertEquals(childExtra, originalActivity.childExtra); 42 | assertEquals(parentExtra, originalActivity.parentExtra); 43 | 44 | assertEquals(childArg, originalActivity.fragment.childArg); 45 | assertEquals(parentArg, originalActivity.fragment.parentArg); 46 | 47 | originalActivity.paInt = random.nextInt(); 48 | originalActivity.caInt = random.nextInt(); 49 | 50 | originalActivity.fragment.pfInt = random.nextInt(); 51 | originalActivity.fragment.cfInt = random.nextInt(); 52 | 53 | Bundle saveState = new Bundle(); 54 | initialController.saveInstanceState(saveState); 55 | 56 | 57 | Bundle copyArgs = new Bundle(); 58 | copyArgs.putInt(ChildFragment.CHILD_ARG, random.nextInt() - childArg); 59 | copyArgs.putInt(ParentFragment.PARENT_ARG, random.nextInt() - parentArg); 60 | 61 | Intent copyIntent = new Intent(RuntimeEnvironment.application, GrandchildActivity.class); 62 | copyIntent.putExtra(ParentActivity.PARENT_EXTRA, random.nextInt() - parentExtra); 63 | copyIntent.putExtra(ChildActivity.CHILD_EXTRA, random.nextInt() - childExtra); 64 | copyIntent.putExtra(ChildActivity.FRAGMENT_ARGS, copyArgs); 65 | 66 | ActivityController secondaryController = Robolectric.buildActivity(GrandchildActivity.class).withIntent(copyIntent).create(saveState); 67 | GrandchildActivity copyActivity = secondaryController.start().restart().visible().get(); 68 | 69 | assertEquals(originalActivity.caInt, copyActivity.caInt); 70 | assertEquals(originalActivity.paInt, copyActivity.paInt); 71 | 72 | assertEquals(originalActivity.fragment.cfInt, copyActivity.fragment.cfInt); 73 | assertEquals(originalActivity.fragment.pfInt, copyActivity.fragment.pfInt); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/builder/BuilderGenerator.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.builder; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.FieldSpec; 5 | import com.squareup.javapoet.JavaFile; 6 | import com.squareup.javapoet.MethodSpec; 7 | import com.squareup.javapoet.TypeSpec; 8 | import org.apache.commons.lang3.StringUtils; 9 | import pocketknife.internal.codegen.BaseGenerator; 10 | import pocketknife.internal.codegen.KeySpec; 11 | import pocketknife.internal.codegen.MethodBinding; 12 | import pocketknife.internal.codegen.TypeUtil; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Set; 17 | import java.util.TreeSet; 18 | 19 | import static javax.lang.model.element.Modifier.FINAL; 20 | import static javax.lang.model.element.Modifier.PRIVATE; 21 | import static javax.lang.model.element.Modifier.PUBLIC; 22 | import static javax.lang.model.element.Modifier.STATIC; 23 | 24 | public class BuilderGenerator extends BaseGenerator { 25 | public static final String CONTEXT = "context"; 26 | private final String classPackage; 27 | private final String className; 28 | private final String interfaceName; 29 | 30 | private List methods = new ArrayList(); 31 | private boolean contextRequired = false; 32 | 33 | public BuilderGenerator(String classPackage, String className, String interfaceName, TypeUtil typeUtil) { 34 | super(typeUtil); 35 | this.classPackage = classPackage; 36 | this.className = className; 37 | this.interfaceName = interfaceName; 38 | } 39 | 40 | public JavaFile generate() { 41 | TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className) 42 | .addSuperinterface(ClassName.get(classPackage, interfaceName)) 43 | .addModifiers(PUBLIC) 44 | .addJavadoc(getGeneratedComment(BuilderGenerator.class)); 45 | 46 | generateKeys(classBuilder); 47 | 48 | if (contextRequired) { 49 | classBuilder.addField(ClassName.get(typeUtil.contextType), CONTEXT, PRIVATE, FINAL); 50 | classBuilder.addMethod(MethodSpec.constructorBuilder() 51 | .addModifiers(PUBLIC) 52 | .addParameter(ClassName.get(typeUtil.contextType), CONTEXT) 53 | .addStatement("this.$N = $N", CONTEXT, CONTEXT) 54 | .build()); 55 | } 56 | 57 | generateMethods(classBuilder); 58 | 59 | return JavaFile.builder(classPackage, classBuilder.build()).build(); 60 | } 61 | 62 | private void generateKeys(TypeSpec.Builder classBuilder) { 63 | Set keys = new TreeSet(); 64 | for (MethodBinding method : methods) { 65 | keys.addAll(method.getKeys()); 66 | } 67 | 68 | for (KeySpec key : keys) { 69 | if (StringUtils.isNotBlank(key.getName())) { 70 | classBuilder.addField(FieldSpec.builder(String.class, key.getName(), PUBLIC, STATIC, FINAL) 71 | .initializer("$S", key.getValue()) 72 | .build()); 73 | } 74 | } 75 | 76 | } 77 | 78 | private void generateMethods(TypeSpec.Builder classBuilder) { 79 | for (MethodBinding method : methods) { 80 | classBuilder.addMethod(method.generateMethodSpec(typeUtil)); 81 | } 82 | } 83 | 84 | public void addMethod(MethodBinding method) { 85 | contextRequired |= method instanceof IntentMethodBinding; 86 | this.methods.add(method); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/builder/BundleMethodBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.builder; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.MethodSpec; 5 | import org.apache.commons.lang3.StringUtils; 6 | import pocketknife.internal.codegen.BundleFieldBinding; 7 | import pocketknife.internal.codegen.KeySpec; 8 | import pocketknife.internal.codegen.MethodBinding; 9 | import pocketknife.internal.codegen.TypeUtil; 10 | 11 | import java.util.ArrayList; 12 | import java.util.LinkedHashSet; 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | import static javax.lang.model.element.Modifier.PUBLIC; 17 | 18 | public class BundleMethodBinding extends MethodBinding { 19 | 20 | private static final String RETURN_VAR_NAME_ROOT = "bundle"; 21 | 22 | private final String name; 23 | 24 | private final List fields = new ArrayList(); 25 | 26 | public BundleMethodBinding(String name) { 27 | this.name = name; 28 | } 29 | 30 | public void addField(BundleFieldBinding fieldBinding) { 31 | if (fields.contains(fieldBinding)) { 32 | throw new IllegalStateException("Cannot have multiple arguments named: " + fieldBinding.getName()); 33 | } 34 | fields.add(fieldBinding); 35 | } 36 | 37 | @Override 38 | public Set getKeys() { 39 | Set keys = new LinkedHashSet(); 40 | for (BundleFieldBinding field : fields) { 41 | keys.add(field.getKey()); 42 | } 43 | return keys; 44 | } 45 | 46 | @Override 47 | public MethodSpec generateMethodSpec(TypeUtil typeUtil) { 48 | String returnVarName = getReturnVarName(RETURN_VAR_NAME_ROOT); 49 | 50 | MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(name) 51 | .addAnnotation(Override.class) 52 | .addModifiers(PUBLIC) 53 | .returns(ClassName.get(typeUtil.bundleType)) 54 | .addStatement("$T $N = new $T()", ClassName.get(typeUtil.bundleType), returnVarName, ClassName.get(typeUtil.bundleType)); 55 | 56 | for (BundleFieldBinding field : fields) { 57 | methodBuilder.addParameter(ClassName.get(field.getType()), field.getName()); 58 | KeySpec key = field.getKey(); 59 | if (field.getBundleSerializer() == null) { 60 | String keyValue; 61 | String stmt = "$N.put$L("; 62 | if (StringUtils.isBlank(key.getName())) { 63 | keyValue = key.getValue(); 64 | stmt = stmt.concat("$S"); 65 | } else { 66 | keyValue = key.getName(); 67 | stmt = stmt.concat("$N"); 68 | } 69 | stmt = stmt.concat(", $N)"); 70 | methodBuilder.addStatement(stmt, returnVarName, field.getBundleType(), keyValue, field.getName()); 71 | } else { 72 | if (StringUtils.isBlank(key.getName())) { 73 | methodBuilder.addStatement("new $T().put($N, $N, $S)", field.getBundleSerializer(), returnVarName, field.getName(), key.getValue()); 74 | } else { 75 | methodBuilder.addStatement("new $T().put($N, $N, $N)", field.getBundleSerializer(), returnVarName, field.getName(), key.getName()); 76 | } 77 | } 78 | } 79 | 80 | methodBuilder.addStatement("return $N", returnVarName); 81 | 82 | return methodBuilder.build(); 83 | } 84 | 85 | @Override 86 | public List getFields() { 87 | return fields; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /pocketknife/src/main/java/pocketknife/internal/Memoizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package pocketknife.internal; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | import java.util.concurrent.locks.Lock; 21 | import java.util.concurrent.locks.ReadWriteLock; 22 | import java.util.concurrent.locks.ReentrantReadWriteLock; 23 | 24 | /** 25 | * An abstract supertype that provides 26 | * memoization for idempotent operations that 27 | * may be computed more than once, but for which performance is prohibitive. Subclasses 28 | * implement the {@link #create} method with the operation to be memoized, while callers invoke the 29 | * {@link #get} method to utilize the memoization. 30 | * 31 | *

Synchronization on this class is implemented using a {@link java.util.concurrent.locks.ReadWriteLock}. Multiple threads 32 | * may accessed previously memoized results without contention. 33 | * 34 | *

This class is implemented such that concurrent requests for the same key may result in 35 | * simultaneous computation in multiple threads - the instance of the result that is persisted for 36 | * subsequent invocations in not guaranteed. 37 | * 38 | *

Warning: there is no eviction. Large input sets will result in growth without bound. 39 | */ 40 | public abstract class Memoizer { 41 | private final Map map; 42 | private final Lock readLock; 43 | private final Lock writeLock; 44 | 45 | protected Memoizer() { 46 | // Don't use LinkedHashMap. This is a performance-oriented class and we don't want overhead 47 | this.map = new HashMap(); 48 | ReadWriteLock lock = new ReentrantReadWriteLock(); 49 | this.readLock = lock.readLock(); 50 | this.writeLock = lock.writeLock(); 51 | } 52 | 53 | public final V get(K key) { 54 | if (key == null) { 55 | throw new NullPointerException("key == null"); 56 | } 57 | 58 | // check to see if we already have a value 59 | readLock.lock(); 60 | try { 61 | V value = map.get(key); 62 | if (value != null) { 63 | return value; 64 | } 65 | } finally { 66 | readLock.unlock(); 67 | } 68 | 69 | // create a new value. this may race and we might create more than one instance, but that's ok 70 | V newValue = create(key); 71 | if (newValue == null) { 72 | throw new NullPointerException("create returned null"); 73 | } 74 | 75 | // write the new value and return it 76 | writeLock.lock(); 77 | try { 78 | map.put(key, newValue); 79 | return newValue; 80 | } finally { 81 | writeLock.unlock(); 82 | } 83 | } 84 | 85 | protected abstract V create(K key); 86 | 87 | @Override public final String toString() { 88 | readLock.lock(); 89 | try { 90 | return map.toString(); 91 | } finally { 92 | readLock.unlock(); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/BuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.robolectric.RobolectricTestRunner; 11 | import org.robolectric.RuntimeEnvironment; 12 | import org.robolectric.annotation.Config; 13 | 14 | import java.util.Random; 15 | 16 | import static org.junit.Assert.assertEquals; 17 | import static org.junit.Assert.assertNotNull; 18 | 19 | @RunWith(RobolectricTestRunner.class) 20 | @Config(manifest = "src/main/AndroidManifest.xml") 21 | public class BuilderTest { 22 | 23 | private Builder builder; 24 | private Random random; 25 | 26 | @Before 27 | public void setup() { 28 | random = new Random(42); 29 | builder = new PocketKnifeBuilder(RuntimeEnvironment.application); 30 | } 31 | 32 | @Test 33 | public void testGetBundle() throws Exception { 34 | int i = random.nextInt(); 35 | int j = random.nextInt(); 36 | Bundle bundle = builder.getBundle(i, j); 37 | assertEquals(i, bundle.getInt(PocketKnifeBuilder.ARG_ARG, (i << 1) | 1)); 38 | assertEquals(j, bundle.getInt("THIS IS A TEST", (j << 1) | 1)); 39 | } 40 | 41 | @Test 42 | public void testGetIntent() throws Exception { 43 | int i = random.nextInt(); 44 | String s = "This is a test"; 45 | Intent intent = builder.getIntent(i, s); 46 | assertEquals(Intent.ACTION_DEFAULT, intent.getAction()); 47 | assertEquals(i, intent.getIntExtra(PocketKnifeBuilder.EXTRA_EXTRA, i << 1)); 48 | assertEquals(s, intent.getStringExtra(Intent.EXTRA_TEXT)); 49 | } 50 | 51 | @Test 52 | public void testGetFragment() throws Exception { 53 | int i = random.nextInt(); 54 | Fragment fragment = builder.getFragment(i); 55 | assertNotNull("Fragment", fragment); 56 | Bundle args = fragment.getArguments(); 57 | assertNotNull("Args", args); 58 | assertEquals(i, args.getInt(PocketKnifeBuilder.ARG_ARG, i << 1)); 59 | 60 | } 61 | 62 | @Test 63 | public void testKeyNameConflict() { 64 | int i = random.nextInt(); 65 | int j = random.nextInt(); 66 | int k = random.nextInt(); 67 | Bundle b1 = builder.getBundle(i); 68 | Bundle b2 = builder.getBundle(j, k); 69 | assertEquals(i, b1.getInt("Testing is awesome", (i << i) | 1)); 70 | assertEquals(j, b2.getInt(PocketKnifeBuilder.ARG_ARG, (j << 1) | 1)); 71 | assertEquals(k, b2.getInt("THIS IS A TEST", (k << 1) | 1)); 72 | 73 | Fragment f1 = builder.getFragment(i); 74 | assertNotNull("Fragment", f1); 75 | Bundle args1 = f1.getArguments(); 76 | assertNotNull("Args", args1); 77 | assertEquals(i, args1.getInt(PocketKnifeBuilder.ARG_ARG, i << 1)); 78 | 79 | Fragment f2 = builder.getFragment(j, k); 80 | assertNotNull("Fragment 2", f2); 81 | Bundle args2 = f2.getArguments(); 82 | assertNotNull("Args 2", args2); 83 | assertEquals(j, args2.getInt("Fragment Test", j << 1)); 84 | assertEquals(k, args2.getInt(PocketKnifeBuilder.ARG_ARG2, k << 1)); 85 | 86 | String text1 = "Text 1"; 87 | String text2 = "Text 2"; 88 | Intent i1 = builder.getIntent(i, text1); 89 | assertEquals(Intent.ACTION_DEFAULT, i1.getAction()); 90 | assertEquals(i, i1.getIntExtra(PocketKnifeBuilder.EXTRA_EXTRA, i << 1)); 91 | assertEquals(text1, i1.getStringExtra(Intent.EXTRA_TEXT)); 92 | 93 | Intent i2 = builder.getIntent(text2); 94 | assertEquals(Intent.ACTION_DEFAULT, i2.getAction()); 95 | assertEquals(text2, i2.getStringExtra(PocketKnifeBuilder.EXTRA_TEXT)); 96 | } 97 | 98 | @After 99 | public void teardown() { 100 | random = null; 101 | builder = null; 102 | } 103 | } -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/FooSerializerClassTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.robolectric.RobolectricTestRunner; 10 | import org.robolectric.RuntimeEnvironment; 11 | import org.robolectric.annotation.Config; 12 | import pocketknife.PocketKnife; 13 | 14 | import java.util.Random; 15 | import java.util.UUID; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertTrue; 19 | 20 | @RunWith(RobolectricTestRunner.class) 21 | @Config(manifest = "src/main/AndroidManifest.xml") 22 | public class FooSerializerClassTest { 23 | 24 | private Random random; 25 | 26 | @Before 27 | public void setup() { 28 | random = new Random(42); 29 | } 30 | 31 | @Test 32 | public void testSaveState() { 33 | String bar = UUID.randomUUID().toString(); 34 | int baz = random.nextInt(); 35 | 36 | Foo foo1 = new Foo(bar, baz); 37 | FooSerializerClass fooSerializerClass1 = new FooSerializerClass(); 38 | fooSerializerClass1.saveFoo = foo1; 39 | 40 | Bundle bundle = new Bundle(); 41 | PocketKnife.saveInstanceState(fooSerializerClass1, bundle); 42 | 43 | Foo foo2 = new Foo(); 44 | FooSerializerClass fooSerializerClass2 = new FooSerializerClass(); 45 | fooSerializerClass2.saveFoo = foo2; 46 | 47 | PocketKnife.restoreInstanceState(fooSerializerClass2, bundle); 48 | assertTrue(foo2 == fooSerializerClass2.saveFoo); // Same memory address didn't get reassigned 49 | assertEquals(bar, foo2.getBar()); 50 | assertEquals(baz, foo2.getBaz()); 51 | } 52 | 53 | @Test 54 | public void testBuildAndBindArguments() { 55 | String bar = UUID.randomUUID().toString(); 56 | int baz = random.nextInt(); 57 | SerializerBuilder builder = new PocketKnifeSerializerBuilder(RuntimeEnvironment.application); 58 | Bundle bundle = builder.buildBundle(new Foo(bar, baz)); 59 | 60 | Foo foo = new Foo(); 61 | FooSerializerClass fooSerializerClass = new FooSerializerClass(); 62 | fooSerializerClass.argFoo = foo; 63 | PocketKnife.bindArguments(fooSerializerClass, bundle); 64 | 65 | 66 | assertTrue(foo == fooSerializerClass.argFoo); // Same memory address didn't get reassigned 67 | assertEquals(bar, foo.getBar()); 68 | assertEquals(baz, foo.getBaz()); 69 | } 70 | 71 | @Test 72 | public void testBuildAndBindExtras() { 73 | String bar = UUID.randomUUID().toString(); 74 | int baz = random.nextInt(); 75 | SerializerBuilder builder = new PocketKnifeSerializerBuilder(RuntimeEnvironment.application); 76 | Intent intent = builder.buildIntent(new Foo(bar, baz)); 77 | 78 | Foo foo = new Foo(); 79 | FooSerializerClass fooSerializerClass = new FooSerializerClass(); 80 | fooSerializerClass.extraFoo = foo; 81 | PocketKnife.bindExtras(fooSerializerClass, intent); 82 | 83 | assertTrue(foo == fooSerializerClass.extraFoo); // Same memory address didn't get reassigned 84 | assertEquals(bar, foo.getBar()); 85 | assertEquals(baz, foo.getBaz()); 86 | } 87 | 88 | @Test 89 | public void testBuildFragmentAndBindArguments() { 90 | String bar = UUID.randomUUID().toString(); 91 | int baz = random.nextInt(); 92 | SerializerBuilder builder = new PocketKnifeSerializerBuilder(RuntimeEnvironment.application); 93 | Fragment fragment = builder.buildFragment(new Foo(bar, baz)); 94 | Bundle bundle = fragment.getArguments(); 95 | 96 | Foo foo = new Foo(); 97 | FooSerializerClass fooSerializerClass = new FooSerializerClass(); 98 | fooSerializerClass.argFoo = foo; 99 | PocketKnife.bindArguments(fooSerializerClass, bundle); 100 | 101 | assertTrue(foo == fooSerializerClass.argFoo); // Same memory address didn't get reassigned 102 | assertEquals(bar, foo.getBar()); 103 | assertEquals(baz, foo.getBaz()); 104 | } 105 | } -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/BaseProcessor.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import com.google.common.base.CaseFormat; 4 | 5 | import javax.lang.model.element.AnnotationMirror; 6 | import javax.lang.model.element.AnnotationValue; 7 | import javax.lang.model.element.Element; 8 | import javax.lang.model.element.ExecutableElement; 9 | import javax.lang.model.type.DeclaredType; 10 | import javax.lang.model.type.TypeMirror; 11 | import javax.lang.model.util.Elements; 12 | import javax.lang.model.util.Types; 13 | import java.lang.annotation.Annotation; 14 | import java.util.Map; 15 | 16 | public abstract class BaseProcessor { 17 | 18 | protected Elements elements; 19 | protected Types types; 20 | protected TypeUtil typeUtil; 21 | 22 | public BaseProcessor(Elements elements, Types types) { 23 | this.elements = elements; 24 | this.types = types; 25 | this.typeUtil = TypeUtil.getInstance(elements, types); 26 | } 27 | 28 | protected boolean isDefaultAnnotationElement(Element element, String annotation, String annotationElement) { 29 | for (AnnotationMirror mirror : element.getAnnotationMirrors()) { 30 | if (annotation.equals(mirror.getAnnotationType().toString())) { 31 | for (Map.Entry entry : mirror.getElementValues().entrySet()) { 32 | if (annotationElement.equals(entry.getKey().getSimpleName().toString())) { 33 | return false; 34 | } 35 | } 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | protected String generateKey(String prefix, String name) { 42 | return prefix + CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, name); 43 | } 44 | 45 | protected TypeMirror getAnnotationElementClass(Element element, Class annotation) { 46 | return getAnnotationElementClass(element, annotation, "value"); 47 | } 48 | 49 | protected TypeMirror getAnnotationElementClass(Element element, Class annotation, String annotationElement) { 50 | for (AnnotationMirror mirror : element.getAnnotationMirrors()) { 51 | if (annotation.getName().equals(mirror.getAnnotationType().toString())) { 52 | for (Map.Entry entry : mirror.getElementValues().entrySet()) { 53 | if (annotationElement.equals(entry.getKey().getSimpleName().toString())) { 54 | return (TypeMirror) entry.getValue().getValue(); 55 | } 56 | } 57 | break; 58 | } 59 | } 60 | return null; 61 | } 62 | 63 | protected void validateSerializer(Element element, Class annotation, TypeMirror serializer, Class abstractSerializer) { 64 | if (serializer == null) { 65 | return; 66 | } 67 | Element absSerializerElement = elements.getTypeElement(abstractSerializer.getName()); 68 | if (absSerializerElement == null) { 69 | throw new IllegalStateException(String.format("Unable to find %s type", abstractSerializer.getName())); 70 | } 71 | TypeMirror absSerializerMirror = absSerializerElement.asType(); 72 | if (!types.isAssignable(serializer, types.erasure(absSerializerMirror))) { 73 | throw new IllegalStateException(String.format("@%s value must extend %s", annotation.getName(), abstractSerializer.getName())); 74 | } 75 | TypeMirror elementType = element.asType(); 76 | for (TypeMirror superType : types.directSupertypes(serializer)) { 77 | if (types.isAssignable(superType, types.erasure(absSerializerMirror)) && superType instanceof DeclaredType) { 78 | if (types.isSameType(((DeclaredType) superType).getTypeArguments().get(0), elementType)) { 79 | return; 80 | } else { 81 | throw new IllegalStateException(String.format("Serializer T must be of type %s", elementType.toString())); 82 | } 83 | } 84 | } 85 | throw new IllegalStateException(String.format("%s must extend %s", serializer.toString(), absSerializerMirror.toString())); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/builder/FragmentMethodBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.builder; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.MethodSpec; 5 | import org.apache.commons.lang3.StringUtils; 6 | import pocketknife.internal.codegen.BundleFieldBinding; 7 | import pocketknife.internal.codegen.FieldBinding; 8 | import pocketknife.internal.codegen.KeySpec; 9 | import pocketknife.internal.codegen.MethodBinding; 10 | import pocketknife.internal.codegen.TypeUtil; 11 | 12 | import javax.lang.model.type.TypeMirror; 13 | import java.util.ArrayList; 14 | import java.util.LinkedHashSet; 15 | import java.util.List; 16 | import java.util.Set; 17 | 18 | import static javax.lang.model.element.Modifier.PUBLIC; 19 | 20 | public class FragmentMethodBinding extends MethodBinding { 21 | 22 | private static final String RETURN_VAR_NAME_ROOT = "fragment"; 23 | private static final String ARGS_VAR_NAME_ROOT = "args"; 24 | 25 | private final String name; 26 | private final TypeMirror returnType; 27 | private final List fields = new ArrayList(); 28 | 29 | 30 | public FragmentMethodBinding(String name, TypeMirror returnType) { 31 | this.name = name; 32 | this.returnType = returnType; 33 | } 34 | 35 | public void addField(BundleFieldBinding fieldBinding) { 36 | if (fields.contains(fieldBinding)) { 37 | throw new IllegalStateException("Cannot have multiple arguments named: " + fieldBinding.getName()); 38 | } 39 | fields.add(fieldBinding); 40 | } 41 | 42 | @Override 43 | public List getFields() { 44 | return fields; 45 | } 46 | 47 | @Override 48 | public Set getKeys() { 49 | Set keys = new LinkedHashSet(); 50 | for (BundleFieldBinding field : fields) { 51 | keys.add(field.getKey()); 52 | } 53 | return keys; 54 | } 55 | 56 | @Override 57 | public MethodSpec generateMethodSpec(TypeUtil typeUtil) { 58 | String returnVarName = getReturnVarName(RETURN_VAR_NAME_ROOT); 59 | String argsVarName = getReturnVarName(ARGS_VAR_NAME_ROOT); 60 | 61 | MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(name) 62 | .addAnnotation(Override.class) 63 | .addModifiers(PUBLIC) 64 | .returns(ClassName.get(returnType)) 65 | .addStatement("$T $N = new $T()", ClassName.get(returnType), returnVarName, ClassName.get(returnType)); 66 | if (!fields.isEmpty()) { 67 | methodBuilder.addStatement("$T $N = new $T()", ClassName.get(typeUtil.bundleType), argsVarName, ClassName.get(typeUtil.bundleType)); 68 | } 69 | 70 | for (BundleFieldBinding field : fields) { 71 | methodBuilder.addParameter(ClassName.get(field.getType()), field.getName()); 72 | KeySpec key = field.getKey(); 73 | if (field.getBundleSerializer() == null) { 74 | String keyValue; 75 | String stmt = "$N.put$L("; 76 | if (StringUtils.isBlank(key.getName())) { 77 | keyValue = key.getValue(); 78 | stmt = stmt.concat("$S"); 79 | } else { 80 | keyValue = key.getName(); 81 | stmt = stmt.concat("$N"); 82 | } 83 | stmt = stmt.concat(", $N)"); 84 | methodBuilder.addStatement(stmt, argsVarName, field.getBundleType(), keyValue, field.getName()); 85 | } else { 86 | if (StringUtils.isBlank(key.getName())) { 87 | methodBuilder.addStatement("new $T().put($N, $N, $S)", field.getBundleSerializer(), argsVarName, field.getName(), key.getValue()); 88 | } else { 89 | methodBuilder.addStatement("new $T().put($N, $N, $N)", field.getBundleSerializer(), argsVarName, field.getName(), key.getName()); 90 | } 91 | } 92 | } 93 | 94 | if (!fields.isEmpty()) { 95 | methodBuilder.addStatement("$N.setArguments($N)", returnVarName, argsVarName); 96 | } 97 | 98 | methodBuilder.addStatement("return $N", returnVarName); 99 | 100 | return methodBuilder.build(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /website/stylesheet/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #404040 } 2 | .highlight { color: #d0d0d0 } 3 | .highlight .c { color: #999999; font-style: italic } /* Comment */ 4 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ 5 | .highlight .g { color: #d0d0d0 } /* Generic */ 6 | .highlight .k { color: #6ab825; font-weight: normal } /* Keyword */ 7 | .highlight .l { color: #d0d0d0 } /* Literal */ 8 | .highlight .n { color: #d0d0d0 } /* Name */ 9 | .highlight .o { color: #d0d0d0 } /* Operator */ 10 | .highlight .x { color: #d0d0d0 } /* Other */ 11 | .highlight .p { color: #d0d0d0 } /* Punctuation */ 12 | .highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */ 13 | .highlight .cp { color: #cd2828; font-weight: normal } /* Comment.Preproc */ 14 | .highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */ 15 | .highlight .cs { color: #e50808; font-weight: normal; background-color: #520000 } /* Comment.Special */ 16 | .highlight .gd { color: #d22323 } /* Generic.Deleted */ 17 | .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ 18 | .highlight .gr { color: #d22323 } /* Generic.Error */ 19 | .highlight .gh { color: #ffffff; font-weight: normal } /* Generic.Heading */ 20 | .highlight .gi { color: #589819 } /* Generic.Inserted */ 21 | .highlight .go { color: #cccccc } /* Generic.Output */ 22 | .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ 23 | .highlight .gs { color: #d0d0d0; font-weight: normal } /* Generic.Strong */ 24 | .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ 25 | .highlight .gt { color: #d22323 } /* Generic.Traceback */ 26 | .highlight .kc { color: #6ab825; font-weight: normal } /* Keyword.Constant */ 27 | .highlight .kd { color: #6ab825; font-weight: normal } /* Keyword.Declaration */ 28 | .highlight .kn { color: #6ab825; font-weight: normal } /* Keyword.Namespace */ 29 | .highlight .kp { color: #6ab825 } /* Keyword.Pseudo */ 30 | .highlight .kr { color: #6ab825; font-weight: normal } /* Keyword.Reserved */ 31 | .highlight .kt { color: #6ab825; font-weight: normal } /* Keyword.Type */ 32 | .highlight .ld { color: #d0d0d0 } /* Literal.Date */ 33 | .highlight .m { color: #3677a9 } /* Literal.Number */ 34 | .highlight .s { color: #9dd5f1 } /* Literal.String */ 35 | .highlight .na { color: #bbbbbb } /* Name.Attribute */ 36 | .highlight .nb { color: #24909d } /* Name.Builtin */ 37 | .highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */ 38 | .highlight .no { color: #40ffff } /* Name.Constant */ 39 | .highlight .nd { color: #ffa500 } /* Name.Decorator */ 40 | .highlight .ni { color: #d0d0d0 } /* Name.Entity */ 41 | .highlight .ne { color: #bbbbbb } /* Name.Exception */ 42 | .highlight .nf { color: #447fcf } /* Name.Function */ 43 | .highlight .nl { color: #d0d0d0 } /* Name.Label */ 44 | .highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */ 45 | .highlight .nx { color: #d0d0d0 } /* Name.Other */ 46 | .highlight .py { color: #d0d0d0 } /* Name.Property */ 47 | .highlight .nt { color: #6ab825;} /* Name.Tag */ 48 | .highlight .nv { color: #40ffff } /* Name.Variable */ 49 | .highlight .ow { color: #6ab825; font-weight: normal } /* Operator.Word */ 50 | .highlight .w { color: #666666 } /* Text.Whitespace */ 51 | .highlight .mf { color: #3677a9 } /* Literal.Number.Float */ 52 | .highlight .mh { color: #3677a9 } /* Literal.Number.Hex */ 53 | .highlight .mi { color: #3677a9 } /* Literal.Number.Integer */ 54 | .highlight .mo { color: #3677a9 } /* Literal.Number.Oct */ 55 | .highlight .sb { color: #9dd5f1 } /* Literal.String.Backtick */ 56 | .highlight .sc { color: #9dd5f1 } /* Literal.String.Char */ 57 | .highlight .sd { color: #9dd5f1 } /* Literal.String.Doc */ 58 | .highlight .s2 { color: #9dd5f1 } /* Literal.String.Double */ 59 | .highlight .se { color: #9dd5f1 } /* Literal.String.Escape */ 60 | .highlight .sh { color: #9dd5f1 } /* Literal.String.Heredoc */ 61 | .highlight .si { color: #9dd5f1 } /* Literal.String.Interpol */ 62 | .highlight .sx { color: #ffa500 } /* Literal.String.Other */ 63 | .highlight .sr { color: #9dd5f1 } /* Literal.String.Regex */ 64 | .highlight .s1 { color: #9dd5f1 } /* Literal.String.Single */ 65 | .highlight .ss { color: #9dd5f1 } /* Literal.String.Symbol */ 66 | .highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */ 67 | .highlight .vc { color: #40ffff } /* Name.Variable.Class */ 68 | .highlight .vg { color: #40ffff } /* Name.Variable.Global */ 69 | .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ 70 | .highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /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 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/PocketKnifeProcessor.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen; 2 | 3 | import com.google.common.collect.ImmutableSet; 4 | import com.squareup.javapoet.JavaFile; 5 | import pocketknife.BindArgument; 6 | import pocketknife.BundleBuilder; 7 | import pocketknife.FragmentBuilder; 8 | import pocketknife.BindExtra; 9 | import pocketknife.IntentBuilder; 10 | import pocketknife.SaveState; 11 | import pocketknife.internal.codegen.builder.BuilderGenerator; 12 | import pocketknife.internal.codegen.builder.BuilderProcessor; 13 | import pocketknife.internal.codegen.binding.BundleBindingAdapterGenerator; 14 | import pocketknife.internal.codegen.binding.BundleBindingProcessor; 15 | import pocketknife.internal.codegen.binding.IntentBindingAdapterGenerator; 16 | import pocketknife.internal.codegen.binding.IntentBindingProcessor; 17 | 18 | import javax.annotation.processing.AbstractProcessor; 19 | import javax.annotation.processing.Filer; 20 | import javax.annotation.processing.Messager; 21 | import javax.annotation.processing.ProcessingEnvironment; 22 | import javax.annotation.processing.RoundEnvironment; 23 | import javax.lang.model.SourceVersion; 24 | import javax.lang.model.element.Element; 25 | import javax.lang.model.element.TypeElement; 26 | import javax.lang.model.util.Elements; 27 | import javax.lang.model.util.Types; 28 | import java.io.PrintWriter; 29 | import java.io.StringWriter; 30 | import java.util.Map; 31 | import java.util.Set; 32 | 33 | import static javax.tools.Diagnostic.Kind.ERROR; 34 | 35 | public class PocketKnifeProcessor extends AbstractProcessor { 36 | 37 | 38 | private Messager messager; 39 | private Filer filer; 40 | 41 | private BundleBindingProcessor bundleBindingProcessor; 42 | private IntentBindingProcessor intentBindingProcessor; 43 | private BuilderProcessor builderProcessor; 44 | 45 | @Override 46 | public SourceVersion getSupportedSourceVersion() { 47 | return SourceVersion.latestSupported(); 48 | } 49 | 50 | @Override 51 | public synchronized void init(ProcessingEnvironment processingEnv) { 52 | super.init(processingEnv); 53 | messager = processingEnv.getMessager(); 54 | filer = processingEnv.getFiler(); 55 | Elements elements = processingEnv.getElementUtils(); 56 | Types types = processingEnv.getTypeUtils(); 57 | bundleBindingProcessor = new BundleBindingProcessor(messager, elements, types); 58 | intentBindingProcessor = new IntentBindingProcessor(messager, elements, types); 59 | builderProcessor = new BuilderProcessor(messager, elements, types); 60 | } 61 | 62 | @Override 63 | public Set getSupportedAnnotationTypes() { 64 | return ImmutableSet.of( 65 | SaveState.class.getCanonicalName(), 66 | BindArgument.class.getCanonicalName(), 67 | BindExtra.class.getCanonicalName(), 68 | IntentBuilder.class.getCanonicalName(), 69 | BundleBuilder.class.getCanonicalName(), 70 | FragmentBuilder.class.getCanonicalName() 71 | ); 72 | } 73 | 74 | @Override 75 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 76 | // Bundle bindings 77 | Map bundleBindingMap = bundleBindingProcessor.findAndParseTargets(roundEnv); 78 | 79 | for (Map.Entry entry : bundleBindingMap.entrySet()) { 80 | TypeElement typeElement = entry.getKey(); 81 | BundleBindingAdapterGenerator generator = entry.getValue(); 82 | try { 83 | JavaFile javaFile = generator.generate(); 84 | javaFile.writeTo(filer); 85 | } catch (Exception e) { 86 | error(typeElement, "Unable to write adapter for type %s: %s", typeElement, e.getMessage()); 87 | } 88 | } 89 | 90 | // Intent bindings 91 | Map intentBindingMap = intentBindingProcessor.findAndParseTargets(roundEnv); 92 | 93 | for (Map.Entry entry : intentBindingMap.entrySet()) { 94 | TypeElement typeElement = entry.getKey(); 95 | IntentBindingAdapterGenerator generator = entry.getValue(); 96 | try { 97 | JavaFile javaFile = generator.generate(); 98 | javaFile.writeTo(filer); 99 | } catch (Exception e) { 100 | error(typeElement, "Unable to write adapter for type %s: %s", typeElement, e.getMessage()); 101 | } 102 | } 103 | 104 | // Builders 105 | Map builderMap = builderProcessor.findAndParseTargets(roundEnv); 106 | 107 | for (Map.Entry entry : builderMap.entrySet()) { 108 | TypeElement typeElement = entry.getKey(); 109 | BuilderGenerator generator = entry.getValue(); 110 | try { 111 | JavaFile javaFile = generator.generate(); 112 | javaFile.writeTo(filer); 113 | } catch (Exception e) { 114 | StringWriter stackTrace = new StringWriter(); 115 | e.printStackTrace(new PrintWriter(stackTrace)); 116 | error(typeElement, "Unable to write builder for type %s: %s", typeElement, stackTrace.toString()); 117 | } 118 | } 119 | 120 | return false; 121 | } 122 | 123 | private void error(Element element, String message, Object... args) { 124 | if (args.length > 0) { 125 | message = String.format(message, args); 126 | } 127 | messager.printMessage(ERROR, message, element); 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/binding/IntentBindingProcessor.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.binding; 2 | 3 | import pocketknife.BindExtra; 4 | import pocketknife.IntentSerializer; 5 | import pocketknife.NotRequired; 6 | import pocketknife.PocketKnifeIntentSerializer; 7 | import pocketknife.internal.codegen.Access; 8 | import pocketknife.internal.codegen.IntentFieldBinding; 9 | import pocketknife.internal.codegen.InvalidTypeException; 10 | import pocketknife.internal.codegen.KeySpec; 11 | 12 | import javax.annotation.processing.Messager; 13 | import javax.annotation.processing.RoundEnvironment; 14 | import javax.lang.model.element.Element; 15 | import javax.lang.model.element.TypeElement; 16 | import javax.lang.model.type.TypeMirror; 17 | import javax.lang.model.type.TypeVariable; 18 | import javax.lang.model.util.Elements; 19 | import javax.lang.model.util.Types; 20 | import java.io.PrintWriter; 21 | import java.io.StringWriter; 22 | import java.util.LinkedHashMap; 23 | import java.util.LinkedHashSet; 24 | import java.util.Map; 25 | import java.util.Set; 26 | 27 | import static pocketknife.internal.GeneratedAdapters.INTENT_ADAPTER_SUFFIX; 28 | 29 | public class IntentBindingProcessor extends BindingProcessor { 30 | 31 | public IntentBindingProcessor(Messager messager, Elements elements, Types types) { 32 | super(messager, elements, types); 33 | } 34 | 35 | public Map findAndParseTargets(RoundEnvironment env) { 36 | Map targetClassMap = new LinkedHashMap(); 37 | Set erasedTargetNames = new LinkedHashSet(); 38 | 39 | // Process each @BindExtra 40 | for (Element element : env.getElementsAnnotatedWith(BindExtra.class)) { 41 | BindExtra annotation = element.getAnnotation(BindExtra.class); 42 | if (annotation == null) { 43 | continue; 44 | } 45 | try { 46 | parseBindExtra(element, targetClassMap, erasedTargetNames); 47 | } catch (Exception e) { 48 | StringWriter stackTrace = new StringWriter(); 49 | e.printStackTrace(new PrintWriter(stackTrace)); 50 | error(element, "Unable to generate intent adapter for @BindExtra.\n\n%s", stackTrace); 51 | } 52 | } 53 | 54 | // Try to find a parent adapter for each adapter 55 | for (Map.Entry entry : targetClassMap.entrySet()) { 56 | TypeElement parent = findParent(entry.getKey(), erasedTargetNames); 57 | if (parent != null) { 58 | entry.getValue().setParentAdapter(getPackageName(parent), parent.getSimpleName() + INTENT_ADAPTER_SUFFIX); 59 | } 60 | } 61 | 62 | return targetClassMap; 63 | } 64 | 65 | private void parseBindExtra(Element element, Map targetClassMap, Set erasedTargetNames) 66 | throws InvalidTypeException { 67 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 68 | 69 | // Verify that the target has all the appropriate information for type 70 | TypeMirror type = element.asType(); 71 | if (type instanceof TypeVariable) { 72 | TypeVariable typeVariable = (TypeVariable) type; 73 | type = typeVariable.getUpperBound(); 74 | } 75 | 76 | TypeMirror intentSerializer = getAnnotationElementClass(element, IntentSerializer.class); 77 | validateSerializer(element, IntentSerializer.class, intentSerializer, PocketKnifeIntentSerializer.class); 78 | 79 | validateNotRequiredArguments(element); 80 | validateBindingPackage(BindExtra.class, element); 81 | validateForCodeGeneration(BindExtra.class, element); 82 | Access access = getAccess(BindExtra.class, element, enclosingElement); 83 | 84 | // Assemble information on the bind point 85 | String name = element.getSimpleName().toString(); 86 | String intentType = null; 87 | KeySpec key = getKey(element); 88 | boolean required = element.getAnnotation(NotRequired.class) == null; 89 | boolean hasDefault = false; 90 | boolean needsToBeCast = false; 91 | if (intentSerializer == null) { 92 | intentType = typeUtil.getIntentType(type); 93 | hasDefault = typeUtil.isPrimitive(type); 94 | needsToBeCast = typeUtil.needToCastIntentType(type); 95 | } 96 | 97 | IntentBindingAdapterGenerator intentBindingAdapterGenerator = getOrCreateTargetClass(targetClassMap, enclosingElement); 98 | IntentFieldBinding binding = new IntentFieldBinding(name, access, type, intentType, key, needsToBeCast, hasDefault, required, intentSerializer); 99 | intentBindingAdapterGenerator.addField(binding); 100 | 101 | // Add the type-erased version to the valid targets set. 102 | erasedTargetNames.add(enclosingElement.toString()); 103 | } 104 | 105 | private KeySpec getKey(Element element) { 106 | if (isDefaultAnnotationElement(element, BindExtra.class.getName(), "value")) { 107 | return new KeySpec(null, generateKey(IntentFieldBinding.KEY_PREFIX, element.getSimpleName().toString())); 108 | } 109 | return new KeySpec(null, element.getAnnotation(BindExtra.class).value()); 110 | } 111 | 112 | private IntentBindingAdapterGenerator getOrCreateTargetClass(Map targetClassMap, 113 | TypeElement enclosingElement) { 114 | IntentBindingAdapterGenerator intentBindingAdapterGenerator = targetClassMap.get(enclosingElement); 115 | if (intentBindingAdapterGenerator == null) { 116 | TypeMirror targetType = enclosingElement.asType(); 117 | String classPackage = getPackageName(enclosingElement); 118 | String className = getClassName(enclosingElement, classPackage) + INTENT_ADAPTER_SUFFIX; 119 | 120 | intentBindingAdapterGenerator = new IntentBindingAdapterGenerator(classPackage, className, targetType, typeUtil); 121 | targetClassMap.put(enclosingElement, intentBindingAdapterGenerator); 122 | } 123 | return intentBindingAdapterGenerator; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/binding/IntentBindingAdapterGenerator.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.binding; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.JavaFile; 5 | import com.squareup.javapoet.MethodSpec; 6 | import com.squareup.javapoet.ParameterSpec; 7 | import com.squareup.javapoet.ParameterizedTypeName; 8 | import com.squareup.javapoet.TypeSpec; 9 | import com.squareup.javapoet.TypeVariableName; 10 | import pocketknife.internal.IntentBinding; 11 | import pocketknife.internal.codegen.Access; 12 | import pocketknife.internal.codegen.BaseGenerator; 13 | import pocketknife.internal.codegen.IntentFieldBinding; 14 | import pocketknife.internal.codegen.TypeUtil; 15 | 16 | import javax.lang.model.type.TypeMirror; 17 | import java.io.IOException; 18 | import java.util.ArrayList; 19 | import java.util.LinkedHashSet; 20 | import java.util.List; 21 | import java.util.Set; 22 | 23 | import static javax.lang.model.element.Modifier.PUBLIC; 24 | 25 | public class IntentBindingAdapterGenerator extends BaseGenerator { 26 | 27 | public static final String BIND_EXTRAS_METHOD = "bindExtras"; 28 | 29 | private static final String TARGET = "target"; 30 | private static final String INTENT = "intent"; 31 | 32 | private final Set fields = new LinkedHashSet(); 33 | private final String classPackage; 34 | private final String className; 35 | private final TypeMirror targetType; 36 | private ClassName parentAdapter; 37 | 38 | 39 | public IntentBindingAdapterGenerator(String classPackage, String className, TypeMirror targetType, TypeUtil typeUtil) { 40 | super(typeUtil); 41 | this.classPackage = classPackage; 42 | this.className = className; 43 | this.targetType = targetType; 44 | } 45 | 46 | public void addField(IntentFieldBinding binding) { 47 | fields.add(binding); 48 | } 49 | 50 | public JavaFile generate() throws IOException { 51 | TypeVariableName t = TypeVariableName.get("T"); 52 | TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className) 53 | .addTypeVariable(TypeVariableName.get(t.name, ClassName.get(targetType))) 54 | .addModifiers(PUBLIC) 55 | .addJavadoc(getGeneratedComment(IntentBindingAdapterGenerator.class)); 56 | 57 | // Add Interface or Parent Class 58 | if (parentAdapter != null) { 59 | classBuilder.superclass(ParameterizedTypeName.get(parentAdapter, t)); 60 | } else { 61 | classBuilder.addSuperinterface(ParameterizedTypeName.get(ClassName.get(IntentBinding.class), t)); 62 | } 63 | 64 | MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(BIND_EXTRAS_METHOD) 65 | .addModifiers(PUBLIC) 66 | .addParameter(ParameterSpec.builder(t, TARGET).build()) 67 | .addParameter(ParameterSpec.builder(ClassName.get(typeUtil.intentType), INTENT).build()); 68 | if (parentAdapter != null) { 69 | methodBuilder.addStatement("super.$L($N, $N)", BIND_EXTRAS_METHOD, TARGET, INTENT); 70 | } 71 | methodBuilder.beginControlFlow("if ($N == null)", INTENT); 72 | methodBuilder.addStatement("throw new $T($S)", IllegalStateException.class, "intent is null"); 73 | methodBuilder.endControlFlow(); 74 | 75 | for (IntentFieldBinding field : fields) { 76 | addBindExtraField(methodBuilder, field); 77 | } 78 | 79 | classBuilder.addMethod(methodBuilder.build()); 80 | 81 | return JavaFile.builder(classPackage, classBuilder.build()).build(); 82 | } 83 | 84 | private void addBindExtraField(MethodSpec.Builder methodBuilder, IntentFieldBinding field) { 85 | Access access = field.getAccess(); 86 | boolean method = access.getType() == Access.Type.METHOD; 87 | if (field.getIntentSerializer() == null) { 88 | methodBuilder.beginControlFlow("if ($N.hasExtra($S))", INTENT, field.getKey().getValue()); 89 | List stmtArgs = new ArrayList(); 90 | String stmt; 91 | if (method) { 92 | stmt = "$N.$N("; 93 | } else { 94 | stmt = "$N.$N = "; 95 | } 96 | stmtArgs.add(TARGET); 97 | stmtArgs.add(access.getSetter()); 98 | 99 | if (field.needsToBeCast()) { 100 | stmt = stmt.concat("($T)"); 101 | stmtArgs.add(field.getType()); 102 | } 103 | stmt = stmt.concat("$N.get$LExtra($S"); 104 | stmtArgs.add(INTENT); 105 | stmtArgs.add(field.getIntentType()); 106 | stmtArgs.add(field.getKey().getValue()); 107 | 108 | if (field.hasDefault()) { 109 | if (method) { 110 | stmt = stmt.concat(", $N.$N()"); 111 | } else { 112 | stmt = stmt.concat(", $N.$N"); 113 | } 114 | stmtArgs.add(TARGET); 115 | stmtArgs.add(access.getGetter()); 116 | } 117 | stmt = stmt.concat(")"); 118 | if (method) { 119 | stmt = stmt.concat(")"); 120 | } 121 | methodBuilder.addStatement(stmt, stmtArgs.toArray(new Object[stmtArgs.size()])); 122 | if (field.isRequired()) { 123 | methodBuilder.nextControlFlow("else") 124 | .addStatement("throw new $T($S)", IllegalStateException.class, String.format("Required Extra with key '%s' was not found for '%s'." 125 | + "If this is not required add '@NotRequired' annotation.", field.getKey().getValue(), field.getName())); 126 | } 127 | 128 | methodBuilder.endControlFlow(); 129 | } else { 130 | String stmt; 131 | if (method) { 132 | stmt = "$N.$N(new $T().get($N, $N.$N(), $S))"; 133 | } else { 134 | stmt = "$N.$N = new $T().get($N, $N.$N, $S)"; 135 | } 136 | methodBuilder.addStatement(stmt, TARGET, access.getSetter(), field.getIntentSerializer(), INTENT, TARGET, 137 | access.getGetter(), field.getKey().getValue()); 138 | } 139 | } 140 | 141 | public void setParentAdapter(String packageName, String className) { 142 | this.parentAdapter = ClassName.get(packageName, className); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/SimpleActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.FragmentActivity; 6 | import pocketknife.BindExtra; 7 | import pocketknife.NotRequired; 8 | import pocketknife.PocketKnife; 9 | 10 | import java.io.Serializable; 11 | import java.util.ArrayList; 12 | 13 | public class SimpleActivity extends FragmentActivity { 14 | 15 | public static final String BOOLEAN_EXTRA = "BOOLEAN_EXTRA"; 16 | public static final String BOOLEAN_ARRAY_EXTRA = "BOOLEAN_ARRAY_EXTRA"; 17 | public static final String BUNDLE_EXTRA = "BUNDLE_EXTRA"; 18 | public static final String BYTE_EXTRA = "BYTE_EXTRA"; 19 | public static final String BYTE_ARRAY_EXTRA = "BYTE_ARRAY_EXTRA"; 20 | public static final String CHAR_EXTRA = "CHAR_EXTRA"; 21 | public static final String CHAR_ARRAY_EXTRA = "CHAR_ARRAY_EXTRA"; 22 | public static final String CHAR_SEQUENCE_EXTRA = "CHAR_SEQUENCE_EXTRA"; 23 | public static final String CHAR_SEQUENCE_ARRAY_EXTRA = "CHAR_SEQUENCE_ARRAY_EXTRA"; 24 | public static final String CHAR_SEQUENCE_ARRAY_LIST_EXTRA = "CHAR_SEQUENCE_ARRAY_LIST_EXTRA"; 25 | public static final String DOUBLE_EXTRA = "DOUBLE_EXTRA"; 26 | public static final String DOUBLE_ARRAY_EXTRA = "DOUBLE_ARRAY_EXTRA"; 27 | public static final String FLOAT_EXTRA = "FLOAT_EXTRA"; 28 | public static final String FLOAT_ARRAY_EXTRA = "FLOAT_ARRAY_EXTRA"; 29 | public static final String INT_EXTRA = "INT_EXTRA"; 30 | public static final String INT_ARRAY_EXTRA = "INT_ARRAY_EXTRA"; 31 | public static final String INTEGER_ARRAY_LIST_EXTRA = "INTEGER_ARRAY_LIST_EXTRA"; 32 | public static final String LONG_EXTRA = "LONG_EXTRA"; 33 | public static final String LONG_ARRAY_EXTRA = "LONG_ARRAY_EXTRA"; 34 | public static final String PARCELABLE_EXTRA = "PARCELABLE_EXTRA"; 35 | public static final String PARCELABLE_ARRAY_EXTRA = "PARCELABLE_ARRAY_EXTRA"; 36 | public static final String PARCELABLE_ARRAY_LIST_EXTRA = "PARCELABLE_ARRAY_LIST_EXTRA"; 37 | public static final String SERIALIZABLE_EXTRA = "SERIALIZABLE_EXTRA"; 38 | public static final String SHORT_EXTRA = "SHORT_EXTRA"; 39 | public static final String SHORT_ARRAY_EXTRA = "SHORT_ARRAY_EXTRA"; 40 | public static final String STRING_EXTRA = "STRING_EXTRA"; 41 | public static final String STRING_ARRAY_EXTRA = "STRING_ARRAY_EXTRA"; 42 | public static final String STRING_ARRAY_LIST_EXTRA = "STRING_ARRAY_LIST_EXTRA"; 43 | public static final String NOT_REQUIRED_INT_EXTRA = "NOT_REQUIRED_INT_EXTRA"; 44 | public static final int NRI_DEFAULT = 101; 45 | 46 | @NotRequired 47 | @BindExtra("Key") 48 | String s; 49 | // boolean dv 50 | @BindExtra(BOOLEAN_EXTRA) 51 | boolean aBoolean; 52 | // boolean[] 53 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required boolean[] 54 | @BindExtra(BOOLEAN_ARRAY_EXTRA) 55 | boolean[] booleans; 56 | // Bundle 57 | @BindExtra(BUNDLE_EXTRA) 58 | Bundle bundle; 59 | // byte dv 60 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required byte 61 | @BindExtra(BYTE_EXTRA) 62 | byte aByte; 63 | // byte[] 64 | @BindExtra(BYTE_ARRAY_EXTRA) 65 | byte[] bytes; 66 | // char dv 67 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required char 68 | @BindExtra(CHAR_EXTRA) 69 | char aChar; 70 | // char[] 71 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required char[] 72 | @BindExtra(CHAR_ARRAY_EXTRA) 73 | char[] chars; 74 | // CharSequence 75 | @BindExtra(CHAR_SEQUENCE_EXTRA) 76 | CharSequence charSequence; 77 | // CharSequence[] 78 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required CharSequence[] 79 | @BindExtra(CHAR_SEQUENCE_ARRAY_EXTRA) 80 | CharSequence[] charSequences; 81 | // ArrayList 82 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required CharSequenceArrayList 83 | @BindExtra(CHAR_SEQUENCE_ARRAY_LIST_EXTRA) 84 | ArrayList charSequenceArrayList; 85 | // double dv 86 | @BindExtra(DOUBLE_EXTRA) 87 | double aDouble; 88 | // double[] 89 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required double[] 90 | @BindExtra(DOUBLE_ARRAY_EXTRA) 91 | double[] doubles; 92 | // float dv 93 | @BindExtra(FLOAT_EXTRA) 94 | float aFloat; 95 | // float[] 96 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required float[] 97 | @BindExtra(FLOAT_ARRAY_EXTRA) 98 | float[] floats; 99 | // int dv 100 | @BindExtra(INT_EXTRA) 101 | int anInt; 102 | // int[] 103 | @BindExtra(INT_ARRAY_EXTRA) 104 | int[] ints; 105 | // ArrayList 106 | @BindExtra(INTEGER_ARRAY_LIST_EXTRA) 107 | ArrayList integerArrayList; 108 | // long dv 109 | @BindExtra(LONG_EXTRA) 110 | long aLong; 111 | // long[] 112 | @BindExtra(LONG_ARRAY_EXTRA) 113 | long[] longs; 114 | // Parcelable 115 | @BindExtra(PARCELABLE_EXTRA) 116 | MyParcelable parcelable; 117 | // Parcelable[] 118 | @BindExtra(PARCELABLE_ARRAY_EXTRA) 119 | MyParcelable[] parcelables; 120 | // ArrayList 121 | @BindExtra(PARCELABLE_ARRAY_LIST_EXTRA) 122 | ArrayList parcelableArrayList; 123 | // Serializable 124 | @BindExtra(SERIALIZABLE_EXTRA) 125 | Serializable serializable; 126 | // short dv 127 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required Short 128 | @BindExtra(SHORT_EXTRA) 129 | short aShort; 130 | // short[] 131 | @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required Short[] 132 | @BindExtra(SHORT_ARRAY_EXTRA) 133 | short[] shorts; 134 | // String 135 | @BindExtra(STRING_EXTRA) 136 | String string; 137 | // String[] 138 | @BindExtra(STRING_ARRAY_EXTRA) 139 | String[] strings; 140 | // ArrayList 141 | @BindExtra(STRING_ARRAY_LIST_EXTRA) 142 | ArrayList stringArrayList; 143 | // Not required 144 | @NotRequired 145 | @BindExtra(NOT_REQUIRED_INT_EXTRA) 146 | int notRequired = NRI_DEFAULT; 147 | @BindExtra(Intent.EXTRA_TEXT) 148 | String text; 149 | 150 | @Override 151 | protected void onCreate(Bundle savedInstanceState) { 152 | super.onCreate(savedInstanceState); 153 | setContentView(R.layout.simple_activity); 154 | 155 | PocketKnife.bindExtras(this); 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /pocketknife-sample/src/main/java/com/example/pocketknife/SimpleFragment.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v4.app.Fragment; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import pocketknife.BindArgument; 10 | import pocketknife.NotRequired; 11 | import pocketknife.PocketKnife; 12 | 13 | import java.io.Serializable; 14 | import java.util.ArrayList; 15 | 16 | public class SimpleFragment extends Fragment { 17 | 18 | public static final String BOOLEAN = "BOOLEAN"; 19 | public static final String BOOLEAN_ARRAY = "BOOLEAN_ARRAY"; 20 | public static final String BUNDLE = "BUNDLE"; 21 | public static final String BYTE = "BYTE"; 22 | public static final String BYTE_ARRAY = "BYTE_ARRAY"; 23 | public static final String CHAR = "CHAR"; 24 | public static final String CHAR_ARRAY = "CHAR_ARRAY"; 25 | public static final String CHAR_SEQUENCE = "CHAR_SEQUENCE"; 26 | public static final String CHAR_SEQUENCE_ARRAY = "CHAR_SEQUENCE_ARRAY"; 27 | public static final String CHAR_SEQUENCE_ARRAY_LIST = "CHAR_SEQUENCE_ARRAY_LIST"; 28 | public static final String DOUBLE = "DOUBLE"; 29 | public static final String DOUBLE_ARRAY = "DOUBLE_ARRAY"; 30 | public static final String FLOAT = "FLOAT"; 31 | public static final String FLOAT_ARRAY = "FLOAT_ARRAY"; 32 | public static final String INT = "INT"; 33 | public static final String INT_ARRAY = "INT_ARRAY"; 34 | public static final String INTEGER_ARRAY_LIST = "INTEGER_ARRAY_LIST"; 35 | public static final String LONG = "LONG"; 36 | public static final String LONG_ARRAY = "LONG_ARRAY"; 37 | public static final String PARCELABLE = "PARCELABLE"; 38 | public static final String PARCELABLE_ARRAY = "PARCELABLE_ARRAY"; 39 | public static final String PARCELABLE_ARRAY_LIST = "PARCELABLE_ARRAY_LIST"; 40 | public static final String SERIALIZABLE = "SERIALIZABLE"; 41 | public static final String SHORT = "SHORT"; 42 | public static final String SHORT_ARRAY = "SHORT_ARRAY"; 43 | public static final String STRING = "STRING"; 44 | public static final String STRING_ARRAY = "STRING_ARRAY"; 45 | public static final String STRING_ARRAY_LIST = "STRING_ARRAY_LIST"; 46 | public static final String NOT_REQUIRED_INT = "NOT_REQUIRED_INT"; 47 | public static final int NRI_DEFAULT = 101; 48 | 49 | @NotRequired 50 | @BindArgument("Key") 51 | String s; 52 | // boolean dv 53 | @BindArgument(BOOLEAN) 54 | boolean aBoolean; 55 | // boolean[] 56 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required boolean[] 57 | @BindArgument(BOOLEAN_ARRAY) 58 | boolean[] booleans; 59 | // Bundle 60 | @BindArgument(BUNDLE) 61 | Bundle bundle; 62 | // byte dv 63 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required byte 64 | @BindArgument(BYTE) 65 | byte aByte; 66 | // byte[] 67 | @BindArgument(BYTE_ARRAY) 68 | byte[] bytes; 69 | // char dv 70 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required char 71 | @BindArgument(CHAR) 72 | char aChar; 73 | // char[] 74 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required char[] 75 | @BindArgument(CHAR_ARRAY) 76 | char[] chars; 77 | // CharSequence 78 | @BindArgument(CHAR_SEQUENCE) 79 | CharSequence charSequence; 80 | // CharSequence[] 81 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required CharSequence[] 82 | @BindArgument(CHAR_SEQUENCE_ARRAY) 83 | CharSequence[] charSequences; 84 | // ArrayList 85 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required CharSequenceArrayList 86 | @BindArgument(CHAR_SEQUENCE_ARRAY_LIST) 87 | ArrayList charSequenceArrayList; 88 | // double dv 89 | @BindArgument(DOUBLE) 90 | double aDouble; 91 | // double[] 92 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required double[] 93 | @BindArgument(DOUBLE_ARRAY) 94 | double[] doubles; 95 | // float dv 96 | @BindArgument(FLOAT) 97 | float aFloat; 98 | // float[] 99 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required float[] 100 | @BindArgument(FLOAT_ARRAY) 101 | float[] floats; 102 | // int dv 103 | @BindArgument(INT) 104 | int anInt; 105 | // int[] 106 | @BindArgument(INT_ARRAY) 107 | int[] ints; 108 | // ArrayList 109 | @BindArgument(INTEGER_ARRAY_LIST) 110 | ArrayList integerArrayList; 111 | // long dv 112 | @BindArgument(LONG) 113 | long aLong; 114 | // long[] 115 | @BindArgument(LONG_ARRAY) 116 | long[] longs; 117 | // Parcelable 118 | @BindArgument(PARCELABLE) 119 | MyParcelable parcelable; 120 | // Parcelable[] 121 | @BindArgument(PARCELABLE_ARRAY) 122 | MyParcelable[] parcelables; 123 | // ArrayList 124 | @BindArgument(PARCELABLE_ARRAY_LIST) 125 | ArrayList parcelableArrayList; 126 | // Serializable 127 | @BindArgument(SERIALIZABLE) 128 | Serializable serializable; 129 | // short dv 130 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required Short 131 | @BindArgument(SHORT) 132 | short aShort; 133 | // short[] 134 | // @NotRequired // ROBOLECTRIC implementation of intent doesn't allow for Required Short[] 135 | @BindArgument(SHORT_ARRAY) 136 | short[] shorts; 137 | // String 138 | @BindArgument(STRING) 139 | String string; 140 | // String[] 141 | @BindArgument(STRING_ARRAY) 142 | String[] strings; 143 | // ArrayList 144 | @BindArgument(STRING_ARRAY_LIST) 145 | ArrayList stringArrayList; 146 | // Not required 147 | @NotRequired 148 | @BindArgument(NOT_REQUIRED_INT) 149 | int notRequired = NRI_DEFAULT; 150 | @BindArgument(Intent.EXTRA_TEXT) 151 | String text; 152 | 153 | 154 | public static SimpleFragment newInstance() { 155 | return new SimpleFragment(); 156 | } 157 | 158 | @Override 159 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 160 | return inflater.inflate(R.layout.simple_fragment, container, false); 161 | } 162 | 163 | @Override 164 | public void onActivityCreated(Bundle savedInstanceState) { 165 | super.onActivityCreated(savedInstanceState); 166 | PocketKnife.bindArguments(this, getArguments()); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/SaveStateActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.robolectric.Robolectric; 7 | import org.robolectric.RobolectricTestRunner; 8 | import org.robolectric.annotation.Config; 9 | import org.robolectric.util.ActivityController; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Random; 13 | import java.util.UUID; 14 | 15 | import static org.junit.Assert.assertArrayEquals; 16 | import static org.junit.Assert.assertEquals; 17 | 18 | @RunWith(RobolectricTestRunner.class) 19 | @Config(manifest = "src/main/AndroidManifest.xml") 20 | public class SaveStateActivityTest extends BaseTest { 21 | 22 | @Test 23 | public void verifySaveRestoreState() { 24 | Random random = new Random(42); 25 | 26 | ActivityController initialController = Robolectric.buildActivity(SaveStateActivity.class).create(); 27 | SaveStateActivity original = initialController.start().restart().visible().get(); 28 | 29 | original.aBoolean = random.nextBoolean(); 30 | original.booleans = new boolean[]{random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean()}; 31 | 32 | Bundle bundle = new Bundle(); 33 | bundle.putInt(BUNDLE_INT_ARG, random.nextInt()); 34 | bundle.putSerializable(BUNDLE_SERIALIZABLE_ARG, UUID.randomUUID().toString()); 35 | 36 | original.aByte = (byte)random.nextInt(); 37 | original.bytes = new byte[random.nextInt(10)]; 38 | random.nextBytes(original.bytes); 39 | 40 | original.aChar = (char)random.nextInt(); 41 | original.chars = new char[]{(char)random.nextInt(), (char)random.nextInt()}; 42 | 43 | 44 | original.charSequence = UUID.randomUUID().toString(); 45 | 46 | original.charSequences = new CharSequence[]{UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString()}; 47 | 48 | original.charSequenceArrayList = new ArrayList(); 49 | original.charSequenceArrayList.add(UUID.randomUUID().toString()); 50 | 51 | original.aDouble = random.nextDouble(); 52 | original.doubles = new double[]{random.nextDouble()}; 53 | 54 | original.aFloat = random.nextFloat(); 55 | original.floats = new float[]{random.nextFloat()}; 56 | 57 | original.anInt = random.nextInt(); 58 | original.ints = new int[]{random.nextInt(), random.nextInt(), random.nextInt(), random.nextInt()}; 59 | 60 | original.integerArrayList = new ArrayList(); 61 | original.integerArrayList.add(random.nextInt()); 62 | 63 | original.aLong = random.nextLong(); 64 | original.longs = new long[]{random.nextLong(), random.nextLong(), random.nextLong(), random.nextLong()}; 65 | 66 | 67 | original.parcelable = new MyParcelable(random.nextInt()); 68 | original.parcelables = new MyParcelable[]{new MyParcelable(random.nextInt()), new MyParcelable(random.nextInt()), new MyParcelable(random.nextInt())}; 69 | original.parcelableArrayList = new ArrayList(); 70 | original.parcelableArrayList.add(new MyParcelable(random.nextInt())); 71 | 72 | 73 | original.serializable = UUID.randomUUID().toString(); 74 | 75 | original.aShort = (short)random.nextInt(); 76 | original.shorts = new short[]{(short)random.nextInt(), (short)random.nextInt(), (short)random.nextInt(), (short)random.nextInt()}; 77 | 78 | original.string = UUID.randomUUID().toString(); 79 | original.strings = new String[]{UUID.randomUUID().toString()}; 80 | original.stringArrayList = new ArrayList(); 81 | original.stringArrayList.add(UUID.randomUUID().toString()); 82 | 83 | Bundle saveState = new Bundle(); 84 | initialController.saveInstanceState(saveState); 85 | ActivityController secondaryController = Robolectric.buildActivity(SaveStateActivity.class).create(saveState); 86 | SaveStateActivity activity = secondaryController.start().restart().visible().get(); 87 | 88 | // Boolean 89 | assertEquals(original.aBoolean, activity.aBoolean); 90 | // Array 91 | assertBooleanArrayEquals(original.booleans, activity.booleans); 92 | 93 | // Bundle 94 | assertBundleEquals(original.bundle, activity.bundle); 95 | 96 | // Byte 97 | assertEquals(original.aByte, activity.aByte); 98 | // Array 99 | assertArrayEquals(original.bytes, activity.bytes); 100 | 101 | // Char 102 | assertEquals(original.aChar, activity.aChar); 103 | // Array 104 | assertArrayEquals(original.chars, activity.chars); 105 | 106 | // CharSequence 107 | assertEquals(original.charSequence, activity.charSequence); 108 | // Array 109 | assertArrayEquals(original.charSequences, activity.charSequences); 110 | // ArrayList 111 | assertArrayListEquals(original.charSequenceArrayList, activity.charSequenceArrayList); 112 | 113 | // Double 114 | assertEquals(original.aDouble, activity.aDouble, 0); 115 | // Array 116 | assertArrayEquals(original.doubles, activity.doubles, 0); 117 | 118 | // Float 119 | assertEquals(original.aFloat, activity.aFloat, 0); 120 | // Array 121 | assertArrayEquals(original.floats, activity.floats, 0); 122 | 123 | // Int 124 | assertEquals(original.anInt, activity.anInt); 125 | // Array 126 | assertArrayEquals(original.ints, activity.ints); 127 | // ArrayList 128 | assertArrayListEquals(original.integerArrayList, activity.integerArrayList); 129 | 130 | // Long 131 | assertEquals(original.aLong, activity.aLong); 132 | // Array 133 | assertArrayEquals(original.longs, activity.longs); 134 | 135 | //Parcelable 136 | assertEquals(original.parcelable, activity.parcelable); 137 | // Array 138 | assertArrayEquals(original.parcelables, activity.parcelables); 139 | // ArrayList 140 | assertArrayListEquals(original.parcelableArrayList, activity.parcelableArrayList); 141 | 142 | // Serializable 143 | String s1 = (String)original.serializable; 144 | String s2 = (String)activity.serializable; 145 | assertEquals(s1, s2); 146 | 147 | // Shorts 148 | assertEquals(original.aShort, activity.aShort); 149 | // Array 150 | assertArrayEquals(original.shorts, activity.shorts); 151 | 152 | // String 153 | assertEquals(original.string, activity.string); 154 | // Array 155 | assertArrayEquals(original.strings, activity.strings); 156 | // ArrayList 157 | assertArrayListEquals(original.stringArrayList, activity.stringArrayList); 158 | 159 | } 160 | 161 | @Test (expected = IllegalStateException.class) 162 | public void verifySaveRestoreExceptionThrown() { 163 | Robolectric.buildActivity(SaveStateActivity.class).create(new Bundle()); 164 | } 165 | 166 | @Test 167 | public void verifyNotRequiredSaveRestoreState() { 168 | Robolectric.buildActivity(NotRequiredSaveStateActivity.class).create(new Bundle()).get(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/BundleBuilderTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.os.Bundle; 4 | import org.junit.After; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.annotation.Config; 10 | 11 | import java.io.Serializable; 12 | import java.util.ArrayList; 13 | import java.util.Random; 14 | import java.util.UUID; 15 | 16 | import static org.junit.Assert.assertArrayEquals; 17 | import static org.junit.Assert.assertEquals; 18 | 19 | @RunWith(RobolectricTestRunner.class) 20 | @Config(manifest = "src/main/AndroidManifest.xml") 21 | public class BundleBuilderTest extends BaseTest { 22 | 23 | private Bundles bundles; 24 | 25 | @Before 26 | public void setup() { 27 | bundles = new PocketKnifeBundles(); 28 | } 29 | 30 | @After 31 | public void tearDown() { 32 | bundles = null; 33 | } 34 | 35 | @Test 36 | public void testExtras() throws Exception { 37 | Random random = new Random(42); 38 | int i = random.nextInt(); 39 | int j = random.nextInt(); 40 | Bundle bundle = bundles.getSingleArgBundle(i); 41 | assertEquals("EXTRA", i, bundle.getInt(PocketKnifeBundles.ARG_I, 0)); 42 | bundle = bundles.getEmptyBundle(); 43 | assertEquals(0, bundle.size()); 44 | 45 | bundle = bundles.getTwinArgsBundle(i, j); 46 | assertEquals("SAME TYPE I", i, bundle.getInt(PocketKnifeBundles.ARG_I, 0)); 47 | assertEquals("SAME TYPE J", j, bundle.getInt(PocketKnifeBundles.ARG_J, 0)); 48 | 49 | bundle = bundles.getNameCollisionBundle(0, 1, 2); 50 | assertEquals("NAME COLLISION", 0, bundle.getInt(PocketKnifeBundles.ARG_BUNDLE, 0)); 51 | assertEquals("NAME COLLISION", 1, bundle.getInt(PocketKnifeBundles.ARG_BUNDLE1, 0)); 52 | assertEquals("NAME COLLISION", 2, bundle.getInt(PocketKnifeBundles.ARG_BUNDLE2, 0)); 53 | 54 | boolean aBoolean = random.nextBoolean(); 55 | boolean[] booleans = {random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean()}; 56 | Bundle bundle1 = new Bundle(); 57 | bundle1.putInt(BUNDLE_INT_ARG, random.nextInt()); 58 | bundle1.putSerializable(BUNDLE_SERIALIZABLE_ARG, UUID.randomUUID().toString()); 59 | byte aByte = (byte) random.nextInt(); 60 | byte[] bytes = new byte[random.nextInt(10)]; 61 | random.nextBytes(bytes); 62 | char aChar = (char) random.nextInt(); 63 | char[] chars = {(char) random.nextInt(), (char) random.nextInt()}; 64 | CharSequence charSequence = UUID.randomUUID().toString(); 65 | CharSequence[] charSequences = {UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString()}; 66 | ArrayList charSequenceArrayList = new ArrayList(); 67 | charSequenceArrayList.add(UUID.randomUUID().toString()); 68 | double aDouble = random.nextDouble(); 69 | double[] doubles = {random.nextDouble()}; 70 | float aFloat = random.nextFloat(); 71 | float[] floats = {random.nextFloat()}; 72 | int anInt = random.nextInt(); 73 | int[] ints = {random.nextInt(), random.nextInt(), random.nextInt(), random.nextInt()}; 74 | ArrayList integerArrayList = new ArrayList(); 75 | integerArrayList.add(random.nextInt()); 76 | long aLong = random.nextLong(); 77 | long[] longs = {random.nextLong(), random.nextLong(), random.nextLong(), random.nextLong()}; 78 | MyParcelable parcelable = new MyParcelable(random.nextInt()); 79 | MyParcelable[] parcelables = {new MyParcelable(random.nextInt()), new MyParcelable(random.nextInt()), new MyParcelable(random.nextInt())}; 80 | ArrayList parcelableArrayList = new ArrayList(); 81 | parcelableArrayList.add(new MyParcelable(random.nextInt())); 82 | Serializable serializable = UUID.randomUUID().toString(); 83 | short aShort = (short) random.nextInt(); 84 | short[] shorts = {(short) random.nextInt(), (short) random.nextInt(), (short) random.nextInt(), (short) random.nextInt()}; 85 | String string = UUID.randomUUID().toString(); 86 | String[] strings = {UUID.randomUUID().toString()}; 87 | ArrayList stringArrayList = new ArrayList(); 88 | stringArrayList.add(UUID.randomUUID().toString()); 89 | bundle = bundles.getBundle(aBoolean, booleans, bundle1, aByte, bytes, aChar, chars, charSequence, charSequences, charSequenceArrayList, aDouble, doubles, 90 | aFloat, floats, anInt, ints, integerArrayList, aLong, longs, parcelable, parcelables, parcelableArrayList, serializable, aShort, 91 | shorts, string, strings, stringArrayList); 92 | 93 | assertEquals(aBoolean, bundle.getBoolean(PocketKnifeBundles.ARG_A_BOOLEAN, false)); 94 | assertArrayEquals(booleans, bundle.getBooleanArray(PocketKnifeBundles.ARG_BOOLEANS)); 95 | assertBundleEquals(bundle1, bundle.getBundle(PocketKnifeBundles.ARG_BUNDLE)); 96 | assertEquals(aByte, bundle.getByte(PocketKnifeBundles.ARG_A_BYTE)); 97 | assertArrayEquals(bytes, bundle.getByteArray(PocketKnifeBundles.ARG_BYTES)); 98 | assertEquals(aChar, bundle.getChar(PocketKnifeBundles.ARG_A_CHAR, '0')); 99 | assertArrayEquals(chars, bundle.getCharArray(PocketKnifeBundles.ARG_CHARS)); 100 | assertEquals(charSequence, bundle.getCharSequence(PocketKnifeBundles.ARG_CHAR_SEQUENCE)); 101 | assertArrayEquals(charSequences, bundle.getCharSequenceArray(PocketKnifeBundles.ARG_CHAR_SEQUENCES)); 102 | assertArrayListEquals(charSequenceArrayList, bundle.getCharSequenceArrayList(PocketKnifeBundles.ARG_CHAR_SEQUENCE_ARRAY_LIST)); 103 | assertEquals(aDouble, bundle.getDouble(PocketKnifeBundles.ARG_A_DOUBLE, 0.0), 0); 104 | assertArrayEquals(doubles, bundle.getDoubleArray(PocketKnifeBundles.ARG_DOUBLES), 0); 105 | assertEquals(aFloat, bundle.getFloat(PocketKnifeBundles.ARG_A_FLOAT, 0.0f), 0); 106 | assertArrayEquals(floats, bundle.getFloatArray(PocketKnifeBundles.ARG_FLOATS), 0); 107 | assertEquals(anInt, bundle.getInt(PocketKnifeBundles.ARG_AN_INT, 0)); 108 | assertArrayEquals(ints, bundle.getIntArray(PocketKnifeBundles.ARG_INTS)); 109 | assertArrayListEquals(integerArrayList, bundle.getIntegerArrayList(PocketKnifeBundles.ARG_INTEGER_ARRAY_LIST)); 110 | assertEquals(aLong, bundle.getLong(PocketKnifeBundles.ARG_A_LONG, 0)); 111 | assertArrayEquals(longs, bundle.getLongArray(PocketKnifeBundles.ARG_LONGS)); 112 | assertEquals(parcelable, bundle.getParcelable(PocketKnifeBundles.ARG_PARCELABLE)); 113 | assertArrayEquals(parcelables, bundle.getParcelableArray(PocketKnifeBundles.ARG_PARCELABLES)); 114 | assertArrayListEquals(parcelableArrayList, bundle.getParcelableArrayList(PocketKnifeBundles.ARG_PARCELABLE_ARRAY_LIST)); 115 | assertEquals(serializable, bundle.getSerializable(PocketKnifeBundles.ARG_SERIALIZABLE)); 116 | assertEquals(aShort, bundle.getShort(PocketKnifeBundles.ARG_A_SHORT, (short) 0)); 117 | assertArrayEquals(shorts, bundle.getShortArray(PocketKnifeBundles.ARG_SHORTS)); 118 | assertEquals(string, bundle.getString(PocketKnifeBundles.ARG_STRING)); 119 | assertArrayEquals(strings, bundle.getStringArray(PocketKnifeBundles.ARG_STRINGS)); 120 | assertArrayListEquals(stringArrayList, bundle.getStringArrayList(PocketKnifeBundles.ARG_STRING_ARRAY_LIST)); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/binding/BindingProcessor.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.binding; 2 | 3 | import android.os.Build; 4 | import com.google.common.base.CaseFormat; 5 | import pocketknife.NotRequired; 6 | import pocketknife.internal.codegen.Access; 7 | import pocketknife.internal.codegen.BaseProcessor; 8 | 9 | import javax.annotation.processing.Messager; 10 | import javax.lang.model.element.Element; 11 | import javax.lang.model.element.ElementKind; 12 | import javax.lang.model.element.ExecutableElement; 13 | import javax.lang.model.element.Modifier; 14 | import javax.lang.model.element.TypeElement; 15 | import javax.lang.model.type.DeclaredType; 16 | import javax.lang.model.type.TypeKind; 17 | import javax.lang.model.type.TypeMirror; 18 | import javax.lang.model.util.Elements; 19 | import javax.lang.model.util.Types; 20 | import java.lang.annotation.Annotation; 21 | import java.util.Set; 22 | 23 | import static javax.lang.model.element.ElementKind.CLASS; 24 | import static javax.lang.model.element.ElementKind.METHOD; 25 | import static javax.lang.model.element.Modifier.PRIVATE; 26 | import static javax.lang.model.element.Modifier.PROTECTED; 27 | import static javax.lang.model.element.Modifier.STATIC; 28 | import static javax.tools.Diagnostic.Kind.ERROR; 29 | import static pocketknife.internal.GeneratedAdapters.ANDROID_PREFIX; 30 | import static pocketknife.internal.GeneratedAdapters.JAVA_PREFIX; 31 | 32 | public abstract class BindingProcessor extends BaseProcessor { 33 | 34 | protected Messager messager; 35 | 36 | private static final String IS = "is"; 37 | private static final String GET = "get"; 38 | private static final String SET = "set"; 39 | private static final String[] GETTER_PREFIXES = {IS, GET}; 40 | 41 | public BindingProcessor(Messager messager, Elements elements, Types types) { 42 | super(elements, types); 43 | this.messager = messager; 44 | 45 | } 46 | 47 | protected String getPackageName(TypeElement type) { 48 | return elements.getPackageOf(type).getQualifiedName().toString(); 49 | } 50 | 51 | protected String getClassName(TypeElement typeElement, String packageName) { 52 | int packageLen = packageName.length() + 1; 53 | return typeElement.getQualifiedName().toString().substring(packageLen).replace('.', '$'); 54 | } 55 | 56 | protected void error(Element element, String message, Object... args) { 57 | if (args.length > 0) { 58 | message = String.format(message, args); 59 | } 60 | messager.printMessage(ERROR, message, element); 61 | } 62 | 63 | protected void validateNotRequiredArguments(Element element) { 64 | NotRequired notRequired = element.getAnnotation(NotRequired.class); 65 | if (notRequired != null && notRequired.value() < Build.VERSION_CODES.FROYO) { 66 | throw new IllegalStateException("NotRequired value must be FROYO(8)+"); 67 | } 68 | } 69 | 70 | protected void validateForCodeGeneration(Class annotationClass, Element element) { 71 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 72 | 73 | // Verify method modifiers 74 | Set modifiers = element.getModifiers(); 75 | if (modifiers.contains(STATIC)) { 76 | throw new IllegalStateException(String.format("@%s fields must not be static. (%s.%s)", 77 | annotationClass.getSimpleName(), enclosingElement.getQualifiedName(), 78 | element.getSimpleName())); 79 | } 80 | 81 | // Verify Containing type. 82 | if (enclosingElement.getKind() != CLASS) { 83 | throw new IllegalStateException(String.format("@%s fields may only be contained in classes. (%s.%s)", 84 | annotationClass.getSimpleName(), enclosingElement.getQualifiedName(), 85 | element.getSimpleName())); 86 | } 87 | 88 | // Verify containing class visibility is not private 89 | if (enclosingElement.getModifiers().contains(PRIVATE)) { 90 | throw new IllegalStateException(String.format("@%s fields may not be contained in private classes (%s.%s)", annotationClass.getSimpleName(), 91 | enclosingElement.getQualifiedName(), element.getSimpleName())); 92 | } 93 | 94 | } 95 | 96 | protected Access getAccess(Class annotationClass, Element element, TypeElement enclosingElement) { 97 | Set modifiers = element.getModifiers(); 98 | if (modifiers.contains(PROTECTED) || modifiers.contains(PRIVATE)) { 99 | String getter = findGetter(element); 100 | String setter = findSetter(element); 101 | if (getter == null || setter == null) { 102 | throw new IllegalStateException(String.format("@%s fields must have a Java Bean getter and a setter if it is private or protected. (%s.%s)", 103 | annotationClass.getSimpleName(), enclosingElement.getQualifiedName(), 104 | element.getSimpleName())); 105 | } 106 | return new Access(Access.Type.METHOD, getter, setter); 107 | } 108 | String name = element.getSimpleName().toString(); 109 | return new Access(Access.Type.FIELD, name, name); 110 | 111 | } 112 | 113 | private String findGetter(Element element) { 114 | String field = element.getSimpleName().toString(); 115 | Element parent = element.getEnclosingElement(); 116 | for (Element child : parent.getEnclosedElements()) { 117 | if (child.getKind() == ElementKind.METHOD && child instanceof ExecutableElement && ((ExecutableElement) child).getParameters().isEmpty()) { 118 | String name = child.getSimpleName().toString(); 119 | for (String prefix : GETTER_PREFIXES) { 120 | if (name.startsWith(prefix) && name.substring(prefix.length()).equals(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field))) { 121 | return name; 122 | } 123 | } 124 | } 125 | } 126 | return null; 127 | } 128 | 129 | private String findSetter(Element element) { 130 | String field = element.getSimpleName().toString(); 131 | Element parent = element.getEnclosingElement(); 132 | for (Element child : parent.getEnclosedElements()) { 133 | if (child.getKind() == METHOD && child instanceof ExecutableElement && ((ExecutableElement) child).getParameters().size() == 1 && !( 134 | (ExecutableElement) child).isVarArgs()) { 135 | String name = child.getSimpleName().toString(); 136 | if (name.startsWith(SET) && name.substring(SET.length()).equals(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, field))) { 137 | return name; 138 | } 139 | } 140 | } 141 | return null; 142 | } 143 | 144 | protected void validateBindingPackage(Class annotationClass, Element element) { 145 | TypeElement enclosingElement = (TypeElement) element.getEnclosingElement(); 146 | String qualifiedName = enclosingElement.getQualifiedName().toString(); 147 | 148 | if (qualifiedName.startsWith(ANDROID_PREFIX)) { 149 | throw new IllegalStateException(String.format("@%s-annotated class incorrectly in Android framework package. (%s)", 150 | annotationClass.getSimpleName(), qualifiedName)); 151 | } 152 | if (qualifiedName.startsWith(JAVA_PREFIX)) { 153 | throw new IllegalStateException(String.format("@%s-annotated class incorrectly in Java framework package. (%s", 154 | annotationClass.getSimpleName(), qualifiedName)); 155 | } 156 | } 157 | 158 | protected TypeElement findParent(TypeElement typeElement, Set parents) { 159 | TypeMirror type; 160 | while (true) { 161 | type = typeElement.getSuperclass(); 162 | if (type.getKind() == TypeKind.NONE) { 163 | return null; 164 | } 165 | typeElement = (TypeElement) ((DeclaredType) type).asElement(); 166 | if (parents.contains(typeElement.toString())) { 167 | return typeElement; 168 | } 169 | } 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /pocketknife-compiler/src/main/java/pocketknife/internal/codegen/builder/IntentMethodBinding.java: -------------------------------------------------------------------------------- 1 | package pocketknife.internal.codegen.builder; 2 | 3 | import com.squareup.javapoet.ClassName; 4 | import com.squareup.javapoet.MethodSpec; 5 | import org.apache.commons.lang3.StringUtils; 6 | import pocketknife.internal.codegen.IntentFieldBinding; 7 | import pocketknife.internal.codegen.KeySpec; 8 | import pocketknife.internal.codegen.MethodBinding; 9 | import pocketknife.internal.codegen.TypeUtil; 10 | 11 | import javax.lang.model.type.TypeMirror; 12 | import java.util.ArrayList; 13 | import java.util.LinkedHashSet; 14 | import java.util.List; 15 | import java.util.Set; 16 | 17 | import static javax.lang.model.element.Modifier.PUBLIC; 18 | 19 | public class IntentMethodBinding extends MethodBinding { 20 | 21 | private static final String RETURN_VAR_NAME_ROOT = "intent"; 22 | public static final String IF_DOLLAR_T_VERSION_SDK_INT_DOLLAR_T_VERSION_CODES_JELLY_BEAN = "if ($T.VERSION.SDK_INT >= $T.VERSION_CODES.JELLY_BEAN)"; 23 | 24 | private final String name; 25 | private final TypeMirror className; 26 | private final String action; 27 | private final String dataParam; 28 | private final Integer flags; 29 | private final String[] categories; 30 | private final String type; 31 | private final boolean dataParamIsString; 32 | 33 | private final List fields = new ArrayList(); 34 | 35 | public IntentMethodBinding(String name, TypeMirror className, String action, String dataParam, Integer flags, String[] categories, String type, 36 | boolean dataParamIsString) { 37 | this.name = name; 38 | this.className = className; 39 | this.action = action; 40 | this.dataParam = dataParam; 41 | this.flags = flags; 42 | this.categories = categories; 43 | this.type = type; 44 | this.dataParamIsString = dataParamIsString; 45 | } 46 | 47 | public void addField(IntentFieldBinding fieldBinding) { 48 | if (fields.contains(fieldBinding)) { 49 | throw new IllegalStateException("Cannot have multiple arguments named: " + fieldBinding.getName()); 50 | } 51 | fields.add(fieldBinding); 52 | } 53 | 54 | @Override 55 | public Set getKeys() { 56 | Set keys = new LinkedHashSet(); 57 | for (IntentFieldBinding field : fields) { 58 | keys.add(field.getKey()); 59 | } 60 | return keys; 61 | } 62 | 63 | @Override 64 | public MethodSpec generateMethodSpec(TypeUtil typeUtil) { 65 | String returnVarName = getReturnVarName(RETURN_VAR_NAME_ROOT); 66 | 67 | MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(name) 68 | .addAnnotation(Override.class) 69 | .addModifiers(PUBLIC) 70 | .returns(ClassName.get(typeUtil.intentType)) 71 | .addStatement("$T $N = new $T()", ClassName.get(typeUtil.intentType), returnVarName, ClassName.get(typeUtil.intentType)); 72 | if (action != null) { 73 | methodBuilder.addStatement("$N.setAction($S)", returnVarName, action); 74 | } 75 | addDataAndOrType(methodBuilder, returnVarName, typeUtil); 76 | if (className != null) { 77 | methodBuilder.addStatement("$N.setClass(this.context, $T.class)", returnVarName, ClassName.get(className)); 78 | } 79 | if (flags != null) { 80 | methodBuilder.addStatement("$N.setFlags($L)", returnVarName, flags); 81 | } 82 | for (String category : categories) { 83 | methodBuilder.addStatement("$N.addCategory($S)", returnVarName, category); 84 | } 85 | 86 | for (IntentFieldBinding field : fields) { 87 | methodBuilder.addParameter(ClassName.get(field.getType()), field.getName()); 88 | if (StringUtils.equals(field.getName(), dataParam)) { 89 | continue; // Data is handled previously 90 | } 91 | addPutExtraStatement(methodBuilder, field, returnVarName); 92 | } 93 | 94 | methodBuilder.addStatement("return $N", returnVarName); 95 | 96 | return methodBuilder.build(); 97 | } 98 | 99 | private void addPutExtraStatement(MethodSpec.Builder methodBuilder, IntentFieldBinding field, String returnVarName) { 100 | KeySpec key = field.getKey(); 101 | if (field.getIntentSerializer() == null) { 102 | String stmt = "$N.put"; 103 | if (field.isArrayList()) { 104 | stmt = stmt.concat("$LExtra("); 105 | } else { 106 | stmt = stmt.concat("Extra("); 107 | } 108 | String keyValue; 109 | if (StringUtils.isBlank(key.getName())) { 110 | keyValue = key.getValue(); 111 | stmt = stmt.concat("$S"); 112 | } else { 113 | keyValue = key.getName(); 114 | stmt = stmt.concat("$N"); 115 | } 116 | stmt = stmt.concat(", $N)"); 117 | if (field.isArrayList()) { 118 | methodBuilder.addStatement(stmt, returnVarName, field.getIntentType(), keyValue, field.getName()); 119 | } else { 120 | methodBuilder.addStatement(stmt, returnVarName, keyValue, field.getName()); 121 | } 122 | } else { 123 | if (StringUtils.isBlank(key.getName())) { 124 | methodBuilder.addStatement("new $T().put($N, $N, $S)", field.getIntentSerializer(), returnVarName, field.getName(), key.getValue()); 125 | } else { 126 | methodBuilder.addStatement("new $T().put($N, $N, $N)", field.getIntentSerializer(), returnVarName, field.getName(), key.getName()); 127 | } 128 | } 129 | } 130 | 131 | private void addDataAndOrType(MethodSpec.Builder methodBuilder, String returnVarName, TypeUtil typeUtil) { 132 | if (dataParam != null && type != null) { 133 | methodBuilder.beginControlFlow(IF_DOLLAR_T_VERSION_SDK_INT_DOLLAR_T_VERSION_CODES_JELLY_BEAN, ClassName.get(typeUtil.buildType), 134 | ClassName.get(typeUtil.buildType)); 135 | if (dataParamIsString) { 136 | methodBuilder.addStatement("$N.setDataAndTypeAndNormalize($T.parse($N), $S)", returnVarName, ClassName.get(typeUtil.uriType), dataParam, type); 137 | } else { 138 | methodBuilder.addStatement("$N.setDataAndTypeAndNormalize($N, $S)", returnVarName, dataParam, type); 139 | } 140 | methodBuilder.nextControlFlow("else"); 141 | if (dataParamIsString) { 142 | methodBuilder.addStatement("$N.setDataAndType($T.parse($N), $S)", returnVarName, ClassName.get(typeUtil.uriType), dataParam, type); 143 | } else { 144 | methodBuilder.addStatement("$N.setDataAndType($N, $S)", returnVarName, dataParam, type); 145 | } 146 | methodBuilder.endControlFlow(); 147 | } else if (dataParam != null) { 148 | methodBuilder.beginControlFlow(IF_DOLLAR_T_VERSION_SDK_INT_DOLLAR_T_VERSION_CODES_JELLY_BEAN, ClassName.get(typeUtil.buildType), 149 | ClassName.get(typeUtil.buildType)); 150 | if (dataParamIsString) { 151 | methodBuilder.addStatement("$N.setDataAndNormalize($T.parse($N))", returnVarName, ClassName.get(typeUtil.uriType), dataParam); 152 | } else { 153 | methodBuilder.addStatement("$N.setDataAndNormalize($N)", returnVarName, dataParam); 154 | } 155 | methodBuilder.nextControlFlow("else"); 156 | if (dataParamIsString) { 157 | methodBuilder.addStatement("$N.setData($T.parse($N))", returnVarName, ClassName.get(typeUtil.uriType), dataParam); 158 | } else { 159 | methodBuilder.addStatement("$N.setData($N)", returnVarName, dataParam); 160 | } 161 | methodBuilder.endControlFlow(); 162 | } else if (type != null) { 163 | methodBuilder.beginControlFlow(IF_DOLLAR_T_VERSION_SDK_INT_DOLLAR_T_VERSION_CODES_JELLY_BEAN, ClassName.get(typeUtil.buildType), 164 | ClassName.get(typeUtil.buildType)); 165 | methodBuilder.addStatement("$N.setTypeAndNormalize($S)", returnVarName, type); 166 | methodBuilder.nextControlFlow("else"); 167 | methodBuilder.addStatement("$N.setType($S)", returnVarName, type); 168 | methodBuilder.endControlFlow(); 169 | } 170 | } 171 | 172 | @Override 173 | public List getFields() { 174 | return fields; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.sonatype.oss 8 | oss-parent 9 | 7 10 | 11 | 12 | com.vikingsen 13 | pocketknife-parent 14 | 3.2.2-SNAPSHOT 15 | pom 16 | 17 | Pocket Knife (Parent) 18 | Intent and Bundle utility for Android. 19 | http://github.com/hansenji/butterknife/ 20 | 2014 21 | 22 | 23 | pocketknife 24 | pocketknife-compiler 25 | pocketknife-sample 26 | 27 | 28 | 29 | UTF-8 30 | 31 | 1.6 32 | 4.1.1.4 33 | 16 34 | r7 35 | 1.3.0 36 | 19.0-rc1 37 | 2.0M10 38 | 4.12 39 | 3.0 40 | 0.8 41 | 3.4 42 | 43 | 44 | 45 | 46 | http://github.com/hansenji/pocketknife/ 47 | scm:git:git://github.com/hansenji/pocketknife.git 48 | scm:git:ssh://git@github.com/hansenji/pocketknife.git 49 | HEAD 50 | 51 | 52 | 53 | GitHub Issues 54 | http://github.com/hansenji/pocketknife/issues 55 | 56 | 57 | 58 | 59 | Apache 2.0 60 | http://www.apache.org/licenses/LICENSE-2.0.txt 61 | 62 | 63 | 64 | 65 | 66 | 67 | com.google.android 68 | android 69 | ${android.version} 70 | 71 | 72 | com.google.android 73 | support-v4 74 | ${support-v4.version} 75 | 76 | 77 | com.squareup 78 | javapoet 79 | ${javapoet.version} 80 | 81 | 82 | com.google.guava 83 | guava 84 | ${guava.version} 85 | 86 | 87 | junit 88 | junit 89 | ${junit.version} 90 | 91 | 92 | org.easytesting 93 | fest-assert-core 94 | ${fest.version} 95 | 96 | 97 | org.robolectric 98 | robolectric 99 | ${robolectric.version} 100 | 101 | 102 | com.google.testing.compile 103 | compile-testing 104 | ${compile-test.version} 105 | 106 | 107 | org.apache.commons 108 | commons-lang3 109 | ${commons-lang3.version} 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | org.apache.maven.plugins 118 | maven-compiler-plugin 119 | 3.0 120 | 121 | ${java.version} 122 | ${java.version} 123 | true 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-checkstyle-plugin 130 | 2.9.1 131 | 132 | true 133 | checkstyle.xml 134 | true 135 | 136 | 137 | 138 | verify 139 | 140 | checkstyle 141 | 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-release-plugin 149 | 2.4 150 | 151 | true 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | com.simpligility.maven.plugins 160 | android-maven-plugin 161 | 4.1.0 162 | 163 | 164 | ${android.platform} 165 | 166 | 167 | 168 | 169 | 170 | org.apache.maven.plugins 171 | maven-javadoc-plugin 172 | 2.9 173 | 174 | -Xdoclint:none 175 | 176 | 177 | 178 | org.jacoco 179 | jacoco-maven-plugin 180 | 0.7.2.201409121644 181 | 182 | 183 | 184 | prepare-agent 185 | 186 | 187 | 188 | report 189 | prepare-package 190 | 191 | report 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | ossrh 202 | https://oss.sonatype.org/content/repositories/snapshots 203 | 204 | 205 | ossrh 206 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /pocketknife-sample/src/test/java/com/example/pocketknife/SimpleActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.example.pocketknife; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.robolectric.Robolectric; 8 | import org.robolectric.RobolectricTestRunner; 9 | import org.robolectric.RuntimeEnvironment; 10 | import org.robolectric.annotation.Config; 11 | 12 | import java.io.Serializable; 13 | import java.util.ArrayList; 14 | import java.util.Random; 15 | import java.util.UUID; 16 | 17 | import static org.junit.Assert.assertArrayEquals; 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertNull; 20 | 21 | @RunWith(RobolectricTestRunner.class) 22 | @Config(manifest = "src/main/AndroidManifest.xml") 23 | public class SimpleActivityTest extends BaseTest { 24 | 25 | @Test 26 | public void verifyExtraBinding() { 27 | Intent intent = new Intent(RuntimeEnvironment.application, SimpleActivity.class); 28 | Random random = new Random(42); 29 | 30 | boolean aBoolean = random.nextBoolean(); 31 | intent.putExtra(SimpleActivity.BOOLEAN_EXTRA, aBoolean); 32 | boolean[] booleans = {random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean(), random.nextBoolean()}; 33 | intent.putExtra(SimpleActivity.BOOLEAN_ARRAY_EXTRA, booleans); 34 | 35 | Bundle bundle = new Bundle(); 36 | bundle.putInt(BUNDLE_INT_ARG, random.nextInt()); 37 | bundle.putSerializable(BUNDLE_SERIALIZABLE_ARG, UUID.randomUUID().toString()); 38 | intent.putExtra(SimpleActivity.BUNDLE_EXTRA, bundle); 39 | 40 | byte aByte = (byte) random.nextInt(); 41 | intent.putExtra(SimpleActivity.BYTE_EXTRA, aByte); 42 | byte[] bytes = new byte[random.nextInt(10)]; 43 | random.nextBytes(bytes); 44 | intent.putExtra(SimpleActivity.BYTE_ARRAY_EXTRA, bytes); 45 | 46 | char aChar = (char) random.nextInt(); 47 | intent.putExtra(SimpleActivity.CHAR_EXTRA, aChar); 48 | char[] chars = {(char) random.nextInt(), (char) random.nextInt()}; 49 | intent.putExtra(SimpleActivity.CHAR_ARRAY_EXTRA, chars); 50 | 51 | CharSequence charSequence = UUID.randomUUID().toString(); 52 | intent.putExtra(SimpleActivity.CHAR_SEQUENCE_EXTRA, charSequence); 53 | CharSequence[] charSequences = {UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString()}; 54 | intent.putExtra(SimpleActivity.CHAR_SEQUENCE_ARRAY_EXTRA, charSequences); 55 | ArrayList charSequenceArrayList = new ArrayList(); 56 | charSequenceArrayList.add(UUID.randomUUID().toString()); 57 | intent.putCharSequenceArrayListExtra(SimpleActivity.CHAR_SEQUENCE_ARRAY_LIST_EXTRA, charSequenceArrayList); 58 | 59 | double aDouble = random.nextDouble(); 60 | intent.putExtra(SimpleActivity.DOUBLE_EXTRA, aDouble); 61 | double[] doubles = {random.nextDouble()}; 62 | intent.putExtra(SimpleActivity.DOUBLE_ARRAY_EXTRA, doubles); 63 | 64 | float aFloat = random.nextFloat(); 65 | intent.putExtra(SimpleActivity.FLOAT_EXTRA, aFloat); 66 | float[] floats = {random.nextFloat()}; 67 | intent.putExtra(SimpleActivity.FLOAT_ARRAY_EXTRA, floats); 68 | 69 | int anInt = random.nextInt(); 70 | intent.putExtra(SimpleActivity.INT_EXTRA, anInt); 71 | int[] ints = {random.nextInt(), random.nextInt(), random.nextInt(), random.nextInt()}; 72 | intent.putExtra(SimpleActivity.INT_ARRAY_EXTRA, ints); 73 | ArrayList integerArrayList = new ArrayList(); 74 | integerArrayList.add(random.nextInt()); 75 | intent.putIntegerArrayListExtra(SimpleActivity.INTEGER_ARRAY_LIST_EXTRA, integerArrayList); 76 | 77 | long aLong = random.nextLong(); 78 | intent.putExtra(SimpleActivity.LONG_EXTRA, aLong); 79 | long[] longs = {random.nextLong(), random.nextLong(), random.nextLong(), random.nextLong()}; 80 | intent.putExtra(SimpleActivity.LONG_ARRAY_EXTRA, longs); 81 | 82 | MyParcelable parcelable = new MyParcelable(random.nextInt()); 83 | intent.putExtra(SimpleActivity.PARCELABLE_EXTRA, parcelable); 84 | MyParcelable[] parcelables = {new MyParcelable(random.nextInt()), new MyParcelable(random.nextInt()), new MyParcelable(random.nextInt())}; 85 | intent.putExtra(SimpleActivity.PARCELABLE_ARRAY_EXTRA, parcelables); 86 | ArrayList parcelableArrayList = new ArrayList(); 87 | parcelableArrayList.add(new MyParcelable(random.nextInt())); 88 | intent.putParcelableArrayListExtra(SimpleActivity.PARCELABLE_ARRAY_LIST_EXTRA, parcelableArrayList); 89 | 90 | Serializable serializable = UUID.randomUUID().toString(); 91 | intent.putExtra(SimpleActivity.SERIALIZABLE_EXTRA, serializable); 92 | 93 | short aShort = (short) random.nextInt(); 94 | intent.putExtra(SimpleActivity.SHORT_EXTRA, aShort); 95 | short[] shorts = {(short) random.nextInt(), (short) random.nextInt(), (short) random.nextInt(), (short) random.nextInt()}; 96 | intent.putExtra(SimpleActivity.SHORT_ARRAY_EXTRA, shorts); 97 | 98 | String string = UUID.randomUUID().toString(); 99 | intent.putExtra(SimpleActivity.STRING_EXTRA, string); 100 | String[] strings = {UUID.randomUUID().toString()}; 101 | intent.putExtra(SimpleActivity.STRING_ARRAY_EXTRA, strings); 102 | ArrayList stringArrayList = new ArrayList(); 103 | stringArrayList.add(UUID.randomUUID().toString()); 104 | intent.putExtra(SimpleActivity.STRING_ARRAY_LIST_EXTRA, stringArrayList); 105 | 106 | intent.putExtra(Intent.EXTRA_TEXT, string); 107 | 108 | SimpleActivity simpleActivity = Robolectric.buildActivity(SimpleActivity.class).withIntent(intent).create().get(); 109 | 110 | // Boolean 111 | assertEquals(aBoolean, simpleActivity.aBoolean); 112 | // Array 113 | assertBooleanArrayEquals(booleans, simpleActivity.booleans); 114 | 115 | // Bundle 116 | assertBundleEquals(bundle, simpleActivity.bundle); 117 | 118 | // Byte 119 | assertEquals(aByte, simpleActivity.aByte); 120 | // Array 121 | assertArrayEquals(bytes, simpleActivity.bytes); 122 | 123 | // Char 124 | assertEquals(aChar, simpleActivity.aChar); 125 | // Array 126 | assertArrayEquals(chars, simpleActivity.chars); 127 | 128 | // CharSequence 129 | assertEquals(charSequence, simpleActivity.charSequence); 130 | // Array 131 | assertArrayEquals(charSequences, simpleActivity.charSequences); 132 | // ArrayList 133 | assertArrayListEquals(charSequenceArrayList, simpleActivity.charSequenceArrayList); 134 | 135 | // Double 136 | assertEquals(aDouble, simpleActivity.aDouble, 0); 137 | // Array 138 | assertArrayEquals(doubles, simpleActivity.doubles, 0); 139 | 140 | // Float 141 | assertEquals(aFloat, simpleActivity.aFloat, 0); 142 | // Array 143 | assertArrayEquals(floats, simpleActivity.floats, 0); 144 | 145 | // Int 146 | assertEquals(anInt, simpleActivity.anInt); 147 | // Array 148 | assertArrayEquals(ints, simpleActivity.ints); 149 | // ArrayList 150 | assertArrayListEquals(integerArrayList, simpleActivity.integerArrayList); 151 | 152 | // Long 153 | assertEquals(aLong, simpleActivity.aLong); 154 | // Array 155 | assertArrayEquals(longs, simpleActivity.longs); 156 | 157 | //Parcelable 158 | assertEquals(parcelable, simpleActivity.parcelable); 159 | // Array 160 | assertArrayEquals(parcelables, simpleActivity.parcelables); 161 | // ArrayList 162 | assertArrayListEquals(parcelableArrayList, simpleActivity.parcelableArrayList); 163 | 164 | // Serializable 165 | String s1 = (String) serializable; 166 | String s2 = (String) simpleActivity.serializable; 167 | assertEquals(s1, s2); 168 | 169 | // Shorts 170 | assertEquals(aShort, simpleActivity.aShort); 171 | // Array 172 | assertArrayEquals(shorts, simpleActivity.shorts); 173 | 174 | // String 175 | assertEquals(string, simpleActivity.string); 176 | // Array 177 | assertArrayEquals(strings, simpleActivity.strings); 178 | // ArrayList 179 | assertArrayListEquals(stringArrayList, simpleActivity.stringArrayList); 180 | 181 | // Not Required 182 | assertNull(simpleActivity.s); 183 | assertEquals(SimpleActivity.NRI_DEFAULT, simpleActivity.notRequired); 184 | 185 | assertEquals(string, simpleActivity.text); 186 | 187 | } 188 | 189 | @Test(expected = IllegalStateException.class) 190 | public void verifyExceptionThrown() { 191 | Intent intent = new Intent(RuntimeEnvironment.application, SimpleActivity.class); 192 | Robolectric.buildActivity(SimpleActivity.class).withIntent(intent).create().get(); 193 | } 194 | 195 | @Test(expected = IllegalStateException.class) 196 | public void verifyNullIntentException() { 197 | Robolectric.buildActivity(SimpleActivity.class).create().get(); 198 | } 199 | 200 | @Test 201 | public void verifyNotRequiredExtraBinding() { 202 | SimpleFragmentActivity activity = Robolectric.buildActivity(SimpleFragmentActivity.class).create().get(); 203 | 204 | assertEquals(2, activity.intExtra); 205 | assertEquals("NOT_REQUIRED", activity.stringExtra); 206 | } 207 | } 208 | --------------------------------------------------------------------------------