├── .gitignore ├── LICENSE ├── README.md ├── README.md.template ├── api ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── flipboard │ └── goldengate │ ├── Bridge.java │ ├── Callback.java │ ├── Debug.java │ ├── GsonJsonSerializer.java │ ├── JavaScriptBridge.java │ ├── JavascriptCallback.java │ ├── JsonSerializer.java │ ├── Method.java │ └── Property.java ├── build.gradle ├── compiler ├── build.gradle ├── libs │ └── android-23.jar └── src │ └── main │ ├── java │ └── com │ │ └── flipboard │ │ └── goldengate │ │ ├── BridgeCallback.java │ │ ├── BridgeInterface.java │ │ ├── BridgeMethod.java │ │ ├── BridgeParameter.java │ │ ├── BridgeProperty.java │ │ ├── Processor.java │ │ └── Util.java │ └── resources │ └── META-INF │ └── services │ └── javax.annotation.processing.Processor ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── flipboard │ │ └── goldengate │ │ └── sample │ │ ├── MainActivity.java │ │ ├── Navigator.java │ │ ├── NoCallbackSample.java │ │ ├── Sample.java │ │ └── Test.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── settings.gradle └── version.properties /.gitignore: -------------------------------------------------------------------------------- 1 | ###Android### 2 | 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | 31 | ###IntelliJ### 32 | 33 | *.iml 34 | *.ipr 35 | *.iws 36 | .idea/ 37 | 38 | 39 | ###OSX### 40 | 41 | .DS_Store 42 | .AppleDouble 43 | .LSOverride 44 | 45 | # Icon must end with two \r 46 | Icon 47 | 48 | 49 | # Thumbnails 50 | ._* 51 | 52 | # Files that might appear on external disk 53 | .Spotlight-V100 54 | .Trashes 55 | 56 | # Directories potentially created on remote AFP share 57 | .AppleDB 58 | .AppleDesktop 59 | Network Trash Folder 60 | Temporary Items 61 | .apdisk 62 | 63 | 64 | ###Linux### 65 | 66 | *~ 67 | 68 | # KDE directory preferences 69 | .directory 70 | 71 | 72 | ###Gradle### 73 | 74 | .gradle 75 | build/ 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Flipboard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of Flipboard nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GoldenGate 2 | ========== 3 | GoldenGate is an Android annotation processor for generating type safe javascript bindings (Bridges). The library is very similar in usage to something like retrofit in that only an interface has to be declared and annotated (though retrofit does not do any compile time code generating). This annotated interface is at compile time used to generate an type safe wrapper around a webview for interfacing with the javascript. 4 | 5 | Installation 6 | ------------ 7 | ```groovy 8 | buildscript { 9 | repositories { 10 | jcenter() 11 | } 12 | dependencies { 13 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' 14 | } 15 | } 16 | 17 | apply plugin: 'com.neenbedankt.android-apt' 18 | 19 | dependencies { 20 | apt 'com.flipboard:goldengate-compiler:1.4.2' 21 | compile 'com.flipboard:goldengate-api:1.4.2' 22 | } 23 | ``` 24 | Usage 25 | ----- 26 | Before starting you will need to configure how object should be serialized to json. By default GoldenGate will use gson but does not package this dependency in case you would like to use something else. If you want to use gson add it as a dependency to your build.gradle. 27 | ```groovy 28 | dependencies { 29 | compile 'com.google.code.gson:gson:x.x.x' 30 | } 31 | ``` 32 | If you would like to use some other library like jackson or maybe a custom json implementation you can register a JsonSerializer with JavascriptBridge. 33 | ```java 34 | JavaScriptBridge.setJsonSerializer(new JsonSerializer(){ 35 | @Override 36 | String toJson(T stuff) { 37 | // do stuff 38 | } 39 | 40 | @Override 41 | T fromJson(String json, Class type) { 42 | // do stuff 43 | } 44 | }); 45 | ``` 46 | 47 | Start by creating an interface and annotate it with `@Bridge` and also add a method which you want to call in javascript. 48 | ```java 49 | @Bridge 50 | interface MyJavascript { 51 | void alert(String message); 52 | } 53 | ``` 54 | 55 | This will automatically generate a class called `MyJavascriptBridge` which is the implementation which wraps a webview and implements the interface we just defined. Now we have a compile time checked type safe way of opening a javascript alert. 56 | ```java 57 | Webview webview = ...; 58 | MyJavascript bridge = new MyJavascriptBridge(webview); 59 | bridge.alert("Hi there!"); 60 | ``` 61 | 62 | The above example is just a fire and forget example. We often want to get some result back. For this we have `Callback`, because javascript runs asynchronously we can't just return this value and must therefor use a callback. The callback argument must allways be the argument specified last in the method decleration. Here is an example of using `Callback`. 63 | ```java 64 | @Bridge 65 | interface MyJavascript { 66 | void calculateSomeValue(Callback value); 67 | } 68 | 69 | Webview webview = ...; 70 | MyJavascript bridge = new MyJavascriptBridge(webview); 71 | bridge.calculateSomeValue(new Callback() { 72 | @Override 73 | void onResult(Integer result) { 74 | // do something with result 75 | } 76 | }); 77 | ``` 78 | 79 | That's it for simple usage! There are two other annotations for customized usage, `@Method` and `@Property`. `@Method` can be used to override the name of the method on the javascript side of the bridge (The java name of the method is automatically chosen if this annotation is not supplied). 80 | ```java 81 | @Bridge 82 | interface MyJavascript { 83 | @Method("console.Log") 84 | void alert(String message); 85 | } 86 | ``` 87 | 88 | The `@Property` annotation should be used for when setting or getting a property on the javascript side of things. In this case the method may only have one parameter (either a callback for result or a value which should be set). Just like with the `@Method` declaration a custom name can be chosen for the property. The default name for properties however is the name of the parameter to the method. 89 | ```java 90 | @Bridge 91 | interface MyJavascript { 92 | @Property("window.innerHeight") 93 | void getWindowHeight(Callback height); 94 | } 95 | ``` 96 | 97 | The `@JavascriptCallback` annotation should be used on method parameters of type `Callback` which will be passed to javascript as a javascript callback. This allows javascript functions to call back into your java code with a result at a later time, perhaps after a network request has finished. 98 | ```java 99 | @Bridge 100 | interface MyJavascript { 101 | @Method("[1,2,3,4,5,6,7,8,9,10].forEach") 102 | void looptyLoop(@JavascriptCallback Callback val); 103 | } 104 | ``` 105 | 106 | And lastly if things aren't working as expected there is a `@Debug` annotation that can be added to your `@Bridge` annotated interface which will cause the javascript being executed to be logged to the console beforehand. 107 | ```java 108 | @Debug 109 | @Bridge 110 | interface MyJavascript { 111 | void alert(String message); 112 | } 113 | ``` 114 | 115 | Proguard 116 | -------- 117 | 118 | If you use Proguard, you'll want to make sure you add the following to your config 119 | ``` 120 | # GoldenGate 121 | -keep class * extends com.flipboard.goldengate.JavaScriptBridge { *; } 122 | -keepattributes JavascriptInterface 123 | -keepclassmembers class ** { 124 | @android.webkit.JavascriptInterface public *; 125 | } 126 | ``` 127 | 128 | Contributing 129 | ------------ 130 | We welcome pull requests for bug fixes, new features, and improvements to GoldenGate. Contributors to the main GoldenGate repository must accept Flipboard's Apache-style [Individual Contributor License Agreement (CLA)](https://docs.google.com/forms/d/1gh9y6_i8xFn6pA15PqFeye19VqasuI9-bGp_e0owy74/viewform) before any changes can be merged. 131 | 132 | -------------------------------------------------------------------------------- /README.md.template: -------------------------------------------------------------------------------- 1 | GoldenGate 2 | ========== 3 | GoldenGate is an Android annotation processor for generating type safe javascript bindings (Bridges). The library is very similar in usage to something like retrofit in that only an interface has to be declared and annotated (though retrofit does not do any compile time code generating). This annotated interface is at compile time used to generate an type safe wrapper around a webview for interfacing with the javascript. 4 | 5 | Installation 6 | ------------ 7 | ```groovy 8 | buildscript { 9 | repositories { 10 | jcenter() 11 | } 12 | dependencies { 13 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' 14 | } 15 | } 16 | 17 | apply plugin: 'com.neenbedankt.android-apt' 18 | 19 | dependencies { 20 | apt 'com.flipboard:goldengate-compiler:%%version%%' 21 | compile 'com.flipboard:goldengate-api:%%version%%' 22 | } 23 | ``` 24 | Usage 25 | ----- 26 | Before starting you will need to configure how object should be serialized to json. By default GoldenGate will use gson but does not package this dependency in case you would like to use something else. If you want to use gson add it as a dependency to your build.gradle. 27 | ```groovy 28 | dependencies { 29 | compile 'com.google.code.gson:gson:x.x.x' 30 | } 31 | ``` 32 | If you would like to use some other library like jackson or maybe a custom json implementation you can register a JsonSerializer with JavascriptBridge. 33 | ```java 34 | JavaScriptBridge.setJsonSerializer(new JsonSerializer(){ 35 | @Override 36 | String toJson(T stuff) { 37 | // do stuff 38 | } 39 | 40 | @Override 41 | T fromJson(String json, Class type) { 42 | // do stuff 43 | } 44 | }); 45 | ``` 46 | 47 | Start by creating an interface and annotate it with `@Bridge` and also add a method which you want to call in javascript. 48 | ```java 49 | @Bridge 50 | interface MyJavascript { 51 | void alert(String message); 52 | } 53 | ``` 54 | 55 | This will automatically generate a class called `MyJavascriptBridge` which is the implementation which wraps a webview and implements the interface we just defined. Now we have a compile time checked type safe way of opening a javascript alert. 56 | ```java 57 | Webview webview = ...; 58 | MyJavascript bridge = new MyJavascriptBridge(webview); 59 | bridge.alert("Hi there!"); 60 | ``` 61 | 62 | The above example is just a fire and forget example. We often want to get some result back. For this we have `Callback`, because javascript runs asynchronously we can't just return this value and must therefor use a callback. The callback argument must allways be the argument specified last in the method decleration. Here is an example of using `Callback`. 63 | ```java 64 | @Bridge 65 | interface MyJavascript { 66 | void calculateSomeValue(Callback value); 67 | } 68 | 69 | Webview webview = ...; 70 | MyJavascript bridge = new MyJavascriptBridge(webview); 71 | bridge.calculateSomeValue(new Callback() { 72 | @Override 73 | void onResult(Integer result) { 74 | // do something with result 75 | } 76 | }); 77 | ``` 78 | 79 | That's it for simple usage! There are two other annotations for customized usage, `@Method` and `@Property`. `@Method` can be used to override the name of the method on the javascript side of the bridge (The java name of the method is automatically chosen if this annotation is not supplied). 80 | ```java 81 | @Bridge 82 | interface MyJavascript { 83 | @Method("console.Log") 84 | void alert(String message); 85 | } 86 | ``` 87 | 88 | The `@Property` annotation should be used for when setting or getting a property on the javascript side of things. In this case the method may only have one parameter (either a callback for result or a value which should be set). Just like with the `@Method` declaration a custom name can be chosen for the property. The default name for properties however is the name of the parameter to the method. 89 | ```java 90 | @Bridge 91 | interface MyJavascript { 92 | @Property("window.innerHeight") 93 | void getWindowHeight(Callback height); 94 | } 95 | ``` 96 | 97 | The `@JavascriptCallback` annotation should be used on method parameters of type `Callback` which will be passed to javascript as a javascript callback. This allows javascript functions to call back into your java code with a result at a later time, perhaps after a network request has finished. 98 | ```java 99 | @Bridge 100 | interface MyJavascript { 101 | @Method("[1,2,3,4,5,6,7,8,9,10].forEach") 102 | void looptyLoop(@JavascriptCallback Callback val); 103 | } 104 | ``` 105 | 106 | And lastly if things aren't working as expected there is a `@Debug` annotation that can be added to your `@Bridge` annotated interface which will cause the javascript being executed to be logged to the console beforehand. 107 | ```java 108 | @Debug 109 | @Bridge 110 | interface MyJavascript { 111 | void alert(String message); 112 | } 113 | ``` 114 | 115 | Proguard 116 | -------- 117 | 118 | If you use Proguard, you'll want to make sure you add the following to your config 119 | ``` 120 | # GoldenGate 121 | -keep class * extends com.flipboard.goldengate.JavaScriptBridge { *; } 122 | -keepattributes JavascriptInterface 123 | -keepclassmembers class ** { 124 | @android.webkit.JavascriptInterface public *; 125 | } 126 | ``` 127 | 128 | Contributing 129 | ------------ 130 | We welcome pull requests for bug fixes, new features, and improvements to GoldenGate. Contributors to the main GoldenGate repository must accept Flipboard's Apache-style [Individual Contributor License Agreement (CLA)](https://docs.google.com/forms/d/1gh9y6_i8xFn6pA15PqFeye19VqasuI9-bGp_e0owy74/viewform) before any changes can be merged. 131 | 132 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /api/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'bintray-release' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 23 11 | } 12 | compileOptions { 13 | sourceCompatibility JavaVersion.VERSION_1_7 14 | targetCompatibility JavaVersion.VERSION_1_7 15 | } 16 | } 17 | 18 | dependencies { 19 | compile fileTree(dir: 'libs', include: ['*.jar']) 20 | provided 'com.google.code.gson:gson:2.4' 21 | } 22 | 23 | publish { 24 | userOrg = 'flipboard' 25 | groupId = 'com.flipboard' 26 | artifactId = 'goldengate-api' 27 | version = VERSION 28 | description = 'Generate type safe java bridge into webview javascript code.' 29 | website = 'https://github.com/flipboard/goldengate' 30 | licences = ['BSD 3-Clause'] 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/Bridge.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotate an interface with this annotation. The methods in the annotated interface serve as a communication bridge 10 | * to javascript running in a web view. An implementation of the javascript bridge will be generated from the interface 11 | * which was annotated with this annotation. The implementation will have the same name as your interface type but with 12 | * 'Bridge' appended to the end of the name. 13 | * 14 | * Methods within the interface can also be annotated with the @Method and @Property annotations for greater control 15 | * over what happens on the javascript side of things. 16 | */ 17 | @Target(ElementType.TYPE) 18 | @Retention(RetentionPolicy.SOURCE) 19 | public @interface Bridge { } 20 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/Callback.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | /** 4 | * Callback used to get results from a javascript call 5 | * 6 | * @param The return type of the javascript call 7 | */ 8 | public interface Callback { 9 | void onResult(T result); 10 | } 11 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/Debug.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotate a @Bridge annotated interface with this to log all the javascript calls before executing them. 10 | */ 11 | @Target(ElementType.TYPE) 12 | @Retention(RetentionPolicy.SOURCE) 13 | public @interface Debug { } 14 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/GsonJsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import com.google.gson.Gson; 4 | 5 | public class GsonJsonSerializer implements JsonSerializer { 6 | 7 | private Gson gson = new Gson(); 8 | 9 | public String toJson(T stuff) { 10 | return gson.toJson(stuff); 11 | } 12 | 13 | public T fromJson(String json, Class type) { 14 | return gson.fromJson(json, type); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/JavaScriptBridge.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.webkit.WebView; 6 | 7 | import java.util.Random; 8 | 9 | /** 10 | * Base class with a couple helper methods for the generated bridges 11 | */ 12 | public abstract class JavaScriptBridge { 13 | 14 | private static JsonSerializer defaultJsonSerializer; 15 | 16 | private final Random random = new Random(); 17 | protected final WebView webView; 18 | private final JsonSerializer jsonSerializer; 19 | 20 | public JavaScriptBridge(WebView webView) { 21 | this(webView, defaultJsonSerializer != null ? defaultJsonSerializer : new GsonJsonSerializer()); 22 | } 23 | 24 | public JavaScriptBridge(WebView webView, JsonSerializer jsonSerializer) { 25 | this.webView = webView; 26 | this.jsonSerializer = jsonSerializer; 27 | } 28 | 29 | protected String toJson(T stuff) { 30 | return jsonSerializer.toJson(stuff); 31 | } 32 | 33 | protected T fromJson(String json, Class type) { 34 | return jsonSerializer.fromJson(json, type); 35 | } 36 | 37 | public static void setJsonSerializer(JsonSerializer serializer) { 38 | defaultJsonSerializer = serializer; 39 | } 40 | 41 | @TargetApi(Build.VERSION_CODES.KITKAT) 42 | protected static void evaluateJavascript(WebView webView, String javascript) { 43 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 44 | webView.evaluateJavascript(javascript, null); 45 | } else { 46 | webView.loadUrl("javascript:" + javascript); 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/JavascriptCallback.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotate a method within a @Bridge annotated interface with this annotation to specify a different name for the 10 | * javascript method then the name of the method on the java side. 11 | */ 12 | @Target(ElementType.PARAMETER) 13 | @Retention(RetentionPolicy.SOURCE) 14 | public @interface JavascriptCallback { } 15 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/JsonSerializer.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | public interface JsonSerializer { 4 | 5 | String toJson(T stuff); 6 | 7 | T fromJson(String json, Class type); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/Method.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotate a method within a @Bridge annotated interface with this annotation to specify a different name for the 10 | * javascript method then the name of the method on the java side. 11 | */ 12 | @Target(ElementType.METHOD) 13 | @Retention(RetentionPolicy.SOURCE) 14 | public @interface Method { 15 | String value(); 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/com/flipboard/goldengate/Property.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotate a method within a @Bridge annotated interface with this annotation to specify that this is not a method but 10 | * a property. This limits the method to be a simple parameter method. By default the name of this parameter is chosen 11 | * to be the name of the javascript property but a different name can be specified as a parameter to this annotation. 12 | */ 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.SOURCE) 15 | public @interface Property { 16 | String value() default ""; 17 | } 18 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:1.5.0' 7 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 8 | classpath 'com.novoda:bintray-release:0.3.4' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | jcenter() 15 | } 16 | } 17 | 18 | ext { 19 | VERSION = version() 20 | } 21 | 22 | task bumpMajor << { 23 | ant.propertyfile(file: 'version.properties') { 24 | entry(key: 'major', type: 'int', operation: '+', value: 1) 25 | entry(key: 'minor', type: 'int', operation: '=', value: 0) 26 | entry(key: 'patch', type: 'int', operation: '=', value: 0) 27 | } 28 | } 29 | 30 | task bumpMinor << { 31 | ant.propertyfile(file: 'version.properties') { 32 | entry(key: 'minor', type: 'int', operation: '+', value: 1) 33 | entry(key: 'patch', type: 'int', operation: '=', value: 0) 34 | } 35 | } 36 | 37 | task bumpPatch << { 38 | ant.propertyfile(file: 'version.properties') { 39 | entry(key: 'patch', type: 'int', operation: '+', value: 1) 40 | } 41 | } 42 | 43 | task genReadMe << { 44 | def template = file('README.md.template').text 45 | def result = template.replaceAll("%%version%%", version()) 46 | file("README.md").withWriter{ it << result } 47 | } 48 | 49 | task version << { 50 | println version() 51 | } 52 | 53 | def String version() { 54 | def versionPropsFile = file('version.properties') 55 | def Properties versionProps = new Properties() 56 | versionProps.load(new FileInputStream(versionPropsFile)) 57 | 58 | return versionProps['major'] + "." + versionProps['minor'] + "." + versionProps['patch'] 59 | } 60 | -------------------------------------------------------------------------------- /compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'bintray-release' 3 | 4 | sourceCompatibility = 1.7 5 | 6 | sourceSets { 7 | main { 8 | java { 9 | srcDirs = ['src/main/java', '../api/src/main/java'] 10 | } 11 | } 12 | } 13 | 14 | dependencies { 15 | compile fileTree(dir: 'libs', include: ['*.jar']) 16 | compile project(':api') 17 | compile 'com.squareup:javapoet:1.4.0' 18 | compile 'com.google.code.gson:gson:2.4' 19 | } 20 | 21 | publish { 22 | userOrg = 'flipboard' 23 | groupId = 'com.flipboard' 24 | artifactId = 'goldengate-compiler' 25 | version = VERSION 26 | description = 'Generate type safe java bridge into webview javascript code.' 27 | website = 'https://github.com/flipboard/goldengate' 28 | licences = ['BSD 3-Clause'] 29 | } 30 | -------------------------------------------------------------------------------- /compiler/libs/android-23.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flipboard/GoldenGate/1f3089b7bf5a7897abbe9f9506c2ba588e538c4c/compiler/libs/android-23.jar -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/BridgeCallback.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import javax.lang.model.element.VariableElement; 4 | import javax.lang.model.type.DeclaredType; 5 | import javax.lang.model.type.TypeMirror; 6 | 7 | public class BridgeCallback extends BridgeParameter { 8 | 9 | public final TypeMirror genericType; 10 | 11 | public BridgeCallback(VariableElement e) { 12 | super(e); 13 | this.genericType = ((DeclaredType) type).getTypeArguments().get(0); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/BridgeInterface.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import android.webkit.WebView; 4 | 5 | import com.google.gson.reflect.TypeToken; 6 | import com.squareup.javapoet.ClassName; 7 | import com.squareup.javapoet.CodeBlock; 8 | import com.squareup.javapoet.FieldSpec; 9 | import com.squareup.javapoet.JavaFile; 10 | import com.squareup.javapoet.MethodSpec; 11 | import com.squareup.javapoet.TypeName; 12 | import com.squareup.javapoet.TypeSpec; 13 | 14 | import java.io.IOException; 15 | import java.lang.ref.WeakReference; 16 | import java.lang.reflect.Type; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | import java.util.concurrent.atomic.AtomicLong; 22 | 23 | import javax.annotation.processing.Filer; 24 | import javax.lang.model.element.Element; 25 | import javax.lang.model.element.ExecutableElement; 26 | import javax.lang.model.element.Modifier; 27 | import javax.lang.model.element.PackageElement; 28 | import javax.lang.model.type.TypeMirror; 29 | import javax.lang.model.util.Elements; 30 | import javax.lang.model.util.Types; 31 | 32 | public class BridgeInterface { 33 | 34 | public final String name; 35 | public final boolean isDebug; 36 | private TypeMirror type; 37 | private ArrayList bridgeMethods = new ArrayList<>(); 38 | private ArrayList bridgeProperties = new ArrayList<>(); 39 | 40 | public BridgeInterface(Element element, Elements elementUtils, Types typeUtils) { 41 | this.name = element.getSimpleName().toString(); 42 | this.isDebug = element.getAnnotation(Debug.class) != null; 43 | this.type = element.asType(); 44 | 45 | for (Element method : element.getEnclosedElements()) { 46 | if (method.getAnnotation(Property.class) != null) { 47 | bridgeProperties.add(new BridgeProperty((ExecutableElement) method)); 48 | } else { 49 | bridgeMethods.add(new BridgeMethod(((ExecutableElement) method), elementUtils, typeUtils)); 50 | } 51 | } 52 | } 53 | 54 | private static boolean hasCallback(List methods) { 55 | for (BridgeMethod method : methods) { 56 | if (method.hasCallbackParameters) { 57 | return true; 58 | } 59 | } 60 | return false; 61 | } 62 | 63 | public void writeToFiler(Filer filer) throws IOException { 64 | String packageName = getPackageName(type); 65 | 66 | // Build Bridge class 67 | TypeSpec.Builder bridge = TypeSpec.classBuilder(name + "Bridge") 68 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 69 | .addSuperinterface(TypeName.get(type)) 70 | .superclass(JavaScriptBridge.class); 71 | 72 | // Generate the result bridge if necessary 73 | boolean needsCallbacks = hasCallback(bridgeMethods); 74 | if (needsCallbacks) { 75 | bridge.addField(ClassName.get(packageName, name + "Bridge", "ResultBridge"), "resultBridge", Modifier.PRIVATE); 76 | bridge.addField(AtomicLong.class, "receiverIds", Modifier.PRIVATE); 77 | Type callbacksMapType = new TypeToken>>>(){}.getType(); 78 | Type callbackType = new TypeToken>(){}.getType(); 79 | bridge.addType(TypeSpec.classBuilder("ResultBridge") 80 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) 81 | .addField(FieldSpec.builder(callbacksMapType, "callbacks") 82 | .initializer("new $T<>()", HashMap.class) 83 | .build()) 84 | .addMethod(MethodSpec.methodBuilder("registerCallback") 85 | .addParameter(long.class, "receiver") 86 | .addParameter(callbackType, "cb") 87 | .addCode(CodeBlock.builder() 88 | .addStatement("callbacks.put($N, new $T($N))", "receiver", WeakReference.class, "cb").build()) 89 | .build()) 90 | .addMethod(MethodSpec.methodBuilder("onResult") 91 | .addModifiers(Modifier.PUBLIC) 92 | .addAnnotation(ClassName.get("android.webkit", "JavascriptInterface")) 93 | .addParameter(String.class, "result") 94 | .addCode(CodeBlock.builder() 95 | .beginControlFlow("try") 96 | .addStatement("$T $N = new $T($N)", ClassName.get("org.json", "JSONObject"), "json", ClassName.get("org.json", "JSONObject"), "result") 97 | .addStatement("$T $N = $N.getLong($S)", long.class, "receiver", "json", "receiver") 98 | .addStatement("$T $N = $N.get($S).toString()", String.class, "realResult", "json", "result") 99 | .addStatement("$T $N = $N.get($N).get()", callbackType, "callback", "callbacks", "receiver") 100 | .beginControlFlow("if ($N != null) ", "callback") 101 | .addStatement("$N.onResult($N)", "callback", "realResult") 102 | .endControlFlow() 103 | .nextControlFlow("catch (org.json.JSONException e)") 104 | .addStatement("$N.printStackTrace()", "e") 105 | .endControlFlow() 106 | .build()) 107 | .build()) 108 | .build()); 109 | } 110 | 111 | // Add Bridge constructor using globally configured json serializer 112 | MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder() 113 | .addModifiers(Modifier.PUBLIC) 114 | .addParameter(WebView.class, "webView") 115 | .addStatement("super($N)", "webView"); 116 | if (needsCallbacks) { 117 | constructorBuilder.addStatement("initBridge()"); 118 | } 119 | bridge.addMethod( 120 | constructorBuilder.build() 121 | ); 122 | 123 | // Add Bridge constructor using custom json serializer 124 | constructorBuilder = MethodSpec.constructorBuilder() 125 | .addModifiers(Modifier.PUBLIC) 126 | .addParameter(WebView.class, "webView") 127 | .addParameter(JsonSerializer.class, "jsonSerializer") 128 | .addStatement("super($N, $N)", "webView", "jsonSerializer"); 129 | if (needsCallbacks) { 130 | constructorBuilder.addStatement("initBridge()"); 131 | } 132 | bridge.addMethod( 133 | constructorBuilder.build() 134 | ); 135 | 136 | // Add common init method for both constructors 137 | if (needsCallbacks) { 138 | bridge.addMethod( 139 | MethodSpec.methodBuilder("initBridge") 140 | .addModifiers(Modifier.PRIVATE) 141 | .addStatement("this.$N = new ResultBridge()", "resultBridge") 142 | .addStatement("this.$N = new $T()", "receiverIds", AtomicLong.class) 143 | .addStatement("this.$N.addJavascriptInterface($N, $L)", "webView", "resultBridge", 144 | "\"" + name + "\"") 145 | .addCode("evaluateJavascript(webView, \n" + 146 | " \"function GoldenGate$$$$CreateCallback(receiver) {\" +\n" + 147 | " \" return function(result) {\" +\n" + 148 | " \" $N.onResult(JSON.stringify({receiver: receiver, result: JSON.stringify(result)}))\" +\n" 149 | + 150 | " \" }\" +\n" + 151 | " \"}\");", name) 152 | .build() 153 | ); 154 | } 155 | 156 | // Add Bridge methods 157 | for (BridgeMethod method : bridgeMethods) { 158 | bridge.addMethod(method.toMethodSpec(this)); 159 | } 160 | 161 | // Add Bridge property methods 162 | for (BridgeProperty property : bridgeProperties) { 163 | bridge.addMethod(property.toMethodSpec(this)); 164 | } 165 | 166 | // Write source 167 | JavaFile javaFile = JavaFile.builder(packageName, bridge.build()).build(); 168 | javaFile.writeTo(filer); 169 | } 170 | 171 | private String getPackageName(TypeMirror type) { 172 | Element element = Processor.instance.typeUtils.asElement(type); 173 | while (!(element instanceof PackageElement)) { 174 | element = element.getEnclosingElement(); 175 | } 176 | return ((PackageElement) element).getQualifiedName().toString(); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/BridgeMethod.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import com.squareup.javapoet.CodeBlock; 4 | import com.squareup.javapoet.MethodSpec; 5 | import com.squareup.javapoet.TypeName; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | import javax.lang.model.element.ExecutableElement; 13 | import javax.lang.model.element.Modifier; 14 | import javax.lang.model.element.VariableElement; 15 | import javax.lang.model.type.TypeMirror; 16 | import javax.lang.model.util.Elements; 17 | import javax.lang.model.util.Types; 18 | 19 | public class BridgeMethod { 20 | 21 | public final String javaName; 22 | public final String name; 23 | public final List parameters = new ArrayList<>(); 24 | public BridgeCallback callback; 25 | public boolean hasCallbackParameters = false; 26 | 27 | public BridgeMethod(ExecutableElement e, Elements elementUtils, Types typeUtils) { 28 | this.javaName = e.getSimpleName().toString(); 29 | this.name = e.getAnnotation(Method.class) != null ? e.getAnnotation(Method.class).value() : javaName; 30 | 31 | TypeMirror callbackMirror = Util.typeFromClass(typeUtils, elementUtils, Callback.class); 32 | 33 | for (int i = 0; i < e.getParameters().size(); i++) { 34 | VariableElement param = e.getParameters().get(i); 35 | boolean isJavascriptCallback = param.getAnnotation(JavascriptCallback.class) != null; 36 | 37 | if (i == e.getParameters().size() - 1 && Util.isCallback(param) && !isJavascriptCallback) { 38 | callback = new BridgeCallback(param); 39 | } else { 40 | if (isJavascriptCallback) { 41 | if (Util.isCallback(param)) { 42 | parameters.add(new BridgeCallback(param)); 43 | hasCallbackParameters = true; 44 | } else { 45 | throw new IllegalArgumentException("Param with annotation @JavascriptCallback must be of type com.flipboard.goldengate.Callback"); 46 | } 47 | } else { 48 | if (typeUtils.isSameType(param.asType(), callbackMirror)) { 49 | hasCallbackParameters = true; 50 | } 51 | parameters.add(new BridgeParameter(param)); 52 | } 53 | } 54 | } 55 | } 56 | 57 | public MethodSpec toMethodSpec(BridgeInterface bridge) { 58 | MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(javaName).addModifiers(Modifier.PUBLIC); 59 | for (int i = 0; i < parameters.size(); i++) { 60 | methodSpec.addParameter(TypeName.get(parameters.get(i).type), parameters.get(i).name, Modifier.FINAL); 61 | } 62 | 63 | if (callback != null) { 64 | methodSpec.addParameter(TypeName.get(callback.type), callback.name, Modifier.FINAL); 65 | methodSpec.addCode(CodeBlock.builder() 66 | .addStatement("$T id = receiverIds.incrementAndGet()", long.class) 67 | .add("this.resultBridge.registerCallback(id, new Callback<$T>() {\n", String.class) 68 | .indent() 69 | .add("@$T\n", Override.class) 70 | .add("public void onResult(String result) {\n") 71 | .indent() 72 | .addStatement("$N.onResult(fromJson(result, $T.class))", callback.name, callback.genericType) 73 | .unindent() 74 | .add("}\n") 75 | .unindent() 76 | .addStatement("})") 77 | .build()); 78 | } 79 | 80 | if (hasCallbackParameters) { 81 | methodSpec.addStatement("$T<$T, $T> idMap = new $T<>()", Map.class, String.class, Long.class, HashMap.class); 82 | } 83 | for (BridgeParameter parameter : parameters) { 84 | if (parameter instanceof BridgeCallback) { 85 | BridgeCallback callbackParameter = (BridgeCallback) parameter; 86 | methodSpec.addCode(CodeBlock.builder() 87 | .addStatement("idMap.put($S, receiverIds.incrementAndGet())", callbackParameter.name) 88 | .add("this.resultBridge.registerCallback(idMap.get($S), new Callback<$T>() {\n", callbackParameter.name, String.class) 89 | .indent() 90 | .add("@$T\n", Override.class) 91 | .add("public void onResult(String result) {\n") 92 | .indent() 93 | .addStatement("$N.onResult(fromJson(result, $T.class))", callbackParameter.name, callbackParameter.genericType) 94 | .unindent() 95 | .add("}\n") 96 | .unindent() 97 | .addStatement("})") 98 | .build()); 99 | } 100 | } 101 | 102 | if (parameters.size() > 0) { 103 | String parameterList = ""; 104 | for (int i = 0; i < parameters.size(); i++) { 105 | BridgeParameter parameter = parameters.get(i); 106 | if (parameter instanceof BridgeCallback) { 107 | BridgeCallback callbackParameter = (BridgeCallback) parameter; 108 | parameterList += "\"GoldenGate$$CreateCallback(\"+idMap.get(\"" + callbackParameter.name + "\")+\")\""; 109 | } else if (i < parameters.size()) { 110 | parameterList += "toJson(" + parameter.name + ")"; 111 | } 112 | if (i < parameters.size()-1) { 113 | parameterList += " + \", \" + "; 114 | } 115 | } 116 | 117 | if (callback == null) { 118 | CodeBlock.Builder codeBlock = CodeBlock.builder(); 119 | codeBlock.addStatement("$T javascript = \"$L(\"+$L+\");\"", String.class, name, parameterList); 120 | if (bridge.isDebug) { 121 | codeBlock.addStatement("android.util.Log.d($S, javascript)", bridge.name); 122 | } 123 | codeBlock.addStatement("evaluateJavascript(webView, javascript)"); 124 | methodSpec.addCode(codeBlock.build()); 125 | } else { 126 | CodeBlock.Builder codeBlock = CodeBlock.builder(); 127 | codeBlock.addStatement("$T javascript = \"$L.onResult(JSON.stringify({receiver:\"+id+\", result:JSON.stringify($L(\"+$L+\"))}));\"", String.class, bridge.name, name, parameterList); 128 | if (bridge.isDebug) { 129 | codeBlock.addStatement("android.util.Log.d($S, javascript)", bridge.name); 130 | } 131 | codeBlock.addStatement("evaluateJavascript(webView, javascript)"); 132 | methodSpec.addCode(codeBlock.build()); 133 | } 134 | } else { 135 | if (callback == null) { 136 | CodeBlock.Builder codeBlock = CodeBlock.builder(); 137 | codeBlock.addStatement("$T javascript = \"$L();\"", String.class, name); 138 | if (bridge.isDebug) { 139 | codeBlock.addStatement("android.util.Log.d($S, javascript)", bridge.name); 140 | } 141 | codeBlock.addStatement("evaluateJavascript(webView, javascript)"); 142 | methodSpec.addCode(codeBlock.build()); 143 | } else { 144 | CodeBlock.Builder codeBlock = CodeBlock.builder(); 145 | codeBlock.addStatement("$T javascript = \"$L.onResult(JSON.stringify({receiver:\"+id+\", result:JSON.stringify($L())}));\"", String.class, bridge.name, name); 146 | if (bridge.isDebug) { 147 | codeBlock.addStatement("android.util.Log.d($S, javascript)", bridge.name); 148 | } 149 | codeBlock.addStatement("evaluateJavascript(webView, javascript)"); 150 | methodSpec.addCode(codeBlock.build()); 151 | } 152 | } 153 | 154 | return methodSpec.build(); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/BridgeParameter.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import javax.lang.model.element.VariableElement; 4 | import javax.lang.model.type.TypeMirror; 5 | 6 | public class BridgeParameter { 7 | 8 | public final String name; 9 | public final TypeMirror type; 10 | 11 | public BridgeParameter(VariableElement param) { 12 | this.name = param.getSimpleName().toString(); 13 | this.type = param.asType(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/BridgeProperty.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import com.squareup.javapoet.CodeBlock; 4 | import com.squareup.javapoet.MethodSpec; 5 | import com.squareup.javapoet.TypeName; 6 | 7 | import javax.lang.model.element.ExecutableElement; 8 | import javax.lang.model.element.Modifier; 9 | import javax.lang.model.element.VariableElement; 10 | 11 | public class BridgeProperty { 12 | 13 | public final String javaName; 14 | public final String name; 15 | public final BridgeParameter parameter; 16 | public final BridgeCallback callback; 17 | 18 | public BridgeProperty(ExecutableElement e) { 19 | this.javaName = e.getSimpleName().toString(); 20 | 21 | String name = e.getAnnotation(Property.class).value(); 22 | VariableElement param = e.getParameters().get(0); 23 | this.name = "".equals(name) ? param.getSimpleName().toString() : name; 24 | 25 | if (Util.isCallback(param)) { 26 | parameter = null; 27 | callback = new BridgeCallback(param); 28 | } else { 29 | parameter = new BridgeParameter(param); 30 | callback = null; 31 | } 32 | } 33 | 34 | public MethodSpec toMethodSpec(BridgeInterface bridge) { 35 | MethodSpec.Builder methodSpec = MethodSpec.methodBuilder(javaName).addModifiers(Modifier.PUBLIC); 36 | 37 | if (callback != null) { 38 | methodSpec.addParameter(TypeName.get(callback.type), callback.name, Modifier.FINAL); 39 | methodSpec.addCode(CodeBlock.builder() 40 | .addStatement("$T id = receiverIds.incrementAndGet()", long.class) 41 | .add("this.resultBridge.registerCallback(id, new Callback<$T>() {\n", String.class) 42 | .indent() 43 | .add("@$T\n", Override.class) 44 | .add("public void onResult(String result) {\n") 45 | .indent() 46 | .addStatement("$N.onResult(fromJson(result, $T.class))", callback.name, callback.genericType) 47 | .unindent() 48 | .add("}\n") 49 | .unindent() 50 | .addStatement("})") 51 | .build()); 52 | 53 | CodeBlock.Builder codeBlock = CodeBlock.builder(); 54 | codeBlock.addStatement("$T javascript = \"$L.onResult(JSON.stringify({receiver:\"+id+\", result:JSON.stringify($L)}));\"", String.class, bridge.name, name); 55 | if (bridge.isDebug) { 56 | codeBlock.addStatement("android.util.Log.d($S, javascript)", bridge.name); 57 | } 58 | codeBlock.addStatement("evaluateJavascript(webView, javascript)"); 59 | methodSpec.addCode(codeBlock.build()); 60 | } else { 61 | methodSpec.addParameter(TypeName.get(parameter.type), parameter.name, Modifier.FINAL); 62 | CodeBlock.Builder codeBlock = CodeBlock.builder(); 63 | codeBlock.addStatement("$T javascript = \"$L = \"+toJson($L)+\";\"", String.class, name, parameter.name); 64 | if (bridge.isDebug) { 65 | codeBlock.addStatement("android.util.Log.d($S, javascript)", bridge.name); 66 | } 67 | codeBlock.addStatement("evaluateJavascript(webView, javascript)"); 68 | methodSpec.addCode(codeBlock.build()); 69 | } 70 | 71 | return methodSpec.build(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/Processor.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | import javax.annotation.processing.AbstractProcessor; 7 | import javax.annotation.processing.Filer; 8 | import javax.annotation.processing.Messager; 9 | import javax.annotation.processing.ProcessingEnvironment; 10 | import javax.annotation.processing.RoundEnvironment; 11 | import javax.lang.model.SourceVersion; 12 | import javax.lang.model.element.Element; 13 | import javax.lang.model.element.ElementKind; 14 | import javax.lang.model.element.TypeElement; 15 | import javax.lang.model.util.Elements; 16 | import javax.lang.model.util.Types; 17 | import javax.tools.Diagnostic; 18 | 19 | public class Processor extends AbstractProcessor { 20 | 21 | public static Processor instance; 22 | 23 | public Types typeUtils; 24 | public Elements elementUtils; 25 | public Filer filer; 26 | public Messager messager; 27 | 28 | @Override 29 | public Set getSupportedAnnotationTypes() { 30 | return new HashSet() {{ 31 | add(Bridge.class.getCanonicalName()); 32 | }}; 33 | } 34 | 35 | @Override 36 | public SourceVersion getSupportedSourceVersion() { 37 | return SourceVersion.latestSupported(); 38 | } 39 | 40 | @Override 41 | public synchronized void init(ProcessingEnvironment processingEnv) { 42 | super.init(processingEnv); 43 | instance = this; 44 | typeUtils = processingEnv.getTypeUtils(); 45 | elementUtils = processingEnv.getElementUtils(); 46 | filer = processingEnv.getFiler(); 47 | messager = processingEnv.getMessager(); 48 | } 49 | 50 | @Override 51 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 52 | for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(Bridge.class)) { 53 | // Make sure element is an interface declaration 54 | if (annotatedElement.getKind() != ElementKind.INTERFACE) { 55 | error(annotatedElement, "Only interfaces can be annotated with @%s", Bridge.class.getSimpleName()); 56 | return true; 57 | } 58 | 59 | try { 60 | new BridgeInterface(annotatedElement, elementUtils, typeUtils).writeToFiler(filer); 61 | } catch (Exception e) { 62 | error(annotatedElement, "%s", e.getMessage()); 63 | } 64 | } 65 | return true; 66 | } 67 | 68 | private void error(Element e, String msg, Object... args) { 69 | messager.printMessage(Diagnostic.Kind.ERROR, String.format(msg, args), e); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /compiler/src/main/java/com/flipboard/goldengate/Util.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate; 2 | 3 | import javax.lang.model.element.Element; 4 | import javax.lang.model.element.TypeElement; 5 | import javax.lang.model.type.TypeKind; 6 | import javax.lang.model.type.TypeMirror; 7 | import javax.lang.model.util.Elements; 8 | import javax.lang.model.util.Types; 9 | 10 | /** 11 | * Created by emilsjolander on 2/2/15. 12 | */ 13 | public class Util { 14 | public static boolean isCallback(Element e) { 15 | return Processor.instance.typeUtils.isSubtype( 16 | Processor.instance.typeUtils.erasure(e.asType()), 17 | Processor.instance.typeUtils.erasure(Processor.instance.elementUtils.getTypeElement(Callback.class.getCanonicalName()).asType()) 18 | ); 19 | } 20 | 21 | /** 22 | * Returns the {@link TypeMirror} for a given {@link Class}. 23 | * 24 | * Adapter from https://github.com/typetools/checker-framework/ 25 | */ 26 | public static TypeMirror typeFromClass(Types types, Elements elements, Class clazz) { 27 | if (clazz == void.class) { 28 | return types.getNoType(TypeKind.VOID); 29 | } else if (clazz.isPrimitive()) { 30 | String primitiveName = clazz.getName().toUpperCase(); 31 | TypeKind primitiveKind = TypeKind.valueOf(primitiveName); 32 | return types.getPrimitiveType(primitiveKind); 33 | } else if (clazz.isArray()) { 34 | TypeMirror componentType = typeFromClass(types, elements, clazz.getComponentType()); 35 | return types.getArrayType(componentType); 36 | } else { 37 | TypeElement element = elements.getTypeElement(clazz.getCanonicalName()); 38 | if (element == null) { 39 | throw new IllegalArgumentException("Unrecognized class: " + clazz); 40 | } 41 | return element.asType(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /compiler/src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | com.flipboard.goldengate.Processor -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flipboard/GoldenGate/1f3089b7bf5a7897abbe9f9506c2ba588e538c4c/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.9-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-apt' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | 8 | defaultConfig { 9 | applicationId "com.flipboard.goldengate.sample" 10 | minSdkVersion 14 11 | targetSdkVersion 23 12 | versionCode 1 13 | versionName "1.0" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | compileOptions { 22 | sourceCompatibility JavaVersion.VERSION_1_7 23 | targetCompatibility JavaVersion.VERSION_1_7 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(dir: 'libs', include: ['*.jar']) 29 | compile 'com.android.support:appcompat-v7:23.1.1' 30 | compile 'com.google.code.gson:gson:2.4' 31 | 32 | apt project(':compiler') 33 | compile project(':api') 34 | } 35 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/emilsjolander/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/src/main/java/com/flipboard/goldengate/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate.sample; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.util.Log; 8 | import android.webkit.WebChromeClient; 9 | import android.webkit.WebView; 10 | import android.webkit.WebViewClient; 11 | import android.widget.Toast; 12 | 13 | import com.flipboard.goldengate.Callback; 14 | 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | @SuppressLint("SetJavaScriptEnabled") 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_main); 23 | 24 | final WebView webview = (WebView) findViewById(R.id.webview); 25 | webview.getSettings().setJavaScriptEnabled(true); 26 | webview.setWebViewClient(new WebViewClient()); 27 | webview.setWebChromeClient(new WebChromeClient()); 28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 29 | WebView.setWebContentsDebuggingEnabled(true); 30 | } 31 | 32 | final SampleBridge bridge = new SampleBridge(webview); 33 | webview.loadUrl("https://flipboard.com/@news/the-daily-edition-3adc9613z"); 34 | webview.postDelayed(new Runnable() { 35 | @Override 36 | public void run() { 37 | bridge.getNavigator(new Callback() { 38 | @Override 39 | public void onResult(Navigator result) { 40 | Toast.makeText(MainActivity.this, result.platform, Toast.LENGTH_SHORT).show(); 41 | } 42 | }); 43 | bridge.getWindowWidth(new Callback() { 44 | @Override 45 | public void onResult(Float result) { 46 | Toast.makeText(MainActivity.this, "window width = " + result, Toast.LENGTH_SHORT).show(); 47 | } 48 | }); 49 | bridge.looptyLoop(new Callback() { 50 | @Override 51 | public void onResult(Integer val) { 52 | Log.d("looptyLoop", "val: " + val); 53 | } 54 | }); 55 | bridge.alert("tjena"); 56 | } 57 | }, 1000); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /sample/src/main/java/com/flipboard/goldengate/sample/Navigator.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate.sample; 2 | 3 | public class Navigator { 4 | 5 | public String userAgent; 6 | public String platform; 7 | public String vendor; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /sample/src/main/java/com/flipboard/goldengate/sample/NoCallbackSample.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate.sample; 2 | 3 | import com.flipboard.goldengate.Bridge; 4 | import com.flipboard.goldengate.Debug; 5 | 6 | /** 7 | * No callbacks necessary for this interface, so no javascript interface logic will be generated 8 | */ 9 | @Debug 10 | @Bridge 11 | public interface NoCallbackSample { 12 | 13 | void alert(String message); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /sample/src/main/java/com/flipboard/goldengate/sample/Sample.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate.sample; 2 | 3 | import com.flipboard.goldengate.Bridge; 4 | import com.flipboard.goldengate.Callback; 5 | import com.flipboard.goldengate.Debug; 6 | import com.flipboard.goldengate.JavascriptCallback; 7 | import com.flipboard.goldengate.Method; 8 | import com.flipboard.goldengate.Property; 9 | 10 | @Debug 11 | @Bridge 12 | public interface Sample { 13 | 14 | void alert(String message); 15 | 16 | @Property("window.innerWidth") 17 | void getWindowWidth(Callback width); 18 | 19 | @Property("navigator") 20 | void getNavigator(Callback navigator); 21 | 22 | @Method("[1,2,3,4,5,6,7,8,9,10].forEach") 23 | void looptyLoop(@JavascriptCallback Callback val); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /sample/src/main/java/com/flipboard/goldengate/sample/Test.java: -------------------------------------------------------------------------------- 1 | package com.flipboard.goldengate.sample; 2 | 3 | import com.flipboard.goldengate.Bridge; 4 | import com.flipboard.goldengate.Callback; 5 | import com.flipboard.goldengate.JavascriptCallback; 6 | import com.flipboard.goldengate.Method; 7 | import com.flipboard.goldengate.Property; 8 | 9 | @Bridge 10 | public interface Test { 11 | 12 | void getter(Callback callback); 13 | 14 | void getterWithParam(int param, Callback callback); 15 | 16 | void multiParamSetter(int param1, int param2); 17 | 18 | @Property 19 | void propertySetter(int param); 20 | 21 | @Property 22 | void propertyGetter(Callback callback); 23 | 24 | @Property("otherName") 25 | void propertyGetterWithChangedName(Callback callback); 26 | 27 | @Method("otherName") 28 | void methodWithChangedName(int param); 29 | 30 | @Method("otherName") 31 | void noParamMethodWithChangedName(Callback callback); 32 | 33 | void reportBackLater(@JavascriptCallback Callback callback); 34 | 35 | } -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flipboard/GoldenGate/1f3089b7bf5a7897abbe9f9506c2ba588e538c4c/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flipboard/GoldenGate/1f3089b7bf5a7897abbe9f9506c2ba588e538c4c/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flipboard/GoldenGate/1f3089b7bf5a7897abbe9f9506c2ba588e538c4c/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Flipboard/GoldenGate/1f3089b7bf5a7897abbe9f9506c2ba588e538c4c/sample/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFE12828 4 | #c32727 5 | #FF0099CC 6 | 7 | -------------------------------------------------------------------------------- /sample/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GoldenGate 5 | Settings 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':api', ':compiler' 2 | -------------------------------------------------------------------------------- /version.properties: -------------------------------------------------------------------------------- 1 | #Tue, 09 Jun 2015 14:55:56 -0700 2 | major=1 3 | minor=4 4 | patch=2 5 | --------------------------------------------------------------------------------