├── app ├── .idea │ ├── .name │ ├── copyright │ │ └── profiles_settings.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── compiler.xml ├── .gitignore ├── src │ ├── main │ │ ├── assets │ │ │ ├── fonts │ │ │ │ ├── GoodDog.otf │ │ │ │ ├── Pacifico.ttf │ │ │ │ ├── Amatic-Bold.ttf │ │ │ │ ├── Chunkfive.otf │ │ │ │ ├── AmaticSC-Regular.ttf │ │ │ │ └── GreatVibes-Regular.otf │ │ │ ├── file │ │ │ │ ├── infinit.gif │ │ │ │ ├── preload.json │ │ │ │ ├── error.json │ │ │ │ └── hello.json │ │ │ ├── agent │ │ │ └── timers │ │ ├── ic_launcher-web.png │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ ├── ids.xml │ │ │ │ ├── arrays.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ ├── java │ │ │ └── com │ │ │ │ └── jasonette │ │ │ │ └── seed │ │ │ │ ├── Helper │ │ │ │ ├── JasonSettings.java │ │ │ │ └── JasonImageHelper.java │ │ │ │ ├── Component │ │ │ │ ├── JasonSpaceComponent.java │ │ │ │ ├── JasonLabelComponent.java │ │ │ │ ├── JasonComponentFactory.java │ │ │ │ ├── JasonSliderComponent.java │ │ │ │ ├── JasonSwitchComponent.java │ │ │ │ ├── JasonButtonComponent.java │ │ │ │ ├── JasonHtmlComponent.java │ │ │ │ ├── JasonTextareaComponent.java │ │ │ │ └── JasonComponent.java │ │ │ │ ├── Action │ │ │ │ ├── JasonVisionAction.java │ │ │ │ ├── JasonLogAction.java │ │ │ │ ├── JasonAgentAction.java │ │ │ │ ├── JasonWebsocketAction.java │ │ │ │ ├── JasonCacheAction.java │ │ │ │ ├── JasonPushAction.java │ │ │ │ ├── JasonSessionAction.java │ │ │ │ ├── JasonScriptAction.java │ │ │ │ ├── JasonGlobalAction.java │ │ │ │ ├── JasonGeoAction.java │ │ │ │ ├── JasonReturnAction.java │ │ │ │ ├── JasonTimerAction.java │ │ │ │ └── JasonConvertAction.java │ │ │ │ ├── Core │ │ │ │ ├── JasonCallback.java │ │ │ │ ├── JasonParser.java │ │ │ │ └── JasonRequire.java │ │ │ │ ├── Service │ │ │ │ ├── push │ │ │ │ │ ├── JasonPushRegisterService.java │ │ │ │ │ └── JasonPushMessageService.java │ │ │ │ ├── websocket │ │ │ │ │ └── JasonWebsocketService.java │ │ │ │ └── vision │ │ │ │ │ └── JasonVisionService.java │ │ │ │ ├── Lib │ │ │ │ └── JasonToolbar.java │ │ │ │ └── Section │ │ │ │ └── JasonLayout.java │ │ └── AndroidManifest.xml │ └── debug │ │ ├── res │ │ └── values │ │ │ └── bools.xml │ │ ├── java │ │ └── com │ │ │ └── jasonette │ │ │ └── seed │ │ │ └── Launcher │ │ │ ├── DebugLauncher.java │ │ │ └── ConfigurableStethoTree.java │ │ └── AndroidManifest.xml ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── proguard-rules.pro ├── gradlew.bat ├── build.gradle └── gradlew ├── settings.gradle ├── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .codeclimate.yml ├── tools └── legacy │ ├── .codeclimate.yml │ └── CONTRIBUTING.md ├── .idea ├── runConfigurations.xml └── gradle.xml ├── .gitignore ├── gradle.properties ├── CONTRIBUTING.md ├── CHANGELOG.md ├── gradlew.bat └── gradlew /app/.idea/.name: -------------------------------------------------------------------------------- 1 | Seed -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/GoodDog.otf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Pacifico.ttf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Amatic-Bold.ttf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/Chunkfive.otf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/AmaticSC-Regular.ttf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/assets/fonts/GreatVibes-Regular.otf: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Development Moved to 2 | https://github.com/jasonelle/jasonelle/ 3 | -------------------------------------------------------------------------------- /app/.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/assets/file/infinit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/assets/file/infinit.gif -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasonette/JASONETTE-Android/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/values/bools.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | network 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Jasonette 3 | file://hello.json 4 | file://preload.json 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | fixme: 3 | enabled: true 4 | pmd: 5 | enabled: true 6 | channel: "beta" 7 | 8 | ratings: 9 | paths: 10 | - "**.java" 11 | 12 | exclude_paths: 13 | - "/.idea/" 14 | - "/app/.idea/" 15 | - "/app/src/main/assets/" 16 | -------------------------------------------------------------------------------- /tools/legacy/.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | fixme: 3 | enabled: true 4 | pmd: 5 | enabled: true 6 | channel: "beta" 7 | 8 | ratings: 9 | paths: 10 | - "**.java" 11 | 12 | exclude_paths: 13 | - "/.idea/" 14 | - "/app/.idea/" 15 | - "/app/src/main/assets/" 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 03 14:27:02 IST 2019 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-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/assets/file/preload.json: -------------------------------------------------------------------------------- 1 | { 2 | "layers": [ 3 | { 4 | "type": "image", 5 | "url": "file://infinit.gif", 6 | "style": { 7 | "width": "200", 8 | "height": "200", 9 | "top": "50%-100", 10 | "left": "50%-100" 11 | } 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /app/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Helper/JasonSettings.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Helper; 2 | 3 | import android.content.Context; 4 | 5 | import com.jasonette.seed.R; 6 | 7 | public class JasonSettings { 8 | public static boolean isAsync(String className, Context context) { 9 | String[]asyncActions = context.getResources().getStringArray(R.array.asyncActions); 10 | for(String s: asyncActions){ 11 | if(s.equalsIgnoreCase(className)) return true; 12 | } 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /captures 3 | .externalNativeBuild 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Gradle generated files 9 | .gradle/ 10 | 11 | # Signing files 12 | .signing/ 13 | 14 | # User-specific configurations 15 | .idea/* 16 | .idea/libraries/ 17 | .idea/workspace.xml 18 | .idea/tasks.xml 19 | .idea/.name 20 | .idea/compiler.xml 21 | .idea/copyright/profiles_settings.xml 22 | .idea/encodings.xml 23 | .idea/misc.xml 24 | .idea/modules.xml 25 | .idea/scopes/scope_settings.xml 26 | .idea/vcs.xml 27 | .idea/codeStyleSettings.xml 28 | *.iml 29 | 30 | # OS-specific files 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | app/app-release.apk 39 | -------------------------------------------------------------------------------- /app/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/Unknower/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 | -------------------------------------------------------------------------------- /app/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | android.useAndroidX=true 14 | android.enableJetifier=true 15 | 16 | # When configured, Gradle will run in incubating parallel mode. 17 | # This option should only be used with decoupled projects. More details, visit 18 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 19 | # org.gradle.parallel=true 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonSpaceComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.View; 6 | import org.json.JSONObject; 7 | 8 | public class JasonSpaceComponent { 9 | 10 | public static View build(View view, final JSONObject component, final JSONObject parent, Context context) { 11 | if(view == null) { 12 | return new View(context); 13 | } else { 14 | try { 15 | JasonComponent.build(view, component, parent, context); 16 | view.requestLayout(); 17 | return view; 18 | } catch (Exception e) { 19 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 20 | return new View(context); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonVisionAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | 5 | import com.jasonette.seed.Core.JasonViewActivity; 6 | import com.jasonette.seed.Helper.JasonHelper; 7 | 8 | import org.json.JSONObject; 9 | 10 | 11 | public class JasonVisionAction { 12 | 13 | /** 14 | * { 15 | * "type": "$vision.scan" 16 | * } 17 | * 18 | * Scans code specified in 19 | * https://developer.apple.com/documentation/avfoundation/avmetadataobjecttype?language=objc for iOS 20 | * https://developers.google.com/vision/android/barcodes-overview for Android 21 | */ 22 | 23 | public void scan(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 24 | ((JasonViewActivity) context).cameraManager.is_open = true; 25 | JasonHelper.next("success", action, data, event, context); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Core/JasonCallback.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Core; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | import com.jasonette.seed.Helper.JasonHelper; 8 | 9 | import org.json.JSONObject; 10 | 11 | public class JasonCallback { 12 | public void href(Intent intent, final JSONObject options) { 13 | try { 14 | JSONObject action = options.getJSONObject("action"); 15 | JSONObject event = options.getJSONObject("event"); 16 | Context context = (Context)options.get("context"); 17 | 18 | String return_string = intent.getStringExtra("return"); 19 | JSONObject return_value = new JSONObject(return_string); 20 | JasonHelper.next("success", action, return_value, event, context); 21 | 22 | } catch (Exception e) { 23 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Service/push/JasonPushRegisterService.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Service.push; 2 | 3 | import android.util.Log; 4 | 5 | import com.jasonette.seed.Core.JasonViewActivity; 6 | import com.jasonette.seed.Launcher.Launcher; 7 | import com.google.firebase.messaging.FirebaseMessagingService; 8 | 9 | import org.json.JSONObject; 10 | 11 | public class JasonPushRegisterService extends FirebaseMessagingService { 12 | @Override 13 | public void onNewToken(String refreshedToken) { 14 | super.onNewToken(refreshedToken); 15 | if (refreshedToken != null) { 16 | try { 17 | JSONObject payload = new JSONObject(); 18 | payload.put("token", refreshedToken); 19 | JSONObject response = new JSONObject(); 20 | response.put("$jason", payload); 21 | ((JasonViewActivity) Launcher.getCurrentContext()).simple_trigger("$push.onregister", response, Launcher.getCurrentContext()); 22 | } catch (Exception e) { 23 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/assets/agent: -------------------------------------------------------------------------------- 1 | $agent={ 2 | callbacks: {}, 3 | 4 | interface: {}, 5 | 6 | /* Make requests to another agent */ 7 | request: function(rpc, callback) { 8 | 9 | /* set nonce to only respond to the return value I requested for */ 10 | var nonce = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15); 11 | 12 | $agent.callbacks[nonce] = function(data) { 13 | /* Execute the callback */ 14 | callback(data); 15 | 16 | /* Delete itself to free up memory */ 17 | delete $agent.callbacks[nonce]; 18 | }; 19 | 20 | /* send message */ 21 | $agent.interface.postMessage({ 22 | request: { data: rpc, nonce: nonce } 23 | }); 24 | 25 | }, 26 | 27 | /* Return response to Jasonette or the caller agent */ 28 | response: function(data) { 29 | $agent.interface.postMessage({ 30 | response: { data: data } 31 | }); 32 | }, 33 | 34 | /* One way event fireoff to Jasonette */ 35 | trigger: function(event, options) { 36 | $agent.interface.postMessage({ 37 | trigger: { name: event, data: options } 38 | }); 39 | }, 40 | 41 | /* Trigger Jasonette href */ 42 | href: function(href) { 43 | $agent.interface.postMessage({ 44 | href: { data: href } 45 | }); 46 | } 47 | }; 48 | 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute to Jasonelle Android 2 | 3 | Please create an issue in [Jasonelle/Jasonelle](https://github.com/jasonelle/jasonelle) before making a pull request in order to talk about 4 | how to approach a solution. 5 | 6 | ## Maintainers 7 | 8 | - [@Panterozo](https://github.com/panterozo): (current | main) 2019 - present. 9 | 10 | - [@CLSource](https://github.com/clsource): (current | colaborator) 2018 - present. 11 | 12 | - [@gliechtenstein](https://github.com/gliechtenstein): (retired | creator) 2016 - 2018. 13 | 14 | ## Branches 15 | 16 | This repo contains the following branches. 17 | 18 | ### `master` 19 | 20 | Stores code that can be used. Is updated once in a while with the 21 | code in `develop`. 22 | 23 | ### `develop` 24 | 25 | This branch contains **bleeding edge** code. May break the build. 26 | Is merged to master when enough changes (not more than two weeks of work) are made and compiles successfully. 27 | 28 | - Only code in the `develop` branch can be merged into master. 29 | 30 | - All commits must be merged. No rebasing or squashing. 31 | 32 | - Other branches must be deleted or archived when it's purpose is met. 33 | 34 | - `uncrustify` (code style standarization) should be applied before merging into `master`. 35 | 36 | ## Releases 37 | 38 | *Jasonelle* will release a new version every six months (June 9 and November 6) in the repository [Jasonelle/Jasonelle](https://github.com/jasonelle/jasonelle). 39 | 40 | - A release will contain the master branch of each project (Android, iOS, tools). The code in master must be tagged with the release version. 41 | 42 | - The version number would be discussed with all the team leaders before hand. 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonLogAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import com.jasonette.seed.Helper.JasonHelper; 6 | import org.json.JSONObject; 7 | 8 | public class JasonLogAction { 9 | public void info(final JSONObject action, JSONObject data, final JSONObject event, Context context) { 10 | log(action, data, event, context, "i"); 11 | } 12 | public void debug(final JSONObject action, JSONObject data, final JSONObject event, Context context) { 13 | log(action, data, event, context, "d"); 14 | } 15 | public void error(final JSONObject action, JSONObject data, final JSONObject event, Context context) { 16 | log(action, data, event, context, "e"); 17 | } 18 | 19 | private void log(final JSONObject action, JSONObject data, JSONObject event, Context context, String mode) { 20 | try { 21 | if (action.has("options")) { 22 | JSONObject options = action.getJSONObject("options"); 23 | if (options.has("text")) { 24 | if(mode.equalsIgnoreCase("i")){ 25 | Log.i("Log", options.getString("text")); 26 | } else if(mode.equalsIgnoreCase("d")){ 27 | Log.d("Log", options.getString("text")); 28 | } else if(mode.equalsIgnoreCase("e")){ 29 | Log.e("Log", options.getString("text")); 30 | } 31 | } 32 | } 33 | JasonHelper.next("success", action, data, event, context); 34 | } catch (Exception e){ 35 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Service/push/JasonPushMessageService.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Service.push; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.firebase.messaging.FirebaseMessagingService; 6 | import com.google.firebase.messaging.RemoteMessage; 7 | import com.jasonette.seed.Core.JasonViewActivity; 8 | import com.jasonette.seed.Launcher.Launcher; 9 | 10 | import org.json.JSONArray; 11 | import org.json.JSONObject; 12 | 13 | import java.util.Map; 14 | 15 | public class JasonPushMessageService extends FirebaseMessagingService { 16 | 17 | public JasonPushMessageService() { 18 | } 19 | 20 | @Override 21 | public void onMessageReceived(RemoteMessage remoteMessage) { 22 | if (remoteMessage.getData().size() > 0) { 23 | Map json = remoteMessage.getData(); 24 | JSONObject payload = new JSONObject(); 25 | JSONObject response = new JSONObject(); 26 | try { 27 | for (Map.Entry entry : json.entrySet()) 28 | { 29 | // Detect if the result is JSONObject, JSONArray, or String 30 | String val = entry.getValue().trim(); 31 | if (val.startsWith("[")) { 32 | payload.put(entry.getKey(), new JSONArray(val)); 33 | } else if (val.startsWith("{")) { 34 | payload.put(entry.getKey(), new JSONObject(val)); 35 | } else { 36 | payload.put(entry.getKey(), val); 37 | } 38 | } 39 | response.put("$jason", payload); 40 | ((JasonViewActivity)Launcher.getCurrentContext()).simple_trigger("$push.onmessage", response, Launcher.getCurrentContext()); 41 | } catch (Exception e) { 42 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 43 | } 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/debug/java/com/jasonette/seed/Launcher/DebugLauncher.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Launcher; 2 | 3 | import android.content.res.Resources; 4 | import android.util.Log; 5 | import com.facebook.stetho.Stetho; 6 | import com.facebook.stetho.okhttp3.StethoInterceptor; 7 | import com.jasonette.seed.R; 8 | import java.util.concurrent.TimeUnit; 9 | import okhttp3.OkHttpClient; 10 | import timber.log.Timber; 11 | 12 | /** 13 | * Provides debug-build specific Application. 14 | * 15 | * To disable Stetho console logging change the setting in src/debug/res/values/bools.xml 16 | */ 17 | public class DebugLauncher extends Launcher { 18 | 19 | private static final String LOGTAG = DebugLauncher.class.getSimpleName(); 20 | 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | 25 | Stetho.initializeWithDefaults(this); 26 | Resources res = getResources(); 27 | boolean enableStethoConsole = res.getBoolean(R.bool.enableStethoConsole); 28 | 29 | if (enableStethoConsole) { 30 | Timber.plant(new ConfigurableStethoTree(new ConfigurableStethoTree.Configuration.Builder() 31 | .showTags(true) 32 | .minimumPriority(Log.DEBUG) 33 | .build())); 34 | Log.i(LOGTAG, "Using Stetho console logging"); 35 | } else { 36 | Timber.plant(new Timber.DebugTree()); 37 | } 38 | Timber.i("Initialised Stetho debugging"+getEnv()); 39 | } 40 | 41 | @Override 42 | public OkHttpClient getHttpClient(long timeout) { 43 | if(timeout > 0) { 44 | return new OkHttpClient.Builder() 45 | .readTimeout(timeout, TimeUnit.SECONDS) 46 | .writeTimeout(timeout, TimeUnit.SECONDS) 47 | .addNetworkInterceptor(new StethoInterceptor()) 48 | .build(); 49 | } else { 50 | return new OkHttpClient.Builder() 51 | .addNetworkInterceptor(new StethoInterceptor()) 52 | .build(); 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonAgentAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.jasonette.seed.Launcher.Launcher; 7 | import com.jasonette.seed.Service.agent.JasonAgentService; 8 | 9 | import org.json.JSONObject; 10 | 11 | public class JasonAgentAction { 12 | public void request(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 13 | try { 14 | ((Launcher)context.getApplicationContext()).call("JasonAgentService", "jason_request", action, context); 15 | } catch (Exception e) { 16 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 17 | } 18 | } 19 | public void refresh(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 20 | try { 21 | JasonAgentService agentService = (JasonAgentService)((Launcher)context.getApplicationContext()).services.get("JasonAgentService"); 22 | agentService.refresh(action, context); 23 | } catch (Exception e) { 24 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 25 | } 26 | } 27 | public void clear(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 28 | try { 29 | JasonAgentService agentService = (JasonAgentService)((Launcher)context.getApplicationContext()).services.get("JasonAgentService"); 30 | agentService.clear(action, context); 31 | } catch (Exception e) { 32 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 33 | } 34 | } 35 | public void inject(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 36 | try { 37 | JasonAgentService agentService = (JasonAgentService)((Launcher)context.getApplicationContext()).services.get("JasonAgentService"); 38 | agentService.inject(action, context); 39 | } catch (Exception e) { 40 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonWebsocketAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | 5 | import com.jasonette.seed.Core.JasonViewActivity; 6 | import com.jasonette.seed.Helper.JasonHelper; 7 | import com.jasonette.seed.Launcher.Launcher; 8 | 9 | import org.json.JSONObject; 10 | 11 | /***************************************** 12 | 13 | ### Actions: 14 | - There are 3 actions: Open, Close, Send 15 | - All actions are asynchronous => They don't wait for a response and immediately calls "success" 16 | - Instead of a return value, all of these actions trigger a service. 17 | - The corresponding service (can be seen at JasonWebsocketService) emits an event when there's a result. 18 | 19 | [1] Open 20 | { 21 | "type": "$websocket.open", 22 | "options": { 23 | "url": "..." 24 | }, 25 | "success": { ... } 26 | } 27 | 28 | [2] Close 29 | { 30 | "type": "$websocket.close", 31 | "success": { ... } 32 | } 33 | 34 | [3] Send 35 | { 36 | "type": "$websocket.send", 37 | "options": { 38 | "message": "..." 39 | }, 40 | "success": { ... } 41 | } 42 | 43 | *****************************************/ 44 | 45 | public class JasonWebsocketAction { 46 | public void open(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 47 | ((Launcher)context.getApplicationContext()).call("JasonWebsocketService", "open", action, context); 48 | JasonHelper.next("success", action, new JSONObject(), event, context); 49 | } 50 | public void close(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 51 | ((Launcher)context.getApplicationContext()).call("JasonWebsocketService", "close", action, context); 52 | JasonHelper.next("success", action, new JSONObject(), event, context); 53 | } 54 | public void send(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 55 | ((Launcher)context.getApplicationContext()).call("JasonWebsocketService", "send", action, context); 56 | JasonHelper.next("success", action, new JSONObject(), event, context); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Sentimental Versioning](http://sentimentalversioning.org/). 6 | 7 | ## [3.0.0] Next Release 8 | 9 | This version is the current in development. Will be released after the week of *6th November 2020*. 10 | 11 | ## [2.0.0](https://github.com/jasonelle/jasonelle/releases/tag/v2.0) 12 | 13 | This version was released in November 2019. 14 | 15 | ### Added 16 | 17 | - Hjson reading capability 18 | 19 | - Upgraded to AndroidX and better performance for Glide implementation 20 | 21 | - onBackPressed avoiding closing the app on Back button press 22 | 23 | ### Changed 24 | 25 | - Complete implementation for Calendar Demo 26 | 27 | - Minimum Android version bumped to `6.0`. 28 | 29 | - Changed from `QR_CODE` to `ALL_FORMATS` as suggested by `Mike Metcalfe`. [Docs](https://developers.google.com/android/reference/com/google/android/gms/vision/barcode/Barcode.html#ALL_FORMATS). With this addition now the android app can scan any barcode supported by the vision engine. 30 | 31 | ### Fixed 32 | 33 | - Fixed issue when accessing Geolocation data. (Need to ask permissions). 34 | 35 | - Fixed `build.gradle` for Android Studio `v3.2.1`. By [@CydeSwype](https://github.com/Jasonette/JASONETTE-Android/commits?author=CydeSwype). 36 | 37 | - Fixed text zoom issue in `Webview` by [@naei](https://github.com/naei). 38 | 39 | ### Updated 40 | 41 | ### Removed 42 | 43 | ### Notes 44 | 45 | This version is a complete overhaul focusing on 46 | modularization of the code and update of the libraries, improving the quality of the framework, maintaining the same json api. 47 | 48 | ### People 49 | 50 | Huge thanks to the following persons that helped in this release: 51 | 52 | - [Moises Portillo](https://github.com/moizest89): Helped with some guidance over the GPS permission issue. 53 | 54 | - [Adán Miranda](https://github.com/takakeiji): Helped compiling *Jason* App APK. 55 | 56 | - [Devs Chile](https://devschile.cl): Chilean commmunity of developers. 57 | 58 | More people here [https://jasonelle.com/docs/#/folks](https://jasonelle.com/docs/#/folks). 59 | 60 | ## [1.0](https://github.com/jasonelle/jasonelle/releases/tag/v1.0) 61 | 62 | First version of the *Jasonette* Mobile Framework. 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonCacheAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.util.Log; 6 | 7 | import com.jasonette.seed.Helper.JasonHelper; 8 | import com.jasonette.seed.Core.JasonViewActivity; 9 | import org.json.JSONObject; 10 | 11 | public class JasonCacheAction { 12 | public void set(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 13 | try { 14 | JasonViewActivity activity = (JasonViewActivity) context; 15 | SharedPreferences pref = context.getSharedPreferences("cache", 0); 16 | SharedPreferences.Editor editor = pref.edit(); 17 | 18 | // Merge with the new input 19 | JSONObject options = action.getJSONObject("options"); 20 | JSONObject old_cache = new JSONObject(pref.getString(activity.url, "{}")); 21 | JSONObject new_cache = JasonHelper.merge(old_cache, options); 22 | 23 | // Update SharedPreferences 24 | String stringified_cache = new_cache.toString(); 25 | editor.putString(activity.url, stringified_cache); 26 | editor.commit(); 27 | 28 | // Update model 29 | ((JasonViewActivity)context).model.cache = new_cache; 30 | 31 | // Execute next 32 | JasonHelper.next("success", action, new_cache, event, context); 33 | 34 | } catch (Exception e){ 35 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 36 | } 37 | 38 | 39 | } 40 | public void reset(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 41 | try { 42 | // Update SharedPreferences 43 | JasonViewActivity activity = (JasonViewActivity) context; 44 | SharedPreferences pref = context.getSharedPreferences("cache", 0); 45 | SharedPreferences.Editor editor = pref.edit(); 46 | editor.remove(activity.url); 47 | editor.commit(); 48 | 49 | // Update model 50 | ((JasonViewActivity)context).model.cache = new JSONObject(); 51 | 52 | // Execute next 53 | JasonHelper.next("success", action, new JSONObject(), event, context); 54 | 55 | } catch (Exception e){ 56 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/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 | -------------------------------------------------------------------------------- /app/src/main/assets/file/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "$jason": { 3 | "head": { 4 | "title": "Error Loading Screen" 5 | }, 6 | "body": { 7 | "layers": [ 8 | { 9 | "type": "label", 10 | "text": "Offline", 11 | "style": { 12 | "top": "50%-80", 13 | "align": "center", 14 | "font": "AvenirNext-Regular", 15 | "size": "20", 16 | "left": "50%-50", 17 | "width": "100" 18 | } 19 | }, 20 | { 21 | "type": "image", 22 | "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAGDUlEQVR4Xu2d7ZUNQRCGayNABIgAESACRIAIEIHdCBABGwEiQASIgI3AioDzrjtOnz41019V1dU7PX/23DvTX/VMdVdX1e09onm5ksCRq97MztAE4uwlmEAmEGcScNadqSETiIoErh5qPVep3bDSy6AhT4jo7UFmT4nonaH8xJsaHcgNIvoRSeUmEf0Ul5RRhaMDeU9EDyNZfSCiR0byE29mZCD3iOjTikTuE9FncWkZVDgyEExVmLK46xsR3TGQn3gTowJ5TkSvEtJ4QUSvxSWmXOGIQGDiQjsWUxciOj3I6XEgL5jAWOCHMoVHBIK3/lkg+N9EdPvwGVPVleDeGyKCNg1zjQaEM3NPiOj4IHH8fTmyGTwaEFhVsK6W6yxa2DGNQUuuB8/A2oLVNcQ1EhDsN7DvCC/sN7DvCK/c51wCGglIbOZ+ibQlFDC04m7wBXbuWODdX6MA4dYG7DMwPXEXFvmv0Y1wrXELZgQga2YunIpbF5yMw5nBIwCJBQszF9ZWan8BkJiqQjMY+5UUyK7a4x0IN/WU7MBLp7quMNC4dyApMzdHgNCSYcxgz0DCwNMi+BovLucVdhvI8goE8z+spNCbu2XmpjSFM4NhpaXWoVS94ve9ApF2gaRcLuKCra3QIxAID9oRenMlnISxUxLaAS1xFe71CCQOy+aauamXcggz2BsQ7QWYC2zVGAop+NX3vQHBVLXENjCo79Hn6oEGBeFuuRV9dhPu9QREysxNQdPWwlT7m/e9AOH8VR+ZFJ+mwQaF4bJ/EHx2E+71AkTazE2Bc2sGewDSSzjWL0HqJbm47wEI56/Cwq69i+bM4O7h3t5Aei+wVoZElnZ40JCSsCw3KEx3oScXzyDxoWT37Src21NDJDZpvyIXC4BgqruW/Ur+y2KJc4RLYi4FTaUf7QWkNiwbj+jPyhBLx+Um3Fva8TTivCfWsg9Lphq0JAUEU5+LrMceQCQzQqSAAK6LcG8PIBJh2UUPJYG4yHq0BiKdVSgJBJCl+5c3gQdPWQNpNXO1FvWw3q5msCUQjTlaWkMARnKNc6shXFhWImlNAwiE2M0MttKQ2uzD1BumBaRbuNcCiOZOWAvImhmsHu61ACJp5los6mEb5lmP2kC0vamaGgIw5t5oTSDS2YfceqINBG2aZj1qArGIyFkAMY1oagHhBiGRfWi9hiztxc5QfK9yyI0WEK3sw15AODNY5ZAbDSCWC6HFlLW8BBIBtdS+SiXJwSL7UMPbmxTWIWaimvUorSHaZm6vKWtpV137JYFYZx9KRgxztGN5RjXrURKImSUSSM9yDVmaVbUgpYCY2uqdgaz5uUTMYCkgvbIPe2gIgKhlPUoAUV/oNib4XkDQJRUDRgKIdFi2ZIHtCWTNz9V0yE0rEM5fpR4zcLCGbJnBTVmPLUCksg9LNKL3PoTrq2i4twUIF5ZFgkBp9uHoQLisx+p8gVogXTMzAoJIrA5P+8Et/Iw6/I17C/DcsmIZNbVANMOyuULAc3g748OUoaGWWrqYwSJnPdYA6Z7dV0LM8FkRuZQCsQjLGspQvKnmrMdSIGJzpbgofFTYvLaWANHKPvQhSrlecGZw9iE3JUC0sg/lROGjpqasx1wgmtmHtWLEwPEbQ+7Cbwy1f1a91e9qD0YuEC9mbu5O3dJ9swamKusxB4iKV7NWLaJya85FD0CqvOApIJy/quXsQyEO/6vxDASdjM3g5CE3KSAW2YctkLwDKY6kbgFRjR23UAjKegeCrhblGmwBsco+bGGz/COXuI6171vaqi1blPW4BqRqQart8Q7KZRtGa0Assw93wONiiFlnPXJATHJY90IhGGfW5joG0iP7cE9sklmPMZAii2BPkhQaa9JyDYEU28xCndxbNZt7uxBIr+zDvQHZPORmAcKFH93+j41LQHDVDF6A9Mw+vATyrRoCG+4FEG5Oq2phFmqWwAmAcAdJNtc8K6iSwDmArDnoqmqchZokcAYgWGCw/4gzAJtqnoWLJYDzho9T8ZDiWmeBNglMIG3yEy89gYiLtK3CCaRNfuKlJxBxkbZVOIG0yU+89F/0oJEjHJ/peAAAAABJRU5ErkJggg==", 23 | "style": { 24 | "width": "100", 25 | "top": "50%-50", 26 | "left": "50%-50" 27 | } 28 | } 29 | ] 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | 31 | 39 | 40 | 44 | 45 | 46 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonPushAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.google.android.gms.tasks.OnCompleteListener; 7 | import com.google.android.gms.tasks.Task; 8 | import com.google.firebase.iid.FirebaseInstanceId; 9 | import com.google.firebase.iid.InstanceIdResult; 10 | import com.jasonette.seed.Core.JasonViewActivity; 11 | import com.jasonette.seed.Helper.JasonHelper; 12 | import com.jasonette.seed.Launcher.Launcher; 13 | 14 | import org.json.JSONObject; 15 | 16 | import androidx.annotation.NonNull; 17 | 18 | // Unlike APN which requires manual registration, FCM automatically registers push upon app launch. 19 | // As a result, $push.register can behave in two different ways: 20 | // 1. If the device is already registered, it immediately triggers $push.onregister event. 21 | // 2. If the device is NOT yet registered, it doesn't do anything (the $push.onregister event will be auto-triggered by JasonPushRegisterService instead) 22 | 23 | public class JasonPushAction { 24 | public void register(final JSONObject action, JSONObject data, final JSONObject event, Context context) { 25 | 26 | FirebaseInstanceId.getInstance().getInstanceId() 27 | .addOnCompleteListener(new OnCompleteListener() { 28 | @Override 29 | public void onComplete(@NonNull Task task) { 30 | if (!task.isSuccessful()) { 31 | Log.w("Instance", "getInstanceId failed", task.getException()); 32 | return; 33 | } 34 | // Get new Instance ID token 35 | String refreshedToken = task.getResult().getToken(); 36 | if(refreshedToken != null){ 37 | // Token exists => already registered => Immediately trigger $push.onregister 38 | try { 39 | JSONObject response = new JSONObject(); 40 | JSONObject payload = new JSONObject(); 41 | payload.put("token", refreshedToken); 42 | response.put("$jason", payload); 43 | ((JasonViewActivity)Launcher.getCurrentContext()).simple_trigger("$push.onregister", response, Launcher.getCurrentContext()); 44 | } catch (Exception e) { 45 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 46 | } 47 | } else { 48 | // Token doesn't exist => ignore => JasonPushRegisterService will take care of $push.onregister 49 | } 50 | } 51 | }); 52 | 53 | 54 | JasonHelper.next("success", action, data, event, context); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonLabelComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.util.Log; 6 | import android.view.Gravity; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | import com.jasonette.seed.Helper.JasonHelper; 10 | import org.json.JSONObject; 11 | 12 | public class JasonLabelComponent { 13 | 14 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context context) { 15 | if(view == null){ 16 | return new TextView(context); 17 | } else { 18 | try { 19 | ((TextView)view).setText(component.getString("text")); 20 | JasonComponent.build(view, component, parent, context); 21 | 22 | String type; 23 | JSONObject style = JasonHelper.style(component, context); 24 | type = component.getString("type"); 25 | 26 | if (style.has("color")) { 27 | int color = JasonHelper.parse_color(style.getString("color")); 28 | ((TextView)view).setTextColor(color); 29 | } 30 | 31 | JasonHelper.setTextViewFont(((TextView)view), style, context); 32 | 33 | int g = 0; 34 | if (style.has("align")) { 35 | String align = style.getString("align"); 36 | if (align.equalsIgnoreCase("center")) { 37 | g = g | Gravity.CENTER_HORIZONTAL; 38 | ((TextView) view).setGravity(Gravity.CENTER_HORIZONTAL); 39 | } else if (align.equalsIgnoreCase("right")) { 40 | g = g | Gravity.RIGHT; 41 | ((TextView) view).setGravity(Gravity.RIGHT); 42 | } else if (align.equalsIgnoreCase("left")) { 43 | g = g | Gravity.LEFT; 44 | } 45 | 46 | if (align.equalsIgnoreCase("top")) { 47 | g = g | Gravity.TOP; 48 | } else if (align.equalsIgnoreCase("bottom")) { 49 | g = g | Gravity.BOTTOM; 50 | } else { 51 | g = g | Gravity.CENTER_VERTICAL; 52 | } 53 | } else { 54 | g = Gravity.CENTER_VERTICAL; 55 | } 56 | 57 | ((TextView)view).setGravity(g); 58 | 59 | 60 | if (style.has("size")) { 61 | ((TextView)view).setTextSize(Float.parseFloat(style.getString("size"))); 62 | } 63 | 64 | ((TextView)view).setHorizontallyScrolling(false); 65 | 66 | JasonComponent.addListener(view, context); 67 | 68 | view.requestLayout(); 69 | return view; 70 | 71 | } catch (Exception e){ 72 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 73 | return new View(context); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonComponentFactory.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.Gravity; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.jasonette.seed.Core.JasonViewActivity; 10 | 11 | import org.json.JSONObject; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class JasonComponentFactory { 16 | Map signature_to_type = new HashMap(); 17 | public static View build(View prototype, final JSONObject component, final JSONObject parent, final Context context) { 18 | try{ 19 | String type; 20 | type = component.getString("type"); 21 | 22 | View view; 23 | if(type.equalsIgnoreCase("label")){ 24 | view = JasonLabelComponent.build(prototype, component, parent, context); 25 | } else if(type.equalsIgnoreCase("image")) { 26 | view = JasonImageComponent.build(prototype, component, parent, context); 27 | } else if(type.equalsIgnoreCase("button")) { 28 | view = JasonButtonComponent.build(prototype, component, parent, context); 29 | } else if(type.equalsIgnoreCase("space")) { 30 | view = JasonSpaceComponent.build(prototype, component, parent, context); 31 | } else if(type.equalsIgnoreCase("textfield")) { 32 | view = JasonTextfieldComponent.build(prototype, component, parent, context); 33 | } else if(type.equalsIgnoreCase("textarea")) { 34 | view = JasonTextareaComponent.build(prototype, component, parent, context); 35 | } else if(type.equalsIgnoreCase("html")) { 36 | view = JasonHtmlComponent.build(prototype, component, parent, context); 37 | } else if(type.equalsIgnoreCase("map")) { 38 | view = JasonMapComponent.build(prototype, component, parent, context); 39 | } else if(type.equalsIgnoreCase("slider")) { 40 | view = JasonSliderComponent.build(prototype, component, parent, context); 41 | } else if(type.equalsIgnoreCase("switch")) { 42 | view = JasonSwitchComponent.build(prototype, component, parent, context); 43 | } else { 44 | // Non-existent component warning 45 | JSONObject error_component = new JSONObject(component.toString()); 46 | error_component.put("type", "label"); 47 | error_component.put("text", "$"+component.getString("type")+"\n(not implemented yet)"); 48 | view = JasonLabelComponent.build(prototype, error_component, parent, context); 49 | ((TextView)view).setGravity(Gravity.CENTER); 50 | } 51 | 52 | // Focus textfield/textarea 53 | if (component.has("focus")) { 54 | ((JasonViewActivity)context).focusView = view; 55 | } 56 | 57 | return view; 58 | 59 | } 60 | catch (Exception e){ 61 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 62 | } 63 | 64 | return new View(context); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { url 'https://plugins.gradle.org/m2/' } 4 | } 5 | } 6 | 7 | repositories { 8 | maven { url 'https://maven.google.com' } 9 | } 10 | 11 | apply plugin: 'com.android.application' 12 | 13 | android { 14 | compileSdkVersion 28 15 | buildToolsVersion '28.0.3' 16 | defaultConfig { 17 | applicationId "com.jasonette" 18 | minSdkVersion 21 19 | targetSdkVersion 28 20 | versionCode 3 21 | versionName "0.1.0" 22 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 23 | multiDexEnabled true 24 | } 25 | buildTypes { 26 | release { 27 | debuggable false 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | lintOptions { 33 | disable "ResourceType" 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation fileTree(include: ['*.jar'], dir: 'libs') 39 | androidTestImplementation('androidx.test.espresso:espresso-core:3.2.0', { 40 | exclude group: 'com.android.support', module: 'support-annotations' 41 | }) 42 | implementation 'com.google.firebase:firebase-messaging:20.0.0' 43 | debugImplementation 'com.facebook.stetho:stetho:1.5.1' 44 | debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' 45 | debugImplementation 'com.facebook.stetho:stetho-timber:1.5.1' 46 | //noinspection GradleCompatible 47 | implementation 'androidx.appcompat:appcompat:1.1.0' 48 | implementation 'androidx.appcompat:appcompat:1.1.0' 49 | implementation 'com.squareup.okhttp3:okhttp:4.2.0' 50 | implementation 'com.github.bumptech.glide:glide:4.10.0' 51 | implementation 'jp.wasabeef:glide-transformations:2.0.1' 52 | implementation 'com.eclipsesource.j2v8:j2v8:5.0.103@aar' 53 | //implementation 'com.android.support:recyclerview-v7:27.1.1' 54 | implementation 'androidx.recyclerview:recyclerview:1.0.0' 55 | //implementation 'com.android.support:design:27.1.1' 56 | implementation 'com.google.android.material:material:1.0.0' 57 | implementation 'com.aurelhubert:ahbottomnavigation:2.0.2' 58 | //implementation 'com.android.support:support-core-ui:27.1.1' 59 | implementation 'androidx.legacy:legacy-support-core-ui:1.0.0' 60 | implementation 'commons-lang:commons-lang:20030203.000129' 61 | implementation 'com.github.scribejava:scribejava-apis:6.8.1' 62 | implementation 'com.yqritc:recyclerview-flexibledivider:1.4.0' 63 | implementation 'com.commonsware.cwac:cam2:0.7.4' 64 | implementation 'com.github.maks:AndroidAudioRecorder:0.3.0-jasonette' 65 | implementation 'com.github.florent37:singledateandtimepicker:1.0.8' 66 | implementation 'com.jakewharton.timber:timber:4.7.1' 67 | //noinspection UseOfBundledGooglePlayServices 68 | //implementation 'com.android.support:multidex:1.0.3' 69 | implementation 'androidx.multidex:multidex:2.0.1' 70 | implementation 'com.android.volley:volley:1.1.1' 71 | implementation 'com.facebook.stetho:stetho:1.5.1' 72 | implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1' 73 | implementation 'com.google.android.gms:play-services-vision:18.0.0' 74 | implementation 'com.google.android.gms:play-services-maps:17.0.0' 75 | 76 | implementation 'org.hjson:hjson:3.0.0' 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonSessionAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.util.Log; 6 | import com.jasonette.seed.Helper.JasonHelper; 7 | import org.json.JSONObject; 8 | import java.net.URI; 9 | 10 | public class JasonSessionAction { 11 | public void set(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 12 | try { 13 | JSONObject options = action.getJSONObject("options"); 14 | String domain; 15 | if(options.has("domain")){ 16 | String urlString = options.getString("domain"); 17 | if(!urlString.startsWith("http")){ 18 | urlString = "https://" + urlString; 19 | } 20 | URI uri = new URI(urlString); 21 | domain = uri.getHost().toLowerCase(); 22 | }else if(options.has("url")){ 23 | String urlString = options.getString("url"); 24 | if(!urlString.startsWith("http")){ 25 | urlString = "https://" + urlString; 26 | } 27 | URI uri = new URI(urlString); 28 | domain = uri.getHost().toLowerCase(); 29 | } else { 30 | return; 31 | } 32 | 33 | // store either header or body under the domain name 34 | SharedPreferences pref = context.getSharedPreferences("session", 0); 35 | SharedPreferences.Editor editor = pref.edit(); 36 | 37 | // Stringify object first 38 | String stringified_session = options.toString(); 39 | editor.putString(domain, stringified_session); 40 | editor.commit(); 41 | 42 | JasonHelper.next("success", action, new JSONObject(), event, context); 43 | 44 | } catch (Exception e){ 45 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 46 | } 47 | 48 | 49 | } 50 | public void reset(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 51 | try { 52 | JSONObject options = action.getJSONObject("options"); 53 | String domain; 54 | if(options.has("domain")){ 55 | String urlString = options.getString("domain"); 56 | if(!urlString.startsWith("http")){ 57 | urlString = "https://" + urlString; 58 | } 59 | URI uri = new URI(urlString); 60 | domain = uri.getHost().toLowerCase(); 61 | }else if(options.has("url")){ 62 | String urlString = options.getString("url"); 63 | if(!urlString.startsWith("http")){ 64 | urlString = "https://" + urlString; 65 | } 66 | URI uri = new URI(urlString); 67 | domain = uri.getHost().toLowerCase(); 68 | } else { 69 | return; 70 | } 71 | 72 | SharedPreferences pref = context.getSharedPreferences("session", 0); 73 | SharedPreferences.Editor editor = pref.edit(); 74 | editor.remove(domain); 75 | editor.commit(); 76 | 77 | JasonHelper.next("success", action, new JSONObject(), event, context); 78 | 79 | } catch (Exception e){ 80 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonScriptAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.jasonette.seed.Core.JasonParser; 7 | import com.jasonette.seed.Core.JasonRequire; 8 | import com.jasonette.seed.Helper.JasonHelper; 9 | import com.jasonette.seed.Launcher.Launcher; 10 | 11 | import org.json.JSONArray; 12 | import org.json.JSONObject; 13 | 14 | import java.util.Iterator; 15 | import java.util.concurrent.CountDownLatch; 16 | import java.util.concurrent.ExecutorService; 17 | import java.util.concurrent.Executors; 18 | 19 | import okhttp3.OkHttpClient; 20 | 21 | /** 22 | * Created by e on 9/14/17. 23 | */ 24 | 25 | public class JasonScriptAction { 26 | public void include(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 27 | try { 28 | JSONObject options = action.getJSONObject("options"); 29 | if(options.has("items")) { 30 | JSONArray items = options.getJSONArray("items"); 31 | JSONObject refs = new JSONObject(); 32 | OkHttpClient client = ((Launcher)context.getApplicationContext()).getHttpClient(0); 33 | 34 | JSONArray urlItems = new JSONArray(); 35 | JSONArray inlineItems = new JSONArray(); 36 | for (int i = 0; i < items.length() ; i++) { 37 | JSONObject item = (JSONObject) items.get(i); 38 | if (item.has("url")) { 39 | urlItems.put(item.getString("url")); 40 | } else if (item.has("text")) { 41 | inlineItems.put(item.getString("text")); 42 | } 43 | } 44 | 45 | if (urlItems.length() > 0) { 46 | CountDownLatch latch = new CountDownLatch(urlItems.length()); 47 | ExecutorService taskExecutor = Executors.newFixedThreadPool(urlItems.length()); 48 | for (int i = 0; i < urlItems.length() ; i++) { 49 | String url = urlItems.getString(i); 50 | taskExecutor.submit(new JasonRequire(url, latch, refs, client, context)); 51 | } 52 | try { 53 | latch.await(); 54 | } catch (Exception e) { 55 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 56 | } 57 | } 58 | 59 | // remote inject 60 | Iterator keys = refs.keys(); 61 | while (keys.hasNext()) { 62 | Object key = keys.next(); 63 | String js = refs.getString((String) key); 64 | JasonParser.getInstance(context).inject(js); 65 | } 66 | 67 | // local inject (inline) 68 | for (int i = 0; i < inlineItems.length() ; i++) { 69 | String js = inlineItems.getString(i); 70 | JasonParser.getInstance(context).inject(js); 71 | } 72 | 73 | JasonHelper.next("success", action, data, event, context); 74 | } 75 | } catch (Exception e) { 76 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 77 | } 78 | 79 | 80 | } 81 | public void clear(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 82 | JasonParser.getInstance(context).reset(); 83 | JasonHelper.next("success", action, data, event, context); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonSliderComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.graphics.PorterDuff; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.SeekBar; 8 | import com.jasonette.seed.Core.JasonViewActivity; 9 | import com.jasonette.seed.Helper.JasonHelper; 10 | import org.json.JSONObject; 11 | 12 | public class JasonSliderComponent { 13 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context context) { 14 | if(view == null) { 15 | return new SeekBar(context); 16 | } else { 17 | try { 18 | view = JasonComponent.build(view, component, parent, context); 19 | SeekBar seekBar = ((SeekBar) view); 20 | if(component.has("name")){ 21 | String val = "0.5"; 22 | if(((JasonViewActivity) context).model.var.has(component.getString("name"))){ 23 | val = ((JasonViewActivity) context).model.var.getString(component.getString("name")); 24 | } else { 25 | // default value 26 | if(component.has("value")){ 27 | val = component.getString("value"); 28 | } 29 | } 30 | seekBar.setProgress((int)Math.round(Double.parseDouble(val)*100.0)); 31 | JasonSliderComponent.addListener(seekBar, context); 32 | } 33 | JSONObject style = JasonHelper.style(component, context); 34 | if (style.has("color")) { 35 | int color = JasonHelper.parse_color(style.getString("color")); 36 | seekBar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN); 37 | seekBar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN); 38 | } else { 39 | // maybe it's not necessary 40 | seekBar.getProgressDrawable().clearColorFilter(); 41 | // it's necessary 42 | seekBar.getThumb().clearColorFilter(); 43 | } 44 | view.requestLayout(); 45 | return view; 46 | } catch (Exception e){ 47 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 48 | return new View(context); 49 | } 50 | } 51 | } 52 | 53 | public static void addListener(final SeekBar view, final Context root_context){ 54 | SeekBar.OnSeekBarChangeListener seekListener = new SeekBar.OnSeekBarChangeListener() { 55 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 56 | } 57 | 58 | public void onStartTrackingTouch(SeekBar seekBar) { 59 | } 60 | 61 | public void onStopTrackingTouch(SeekBar seekBar) { 62 | JSONObject component = (JSONObject)seekBar.getTag(); 63 | try { 64 | // don't work with int if progress == 0 65 | String progress = Double.toString(seekBar.getProgress()/100.0); 66 | ((JasonViewActivity) root_context).model.var.put(component.getString("name"), progress); 67 | if (component.has("action")) { 68 | JSONObject action = component.getJSONObject("action"); 69 | ((JasonViewActivity) root_context).call(action.toString(), new JSONObject().toString(), "{}", view.getContext()); 70 | } 71 | } catch (Exception e) { 72 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 73 | } 74 | } 75 | }; 76 | view.setOnSeekBarChangeListener(seekListener); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonGlobalAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | import android.content.Context; 3 | import android.content.SharedPreferences; 4 | import android.util.Log; 5 | 6 | import com.jasonette.seed.Core.JasonViewActivity; 7 | import com.jasonette.seed.Helper.JasonHelper; 8 | import com.jasonette.seed.Launcher.Launcher; 9 | 10 | import org.json.JSONArray; 11 | import org.json.JSONObject; 12 | 13 | import java.util.Iterator; 14 | 15 | public class JasonGlobalAction { 16 | public void reset(final JSONObject action, final JSONObject data, final JSONObject event, final Context context) { 17 | 18 | /******************** 19 | 20 | The following resets a global variable named "db". 21 | When a variable is reset, the key itself gets destroyed, so when you check ('db' in $global), it will return false 22 | 23 | { 24 | "type": "$global.reset", 25 | "options": { 26 | "items": ["db"] 27 | } 28 | } 29 | 30 | ********************/ 31 | 32 | try { 33 | SharedPreferences pref = context.getSharedPreferences("global", 0); 34 | SharedPreferences.Editor editor = pref.edit(); 35 | 36 | JSONObject options = action.getJSONObject("options"); 37 | if(options.has("items")){ 38 | JSONArray items = options.getJSONArray("items"); 39 | for (int i=0; i keysIterator = options.keys(); 88 | while (keysIterator.hasNext()) { 89 | String key = (String) keysIterator.next(); 90 | Object val = options.get(key); 91 | editor.putString(key, val.toString()); 92 | ((Launcher)context.getApplicationContext()).setGlobal(key, val); 93 | } 94 | editor.commit(); 95 | 96 | // Execute next 97 | JasonHelper.next("success", action, ((Launcher)context.getApplicationContext()).getGlobal(), event, context); 98 | 99 | } catch (Exception e) { 100 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /tools/legacy/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to Jasonette 2 | 3 | ## **Want to help with documentation?** 4 | 5 | If you would like to contribute to the [documentation](https://jasonette.github.io/documentation/), let's discuss on the [documentation repository](https://github.com/Jasonette/documentation/issues). 6 | 7 | ## **Do you have a bug report or a feature request?** 8 | 9 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/Jasonette/JASONETTE-Android/issues). 10 | 11 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Jasonette/JASONETTE-Android/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. 12 | 13 | 14 | ## **Did you write a patch that fixes a bug?** 15 | 16 | * Open a new GitHub pull request with the patch. 17 | 18 | * Don't fork `master` branch. **Fork `develop` branch and send a pull request to `develop`. 19 | 20 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 21 | 22 | ## **Did you write a cool extension?** 23 | 24 | Feel free to fork the project and [write your own extension](https://jasonette.github.io/documentation/advanced) 25 | 26 | If you wrote a cool extension, please share it with the community in the [slack channel](https://jasonette.now.sh). 27 | 28 | ## **Do you have other types of questions?** 29 | 30 | * Ask any question about how to use Jasonette on the [Jasonette Slack channel](https://jasonette.now.sh). 31 | 32 | ## **Project Structure** 33 | 34 | ### Class hierarchy 35 | ![hierarchy](https://raw.githubusercontent.com/gliechtenstein/images/master/android_hierarchy.png) 36 | 37 | Here's a brief walkthrough of how the project is structured: 38 | 39 | - java 40 | - com.jasonette.seed 41 | - **Action**: Where all [actions](https://jasonette.github.io/documentation/actions/) are implemented. The implementation follows [the convention described here](https://jasonette.github.io/documentation/advanced/#2-extend-actions). 42 | - **Component**: Implements [components](https://jasonette.github.io/documentation/components/), following [the convention described here](https://jasonette.github.io/documentation/advanced/#1-extend-ui-components). 43 | - **Core**: Core logic that handles action dispatch, view construction, templating, and some native system actions. 44 | - JasonModel: Deals with all the data associated with JasonViewActivity. 45 | - JasonParser: Handles template parsing. Calls `parser.js`. 46 | - JasonViewActivity: The main Jason view. 47 | - **Helpe** 48 | - JasonHelper: Various helper methods 49 | - JasonSettings: Helper methods for app-wide settings 50 | - **Launcher** 51 | - You can ignore this, this just launches the app. 52 | - **Section**: Implements [sections](https://jasonette.github.io/documentation/document/#bodysections) 53 | - ItemAdapter: data binding for the scrolling section items. 54 | - JasonLayout: Deals with layout. 55 | - assets 56 | - parser.js: The javascript powered JSON templating engine. 57 | - csv.js: CSV parser 58 | - res 59 | - values 60 | - strings.xml: Config strings 61 | 62 | ### What files you will be touching 63 | 64 | ####User 65 | In most cases, the only thing you will ever need to touch is the `res/values/strings.xml` file. This is where you set the main url your app will launch from, and the title of the app. 66 | 67 | ####Advanced 68 | Sometimes you may want to write an [extension](https://jasonette.github.io/documentation/advanced/#extension). In this case you may need to deal with: 69 | - `Action`: To write action extension 70 | - `Component`: To write UI component extension 71 | 72 | ####Guru 73 | If you find a bug **anywhere in the code**, or have any improvements anywhere else, please feel free to: 74 | 1. Fork the `develop` branch 75 | 2. Create a feature branch 76 | 3. Fix 77 | 4. Send a pull request 78 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 35 | 36 | 37 | 45 | 46 | 50 | 51 | 52 | 55 | 56 | 57 | 71 | 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 86 | 87 | 88 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonGeoAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.location.Location; 8 | import android.location.LocationListener; 9 | import android.location.LocationManager; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import android.os.Looper; 13 | import androidx.core.app.ActivityCompat; 14 | import androidx.core.content.ContextCompat; 15 | import android.util.Log; 16 | 17 | import com.jasonette.seed.Helper.JasonHelper; 18 | 19 | import org.json.JSONObject; 20 | 21 | public class JasonGeoAction { 22 | 23 | public static final String COORDS_STRING_FORMAT = "%f,%f"; 24 | 25 | public void get(final JSONObject action, JSONObject data, final JSONObject event, final Context context) { 26 | try { 27 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 28 | if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED 29 | && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 30 | ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 51); 31 | } else { 32 | getLocationManagerInfo(action, event, context); 33 | } 34 | } else { 35 | getLocationManagerInfo(action, event, context); 36 | } 37 | } catch (SecurityException e){ 38 | JasonHelper.permission_exception("$geo.get", context); 39 | } catch (Exception e) { 40 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 41 | } 42 | } 43 | 44 | 45 | private void getLocationManagerInfo(final JSONObject action, final JSONObject event, final Context context){ 46 | final LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); 47 | LocationListener locationListener = new LocationListener() { 48 | public void onLocationChanged(Location location) { 49 | try { 50 | 51 | locationManager.removeUpdates(this); 52 | 53 | JSONObject ret = new JSONObject(); 54 | String val = String.format(COORDS_STRING_FORMAT, location.getLatitude(), location.getLongitude()); 55 | ret.put("coord", val); 56 | ret.put("value", val); 57 | JasonHelper.next("success", action, ret, event, context); 58 | } catch (Exception e) { 59 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 60 | } 61 | } 62 | 63 | public void onStatusChanged(String provider, int status, Bundle extras) { 64 | } 65 | 66 | public void onProviderEnabled(String provider) { 67 | } 68 | 69 | public void onProviderDisabled(String provider) { 70 | } 71 | }; 72 | 73 | try { 74 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 75 | if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED 76 | && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 77 | ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 51); 78 | } else { 79 | getLocationManagerInfo(action, event, context); 80 | } 81 | } else { 82 | getLocationManagerInfo(action, event, context); 83 | } 84 | } catch (SecurityException e){ 85 | JasonHelper.permission_exception("$geo.get", context); 86 | } catch (Exception e) { 87 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 88 | } 89 | locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener, Looper.getMainLooper()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonSwitchComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.graphics.PorterDuff; 5 | import android.os.Build; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.widget.CompoundButton; 9 | import android.widget.Switch; 10 | 11 | import com.jasonette.seed.Core.JasonViewActivity; 12 | import com.jasonette.seed.Helper.JasonHelper; 13 | 14 | import org.json.JSONObject; 15 | 16 | 17 | public class JasonSwitchComponent { 18 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context context) { 19 | if(view == null) { 20 | return new Switch(context); 21 | } else { 22 | try { 23 | view = JasonComponent.build(view, component, parent, context); 24 | final Switch aSwitch = ((Switch) view); 25 | 26 | Boolean checked = false; 27 | if(component.has("name")){ 28 | if(((JasonViewActivity) context).model.var.has(component.getString("name"))){ 29 | checked = ((JasonViewActivity) context).model.var.getBoolean(component.getString("name")); 30 | } else { 31 | if(component.has("value")){ 32 | checked = component.getBoolean("value"); 33 | } 34 | } 35 | } 36 | final JSONObject style = JasonHelper.style(component, context); 37 | aSwitch.setChecked(checked); 38 | aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 39 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 40 | onChange(aSwitch, isChecked, style, context); 41 | } 42 | }); 43 | changeColor(aSwitch, checked, style); 44 | view.requestLayout(); 45 | return view; 46 | } catch (Exception e){ 47 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 48 | return new View(context); 49 | } 50 | } 51 | } 52 | 53 | public static void onChange(Switch view, boolean isChecked, JSONObject style, Context root_context) { 54 | changeColor(view, isChecked, style); 55 | JSONObject component = (JSONObject)view.getTag(); 56 | try { 57 | ((JasonViewActivity) root_context).model.var.put(component.getString("name"), view.isChecked()); 58 | if (component.has("action")) { 59 | JSONObject action = component.getJSONObject("action"); 60 | ((JasonViewActivity) root_context).call(action.toString(), new JSONObject().toString(), "{}", view.getContext()); 61 | } 62 | } catch (Exception e) { 63 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 64 | } 65 | } 66 | public static void changeColor(Switch s, boolean isChecked, JSONObject style) { 67 | try { 68 | if(isChecked) { 69 | int color; 70 | if (style.has("color")) { 71 | color = JasonHelper.parse_color(style.getString("color")); 72 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 73 | s.getThumbDrawable().setColorFilter(color, PorterDuff.Mode.MULTIPLY); 74 | s.getTrackDrawable().setColorFilter(color, PorterDuff.Mode.MULTIPLY); 75 | } 76 | } else { 77 | s.getThumbDrawable().clearColorFilter(); 78 | s.getTrackDrawable().clearColorFilter(); 79 | } 80 | } else { 81 | int color; 82 | if (style.has("color:disabled")) { 83 | color = JasonHelper.parse_color(style.getString("color:disabled")); 84 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 85 | s.getThumbDrawable().setColorFilter(color, PorterDuff.Mode.MULTIPLY); 86 | s.getTrackDrawable().setColorFilter(color, PorterDuff.Mode.SRC_ATOP); 87 | } 88 | } else { 89 | s.getThumbDrawable().clearColorFilter(); 90 | s.getTrackDrawable().clearColorFilter(); 91 | } 92 | } 93 | } catch (Exception e) { 94 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Lib/JasonToolbar.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Lib; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.util.AttributeSet; 6 | import android.view.Gravity; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.annotation.Nullable; 11 | import androidx.appcompat.widget.Toolbar; 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.jasonette.seed.Component.JasonImageComponent; 15 | import com.jasonette.seed.Helper.JasonHelper; 16 | 17 | import org.json.JSONObject; 18 | 19 | 20 | /** 21 | * Created by realitix on 27/07/17. 22 | */ 23 | 24 | public class JasonToolbar extends Toolbar { 25 | private TextView titleView; 26 | private ImageView logoView; 27 | private int alignment = -1; 28 | private int leftOffset; 29 | private int topOffset; 30 | private int imageWidth; 31 | private int imageHeight; 32 | 33 | public JasonToolbar(Context context) { 34 | super(context); 35 | } 36 | 37 | public JasonToolbar(Context context, @Nullable AttributeSet attrs) { 38 | super(context, attrs); 39 | } 40 | 41 | public JasonToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 42 | super(context, attrs, defStyleAttr); 43 | } 44 | 45 | @Override 46 | public void setTitle(CharSequence title) { 47 | // remove image view before inserting title view 48 | if (logoView != null && logoView.getParent() == this) { 49 | removeView(logoView); 50 | } 51 | 52 | // remove title if empty 53 | if (title.length() <= 0) { 54 | if (titleView != null && titleView.getParent() == this) { 55 | removeView(titleView); 56 | } 57 | return; 58 | } 59 | 60 | // create title only on the first call 61 | if (titleView == null) { 62 | titleView = new TextView(getContext()); 63 | } 64 | 65 | // insert into toolbar 66 | if (titleView.getParent() != this) { 67 | addView(titleView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 68 | } 69 | 70 | // manage positioning 71 | Toolbar.LayoutParams params = new Toolbar.LayoutParams(Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.WRAP_CONTENT); 72 | params.gravity = (alignment == -1) ? Gravity.LEFT : alignment; 73 | params.leftMargin = leftOffset; 74 | params.topMargin = topOffset; 75 | titleView.setLayoutParams(params); 76 | 77 | // set text 78 | titleView.setText(title); 79 | } 80 | 81 | public void setImage(JSONObject url) { 82 | // remove title view before inserting image view 83 | if (titleView != null && titleView.getParent() == this) { 84 | removeView(titleView); 85 | } 86 | 87 | // create the image view only on the first call 88 | if (logoView == null) { 89 | logoView = new ImageView(getContext()); 90 | } 91 | 92 | // insert into toolbar 93 | if (logoView.getParent() != this) { 94 | addView(logoView); 95 | } 96 | 97 | // manage positioning 98 | Toolbar.LayoutParams params = new Toolbar.LayoutParams(imageWidth, imageHeight); 99 | params.gravity = (alignment == -1) ? Gravity.CENTER : alignment; 100 | params.leftMargin = leftOffset; 101 | params.topMargin = topOffset; 102 | logoView.setLayoutParams(params); 103 | 104 | // load image with glide 105 | Glide.with(getContext()) 106 | .load(JasonImageComponent.resolve_url(url, getContext())) 107 | .into(logoView); 108 | } 109 | 110 | public void setTitleFont(JSONObject style) { 111 | JasonHelper.setTextViewFont(titleView, style, getContext()); 112 | } 113 | 114 | @Override 115 | public void setTitleTextColor(int color) { 116 | titleView.setTextColor(color); 117 | } 118 | 119 | public void setTitleSize(float size) { 120 | titleView.setTextSize(size); 121 | } 122 | 123 | public void setTitleTypeface(Typeface font) { 124 | titleView.setTypeface(font); 125 | } 126 | 127 | public void setAlignment(int alignment) { 128 | this.alignment = alignment; 129 | } 130 | 131 | public void setLeftOffset(int offset) { 132 | leftOffset = offset; 133 | } 134 | 135 | public void setTopOffset(int offset) { 136 | topOffset = offset; 137 | } 138 | 139 | public void setImageHeight(int height) { 140 | imageHeight = height; 141 | } 142 | 143 | public void setImageWidth(int width) { 144 | imageWidth = width; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonButtonComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.Gravity; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import com.jasonette.seed.Helper.JasonHelper; 10 | 11 | import org.json.JSONObject; 12 | 13 | public class JasonButtonComponent{ 14 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context context) { 15 | if(component.has("url")){ 16 | // image button 17 | view = JasonImageComponent.build(view, component, parent, context); 18 | } else if(component.has("text")){ 19 | // label button 20 | 21 | view = JasonLabelComponent.build(view, component, parent, context); 22 | 23 | try { 24 | JSONObject style = component.getJSONObject("style"); 25 | 26 | 27 | /******* 28 | * ALIGN : By default align center 29 | ******/ 30 | 31 | // Default is center 32 | int g = Gravity.CENTER; 33 | 34 | if (style.has("align")) { 35 | String align = style.getString("align"); 36 | if (align.equalsIgnoreCase("center")) { 37 | g = g | Gravity.CENTER_HORIZONTAL; 38 | ((TextView) view).setGravity(Gravity.CENTER_HORIZONTAL); 39 | } else if (align.equalsIgnoreCase("right")) { 40 | g = g | Gravity.RIGHT; 41 | ((TextView) view).setGravity(Gravity.RIGHT); 42 | } else if (align.equalsIgnoreCase("left")) { 43 | g = g | Gravity.LEFT; 44 | } 45 | 46 | if (align.equalsIgnoreCase("top")) { 47 | g = g | Gravity.TOP; 48 | } else if (align.equalsIgnoreCase("bottom")) { 49 | g = g | Gravity.BOTTOM; 50 | } else { 51 | g = g | Gravity.CENTER_VERTICAL; 52 | } 53 | } 54 | ((TextView)view).setGravity(g); 55 | 56 | 57 | 58 | /******* 59 | * Padding: By default padding is 15 60 | ******/ 61 | // override each padding value only if it's not specified 62 | 63 | int padding_top = -1; 64 | int padding_left = -1; 65 | int padding_bottom = -1; 66 | int padding_right = -1; 67 | if(style.has("padding")){ 68 | padding_top = (int)JasonHelper.pixels(context, style.getString("padding_top"), "horizontal"); 69 | padding_left = padding_top; 70 | padding_right = padding_top; 71 | padding_bottom = padding_top; 72 | } 73 | if(style.has("padding_top")){ 74 | padding_top = (int)JasonHelper.pixels(context, style.getString("padding_top"), "vertical"); 75 | } 76 | if(style.has("padding_left")){ 77 | padding_left = (int)JasonHelper.pixels(context, style.getString("padding_left"), "horizontal"); 78 | } 79 | if(style.has("padding_bottom")){ 80 | padding_bottom = (int)JasonHelper.pixels(context, style.getString("padding_bottom"), "vertical"); 81 | } 82 | if(style.has("padding_right")){ 83 | padding_right = (int)JasonHelper.pixels(context, style.getString("padding_right"), "horizontal"); 84 | } 85 | 86 | // if not specified, default is 15 87 | if(padding_top < 0){ 88 | padding_top = 15; 89 | } 90 | if(padding_left < 0){ 91 | padding_left = 15; 92 | } 93 | if(padding_bottom < 0){ 94 | padding_bottom = 15; 95 | } 96 | if(padding_right < 0){ 97 | padding_right = 15; 98 | } 99 | 100 | view.setPadding(padding_left, padding_top, padding_right, padding_bottom); 101 | } catch (Exception e) { 102 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 103 | } 104 | 105 | } else { 106 | // shouldn't happen 107 | if (view == null) { 108 | return new View(context); 109 | } else { 110 | return view; 111 | } 112 | } 113 | JasonComponent.addListener(view, context); 114 | 115 | return view; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonReturnAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 8 | 9 | import com.jasonette.seed.Core.JasonParser; 10 | import com.jasonette.seed.Helper.JasonHelper; 11 | 12 | import org.json.JSONObject; 13 | 14 | public class JasonReturnAction { 15 | 16 | 17 | /* 18 | 19 | ------------------------------------------- 20 | 21 | Throughout every action call chain, the original caller action is passed down inside the 'event' object. 22 | 23 | 1. When we reach the end via `$return.success`, we look into what event.success contains. 24 | Then we execute $lambda on it. 25 | 26 | 2. When we reach the end via `$return.success`, we look into what event.success contains. 27 | Then we execute $lambda on it. 28 | 29 | 30 | 31 | ------------------------------------------- 32 | 33 | { 34 | "$jason": { 35 | "head": { 36 | "actions": { 37 | "$load": { 38 | "type": "$trigger", 39 | "options": { 40 | "name": "sync" 41 | }, 42 | "success": { 43 | "type": "$trigger", 44 | "options": { 45 | "name": "process" 46 | }, 47 | "success": { 48 | "type": "$render" 49 | } 50 | }, 51 | "error": { 52 | "trigger": "err" 53 | } 54 | }, 55 | "sync": { 56 | "type": "$network.request", 57 | "options": { 58 | "url": "https://www.jasonbase.com/things/4nf.json" 59 | }, 60 | "success": { 61 | "type": "$return.success" 62 | } 63 | }, 64 | "err": { 65 | "type": "$util.banner", 66 | "options": { 67 | "title": "error", 68 | "description": "Something went wrong." 69 | } 70 | } 71 | }, 72 | "templates": { 73 | ... 74 | } 75 | } 76 | } 77 | } 78 | 79 | 80 | whenever triggering something, 81 | attach the original action as event 82 | when reaching $return.success or $return.error, just replace it with $event.success 83 | */ 84 | private void next(final String type, final JSONObject action, JSONObject data, final JSONObject event, final Context context) { 85 | if(event.has(type)){ 86 | try{ 87 | JSONObject options; 88 | if (action.has("options")) { 89 | options = action.getJSONObject("options"); 90 | } else { 91 | options = new JSONObject(); 92 | } 93 | JasonParser.getInstance(context).setParserListener(new JasonParser.JasonParserListener() { 94 | @Override 95 | public void onFinished(JSONObject parsed_options) { 96 | try { 97 | Intent intent = new Intent("call"); 98 | intent.putExtra("action", event.getJSONObject(type).toString()); 99 | intent.putExtra("data", parsed_options.toString()); 100 | LocalBroadcastManager.getInstance(context).sendBroadcast(intent); 101 | } catch (Exception e) { 102 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 103 | } 104 | } 105 | }); 106 | JasonParser.getInstance(context).parse("json", data, options, context); 107 | } catch (Exception e){ 108 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 109 | } 110 | } else { 111 | JasonHelper.next(type, new JSONObject(), data, event, context); 112 | } 113 | } 114 | 115 | 116 | public void success(final JSONObject action, JSONObject data, final JSONObject event, final Context context) { 117 | next("success", action, data, event, context); 118 | } 119 | public void error(final JSONObject action, JSONObject data, final JSONObject event, final Context context) { 120 | next("error", action, data, event, context); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/src/debug/java/com/jasonette/seed/Launcher/ConfigurableStethoTree.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Launcher; 2 | /* 3 | * Copyright (c) 2014-present, Facebook, Inc. 4 | * All rights reserved. 5 | * 6 | * This source code is licensed under the BSD-style license found in the 7 | * LICENSE file in the root directory of this source tree. An additional grant 8 | * of patent rights can be found in the PATENTS file in the same directory. 9 | */ 10 | import android.util.Log; 11 | import com.facebook.stetho.inspector.console.CLog; 12 | import com.facebook.stetho.inspector.console.ConsolePeerManager; 13 | import com.facebook.stetho.inspector.protocol.module.Console; 14 | import timber.log.Timber; 15 | 16 | /** 17 | * NOTE: Using this ONLY UNTIL this PR: https://github.com/facebook/stetho/pull/490 18 | * gets merged into Stetho. 19 | */ 20 | 21 | /** 22 | * Timber tree implementation which forwards logs to the Chrome Dev console. 23 | * This uses a {@link Timber.DebugTree} to automatically infer the tag from the calling class. 24 | * Plant it using {@link Timber#plant(Timber.Tree)} 25 | *
 26 |  *   {@code
 27 |  *   Timber.plant(new StethoTree())
 28 |  *   //or
 29 |  *   Timber.plant(new StethoTree(
 30 |  *       new StethoTree.Configuration.Builder()
 31 |  *           .showTags(true)
 32 |  *           .minimumPriority(Log.WARN)
 33 |  *           .build()));
 34 |  *   }
 35 |  * 
36 | */ 37 | public class ConfigurableStethoTree extends Timber.DebugTree { 38 | private final Configuration mConfiguration; 39 | 40 | public ConfigurableStethoTree() { 41 | this.mConfiguration = new Configuration.Builder().build(); 42 | } 43 | 44 | public ConfigurableStethoTree(Configuration configuration) { 45 | this.mConfiguration = configuration; 46 | } 47 | 48 | @Override 49 | protected void log(int priority, String tag, String message, Throwable t) { 50 | 51 | if(priority < mConfiguration.mMinimumPriority) { 52 | return; 53 | } 54 | 55 | ConsolePeerManager peerManager = ConsolePeerManager.getInstanceOrNull(); 56 | if (peerManager == null) { 57 | Log.println(priority, tag, message); 58 | return; 59 | } 60 | 61 | Console.MessageLevel logLevel; 62 | 63 | switch (priority) { 64 | case Log.VERBOSE: 65 | case Log.DEBUG: 66 | logLevel = Console.MessageLevel.DEBUG; 67 | break; 68 | case Log.INFO: 69 | logLevel = Console.MessageLevel.LOG; 70 | break; 71 | case Log.WARN: 72 | logLevel = Console.MessageLevel.WARNING; 73 | break; 74 | case Log.ERROR: 75 | case Log.ASSERT: 76 | logLevel = Console.MessageLevel.ERROR; 77 | break; 78 | default: 79 | logLevel = Console.MessageLevel.LOG; 80 | } 81 | 82 | StringBuilder messageBuilder = new StringBuilder(); 83 | 84 | if(mConfiguration.mShowTags && tag != null) { 85 | messageBuilder 86 | .append("[") 87 | .append(tag) 88 | .append("] "); 89 | } 90 | 91 | messageBuilder.append(message); 92 | 93 | CLog.writeToConsole( 94 | logLevel, 95 | Console.MessageSource.OTHER, 96 | messageBuilder.toString() 97 | ); 98 | 99 | } 100 | 101 | public static class Configuration { 102 | 103 | private final boolean mShowTags; 104 | private final int mMinimumPriority; 105 | 106 | private Configuration(boolean showTags, int minimumPriority) { 107 | this.mShowTags = showTags; 108 | this.mMinimumPriority = minimumPriority; 109 | } 110 | 111 | public static class Builder { 112 | 113 | private boolean mShowTags = false; 114 | private int mMinimumPriority = Log.VERBOSE; 115 | 116 | /** 117 | * @param showTags Logs the tag of the calling class when true. 118 | * Default is false. 119 | * @return This {@link Configuration.Builder} instance. 120 | */ 121 | public Builder showTags(boolean showTags) { 122 | this.mShowTags = showTags; 123 | return this; 124 | } 125 | 126 | /** 127 | * @param priority Minimum log priority to send log. 128 | * Expects one of constants defined in {@link android.util.Log}. 129 | * Default is {@link Log#VERBOSE}. 130 | * @return This {@link Configuration.Builder} instance. 131 | */ 132 | public Builder minimumPriority(int priority) { 133 | this.mMinimumPriority = priority; 134 | return this; 135 | } 136 | 137 | public Configuration build() { 138 | return new Configuration(mShowTags, mMinimumPriority); 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/assets/file/hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "$jason": { 3 | "head": { 4 | "title": "{ ˃̵̑ᴥ˂̵̑}", 5 | "actions": { 6 | "$foreground": { 7 | "type": "$reload" 8 | } 9 | } 10 | }, 11 | "body": { 12 | "header": { 13 | "style": { 14 | "theme": true 15 | } 16 | }, 17 | "style": { 18 | "background": "#ffffff", 19 | "border": "none" 20 | }, 21 | "sections": [ 22 | { 23 | "items": [ 24 | { 25 | "type": "vertical", 26 | "style": { 27 | "padding": 30, 28 | "spacing": 20, 29 | "align": "center" 30 | }, 31 | "components": [ 32 | { 33 | "type": "label", 34 | "text": "It's ALIVE!", 35 | "style": { 36 | "align": "center", 37 | "font": "Courier-Bold", 38 | "size": 26 39 | } 40 | }, 41 | { 42 | "type": "label", 43 | "text": "This is a demo app. You can make your own app by changing the url inside strings.xml", 44 | "style": { 45 | "align": "center", 46 | "font": "Courier", 47 | "padding": 30, 48 | "size": 20 49 | } 50 | }, 51 | { 52 | "type": "label", 53 | "text": "{ ˃̵̑ᴥ˂̵̑}", 54 | "style": { 55 | "align": "center", 56 | "font": "HelveticaNeue-Bold", 57 | "size": 50 58 | } 59 | } 60 | ] 61 | }, 62 | { 63 | "type": "label", 64 | "style": { 65 | "align": "right", 66 | "padding": 10, 67 | "color": "#000000", 68 | "font": "HelveticaNeue", 69 | "size": 20 70 | }, 71 | "text": "Check out Live DEMO", 72 | "href": { 73 | "url": "https://raw.githubusercontent.com/jasonelle/docs/develop/examples/jasonette/apps/jasonpedia/demo.json", 74 | "fresh": true 75 | } 76 | }, 77 | { 78 | "type": "label", 79 | "style": { 80 | "align": "right", 81 | "padding": 10, 82 | "color": "#000000", 83 | "font": "HelveticaNeue", 84 | "size": 20 85 | }, 86 | "text": "Watch the tutorial video", 87 | "href": { 88 | "url": "https://www.youtube.com/watch?v=hfevBAAfCMQ", 89 | "view": "web" 90 | } 91 | }, 92 | { 93 | "type": "label", 94 | "style": { 95 | "align": "right", 96 | "padding": 10, 97 | "color": "#000000", 98 | "font": "HelveticaNeue", 99 | "size": 20 100 | }, 101 | "text": "View documentation", 102 | "href": { 103 | "url": "https://jasonelle.com/docs", 104 | "view": "web", 105 | "options": { 106 | "reader": true 107 | } 108 | } 109 | } 110 | ] 111 | } 112 | ] 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonTimerAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Handler; 6 | import android.os.HandlerThread; 7 | import android.util.Log; 8 | 9 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 10 | 11 | import com.jasonette.seed.Helper.JasonHelper; 12 | 13 | import org.json.JSONObject; 14 | 15 | import java.util.HashMap; 16 | 17 | 18 | public class JasonTimerAction { 19 | private HashMap timers; 20 | private Handler handler; 21 | public JasonTimerAction(){ 22 | HandlerThread thread = new HandlerThread("TimerThread"); 23 | thread.start(); 24 | timers = new HashMap(); 25 | handler = new Handler(thread.getLooper()); 26 | } 27 | public void start(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 28 | try { 29 | if (action.has("options")) { 30 | JSONObject options = action.getJSONObject("options"); 31 | if(options.has("name")){ 32 | 33 | String name = options.getString("name"); 34 | 35 | // Look up timer 36 | // if it exists, reset first, and then start 37 | if(timers.get(name) != null){ 38 | cancelTimer(name); 39 | } 40 | 41 | Boolean repeats = options.has("repeats"); 42 | final int interval = (int)(Float.valueOf(options.getString("interval"))*1000); 43 | final JSONObject timerAction = options.getJSONObject("action"); 44 | 45 | if(repeats) { 46 | final Runnable runnableCode = new Runnable() { 47 | @Override 48 | public void run() { 49 | Log.d("Handlers", "Called on main thread"); 50 | 51 | Intent intent = new Intent("call"); 52 | intent.putExtra("action", timerAction.toString()); 53 | LocalBroadcastManager.getInstance(context).sendBroadcast(intent); 54 | handler.postDelayed(this, interval); 55 | } 56 | }; 57 | handler.post(runnableCode); 58 | 59 | // Register timer 60 | timers.put(name, runnableCode); 61 | } else { 62 | final Runnable runnableCode = new Runnable() { 63 | @Override 64 | public void run() { 65 | Log.d("Handlers", "Called on main thread"); 66 | 67 | Intent intent = new Intent("call"); 68 | intent.putExtra("action", timerAction.toString()); 69 | LocalBroadcastManager.getInstance(context).sendBroadcast(intent); 70 | } 71 | }; 72 | handler.postDelayed(runnableCode, interval); 73 | 74 | // Register timer 75 | timers.put(name, runnableCode); 76 | } 77 | 78 | } 79 | } 80 | 81 | // Go on to the next success action 82 | JasonHelper.next("success", action, data, event, context); 83 | 84 | } catch (Exception e){ 85 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 86 | } 87 | } 88 | 89 | public void stop(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 90 | try { 91 | if (action.has("options")) { 92 | JSONObject options = action.getJSONObject("options"); 93 | if (options.has("name")) { 94 | cancelTimer(options.getString("name")); 95 | } else { 96 | cancelTimer(null); 97 | } 98 | } else { 99 | cancelTimer(null); 100 | } 101 | 102 | // Go on to the next success action 103 | JasonHelper.next("success", action, data, event, context); 104 | } catch (Exception e){ 105 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 106 | } 107 | 108 | } 109 | 110 | private void cancelTimer(String name){ 111 | if(name != null) { 112 | Runnable runnableCode = timers.get(name); 113 | if(runnableCode != null) { 114 | handler.removeCallbacks(runnableCode); 115 | timers.remove(name); 116 | } 117 | } else { 118 | // Cancel all timers 119 | for (String key : timers.keySet()) { 120 | Runnable runnableCode = timers.get(key); 121 | handler.removeCallbacks(runnableCode); 122 | timers.remove(name); 123 | } 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Action/JasonConvertAction.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Action; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.eclipsesource.v8.JavaVoidCallback; 7 | import com.jasonette.seed.Helper.JasonHelper; 8 | 9 | import com.eclipsesource.v8.V8; 10 | import com.eclipsesource.v8.V8Array; 11 | import com.eclipsesource.v8.V8Object; 12 | import org.json.JSONObject; 13 | 14 | import java.lang.Thread; 15 | 16 | public class JasonConvertAction { 17 | private JSONObject action; 18 | private Context context; 19 | private JSONObject event_cache; 20 | 21 | public void csv(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 22 | this.action = action; 23 | this.context = context; 24 | event_cache = event; 25 | try{ 26 | final JSONObject options = action.getJSONObject("options"); 27 | String result = "[]"; 28 | if(options.has("data")) { 29 | String csv_data = options.getString("data"); 30 | if (!csv_data.isEmpty()) { 31 | String js = JasonHelper.read_file("csv", context); 32 | V8 runtime = V8.createV8Runtime(); 33 | runtime.executeVoidScript(js); 34 | V8Object csv = runtime.getObject("csv"); 35 | V8Array parameters = new V8Array(runtime).push(csv_data.toString()); 36 | V8Array val = csv.executeArrayFunction("run", parameters); 37 | parameters.release(); 38 | csv.release(); 39 | 40 | result = stringify(runtime, val); 41 | 42 | runtime.release(); 43 | } 44 | } 45 | JasonHelper.next("success", action, result, event, context); 46 | } catch (Exception e){ 47 | handle_exception(e); 48 | } 49 | } 50 | 51 | public void rss(final JSONObject action, final JSONObject data, final JSONObject event, final Context context){ 52 | this.action = action; 53 | this.context = context; 54 | event_cache = event; 55 | try{ 56 | final JSONObject options = action.getJSONObject("options"); 57 | String rss_data = options.getString("data"); 58 | String js = JasonHelper.read_file("rss", context); 59 | String timers = JasonHelper.read_file("timers", context); 60 | V8 runtime = V8.createV8Runtime(); 61 | runtime.executeVoidScript(js); 62 | 63 | // Shim to support javascript timer functions in V8 64 | runtime.registerJavaMethod(new Sleep(), "sleep"); 65 | runtime.executeVoidScript(timers); 66 | runtime.executeVoidScript("var timerLoop = makeWindowTimer(this, sleep);"); 67 | 68 | V8Object rss = runtime.getObject("rss"); 69 | V8Array parameters = new V8Array(runtime).push(rss_data.toString()); 70 | // Register a callback to receive the RSS JSON data 71 | runtime.registerJavaMethod(new RSSCallback(), "callback"); 72 | rss.executeObjectFunction("run", parameters); 73 | 74 | // Now we need to kick off the timer loop to get the parsing started 75 | runtime.executeVoidScript("timerLoop()"); 76 | 77 | parameters.release(); 78 | rss.release(); 79 | runtime.release(); 80 | } catch (Exception e){ 81 | handle_exception(e); 82 | } 83 | } 84 | 85 | /** 86 | * Converts a JSON object to a string using the javascript method `JSON.stringify` 87 | * @param runtime a V8 runtime 88 | * @param jsonObject a JSON object (V8Object) 89 | * @return a String representation of the JSON object 90 | */ 91 | private String stringify(final V8 runtime, V8Object jsonObject){ 92 | V8Array parameters = new V8Array(runtime).push(jsonObject); 93 | V8Object json = runtime.getObject("JSON"); 94 | String result = json.executeStringFunction("stringify", parameters); 95 | parameters.release(); 96 | jsonObject.release(); 97 | json.release(); 98 | return result; 99 | } 100 | 101 | /** 102 | * Handles an exception by passing the error to JasonHelper.next if possible, otherwise log 103 | * the output 104 | * @param exc Exception 105 | */ 106 | private void handle_exception(Exception exc){ 107 | try { 108 | JSONObject error = new JSONObject(); 109 | error.put("data", exc.toString()); 110 | JasonHelper.next("error", action, error, event_cache, context); 111 | } catch (Exception e){ 112 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 113 | } 114 | } 115 | 116 | /** 117 | * Callback to handle the sleep function called from timer javascript code. Simply sleeps for 118 | * the number of milliseconds passed in the arguments. 119 | */ 120 | class Sleep implements JavaVoidCallback { 121 | @Override 122 | public void invoke(V8Object receiver, V8Array parameters) { 123 | try { 124 | Thread.sleep((long)parameters.get(0)); 125 | } catch (InterruptedException e) { 126 | handle_exception(e); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * Callback that gets run at the end of RSS parsing. The one parameter is the RSS data, as a 133 | * V8Array 134 | */ 135 | class RSSCallback implements JavaVoidCallback { 136 | @Override 137 | public void invoke(V8Object receiver, V8Array parameters) { 138 | try { 139 | V8Array rss_data = (V8Array) parameters.get(0); 140 | String result = stringify(receiver.getRuntime(), rss_data); 141 | 142 | JasonHelper.next("success", action, result, event_cache, context); 143 | } catch (Exception e) { 144 | handle_exception(e); 145 | } 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Section/JasonLayout.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Section; 2 | 3 | import android.content.Context; 4 | import android.widget.LinearLayout; 5 | import com.jasonette.seed.Helper.JasonHelper; 6 | import org.json.JSONObject; 7 | 8 | public class JasonLayout { 9 | public static LinearLayout.LayoutParams autolayout(Boolean isHorizontalScroll, JSONObject parent, JSONObject item, Context root_context) { 10 | 11 | float width = 0; 12 | float height = 0; 13 | int weight = 0; 14 | 15 | try { 16 | JSONObject style = JasonHelper.style(item, root_context); 17 | 18 | String item_type = item.getString("type"); 19 | 20 | if (parent == null){ 21 | // parent == null means: it's at the root level. Which can be: 22 | // 1. a layer item 23 | // 2. the root level of a section item 24 | if (style.has("width")) { 25 | try { 26 | width = (int) JasonHelper.pixels(root_context, style.getString("width"), "horizontal"); 27 | if (style.has("ratio")) { 28 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 29 | height = width / ratio; 30 | } 31 | } catch (Exception e) { } 32 | } else { 33 | if(isHorizontalScroll){ 34 | width = LinearLayout.LayoutParams.WRAP_CONTENT; 35 | } else { 36 | width = LinearLayout.LayoutParams.MATCH_PARENT; 37 | } 38 | } 39 | if (style.has("height")) { 40 | try { 41 | height = (int) JasonHelper.pixels(root_context, style.getString("height"), "vertical"); 42 | if (style.has("ratio")) { 43 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 44 | width = height * ratio; 45 | } 46 | } catch (Exception e) { 47 | } 48 | } else { 49 | height = LinearLayout.LayoutParams.WRAP_CONTENT; 50 | } 51 | 52 | 53 | } else if (parent.getString("type").equalsIgnoreCase("vertical")) { 54 | 55 | if (style.has("height")) { 56 | try { 57 | height = (int) JasonHelper.pixels(root_context, style.getString("height"), "vertical"); 58 | if (style.has("ratio")) { 59 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 60 | width = height * ratio; 61 | } 62 | } catch (Exception e) { } 63 | } else { 64 | if(item_type.equalsIgnoreCase("vertical") || item_type.equalsIgnoreCase("horizontal") || item_type.equalsIgnoreCase("space")){ 65 | // layouts should have flexible height inside a vertical layout 66 | height = 0; 67 | weight = 1; 68 | } else { 69 | // components should stay as their intrinsic size 70 | height = LinearLayout.LayoutParams.WRAP_CONTENT; 71 | } 72 | } 73 | 74 | if (style.has("width")) { 75 | try { 76 | width = (int) JasonHelper.pixels(root_context, style.getString("width"), "horizontal"); 77 | if (style.has("ratio")) { 78 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 79 | height = width / ratio; 80 | } 81 | } catch (Exception e) { } 82 | } else { 83 | // in case of vertical layout, all its children, regardless of whether they are layout or components, 84 | // should have the width match parent 85 | // (Except for images, which will be handled inside JasonImageComponent) 86 | width = LinearLayout.LayoutParams.MATCH_PARENT; 87 | } 88 | 89 | 90 | 91 | } else if (parent.getString("type").equalsIgnoreCase("horizontal")) { 92 | if (style.has("width")) { 93 | try { 94 | width = (int) JasonHelper.pixels(root_context, style.getString("width"), "horizontal"); 95 | if (style.has("ratio")) { 96 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 97 | height = width / ratio; 98 | } 99 | } catch (Exception e) { 100 | } 101 | } else { 102 | // in a horizontal layout, the child components shouldn't fight with width. 103 | // All must be flexible width unless otherwise specified. 104 | width = 0; 105 | weight = 1; 106 | } 107 | if (style.has("height")) { 108 | try { 109 | height = (int) JasonHelper.pixels(root_context, style.getString("height"), "vertical"); 110 | if (style.has("ratio")) { 111 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 112 | width = height * ratio; 113 | } 114 | } catch (Exception e) { 115 | } 116 | } else { 117 | height = LinearLayout.LayoutParams.WRAP_CONTENT; 118 | } 119 | } 120 | 121 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams((int)width, (int)height); 122 | if (weight > 0) { 123 | layoutParams.weight = weight; 124 | } 125 | 126 | 127 | return layoutParams; 128 | 129 | } catch (Exception e){ 130 | return new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonHtmlComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | import android.webkit.CookieManager; 8 | import android.webkit.WebChromeClient; 9 | import android.webkit.WebSettings; 10 | import android.webkit.WebView; 11 | 12 | import com.jasonette.seed.Core.JasonViewActivity; 13 | 14 | import org.json.JSONObject; 15 | 16 | 17 | public class JasonHtmlComponent { 18 | 19 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context context) { 20 | if(view == null){ 21 | try { 22 | WebView webview = new WebView(context); 23 | webview.getSettings().setDefaultTextEncodingName("utf-8"); 24 | 25 | return webview; 26 | } catch (Exception e) { 27 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 28 | } 29 | } else { 30 | JasonComponent.build(view, component, parent, context); 31 | 32 | try { 33 | String text = component.getString("text"); 34 | String html = text; 35 | CookieManager.getInstance().setAcceptCookie(true); 36 | ((WebView) view).loadDataWithBaseURL("http://localhost/", html, "text/html", "utf-8", null); 37 | ((WebView) view).setWebChromeClient(new WebChromeClient()); 38 | ((WebView) view).setVerticalScrollBarEnabled(false); 39 | ((WebView) view).setHorizontalScrollBarEnabled(false); 40 | WebSettings settings = ((WebView) view).getSettings(); 41 | settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); 42 | settings.setJavaScriptEnabled(true); 43 | settings.setDomStorageEnabled(true); 44 | settings.setJavaScriptCanOpenWindowsAutomatically(true); 45 | settings.setMediaPlaybackRequiresUserGesture(false); 46 | 47 | settings.setAppCachePath( context.getCacheDir().getAbsolutePath() ); 48 | settings.setAllowFileAccess( true ); 49 | settings.setAppCacheEnabled( true ); 50 | settings.setCacheMode( WebSettings.LOAD_DEFAULT ); 51 | 52 | 53 | // not interactive by default; 54 | Boolean responds_to_webview = false; 55 | if(component.has("action")){ 56 | if(component.getJSONObject("action").has("type")){ 57 | String action_type = component.getJSONObject("action").getString("type"); 58 | if(action_type.equalsIgnoreCase("$default")){ 59 | responds_to_webview = true; 60 | } 61 | } 62 | } 63 | if(responds_to_webview){ 64 | // webview receives click 65 | ((WebView) view).setOnTouchListener(null); 66 | // Don't add native listener to this component 67 | } else { 68 | // webview shouldn't receive click 69 | 70 | ((WebView)view).setOnTouchListener(new View.OnTouchListener() { 71 | @Override 72 | public boolean onTouch(View v, MotionEvent event) { 73 | JSONObject component = (JSONObject)v.getTag(); 74 | try { 75 | // 1. if the action type $default is specified, do what's default for webview 76 | if (component.has("action")) { 77 | JSONObject action = component.getJSONObject("action"); 78 | if (action.has("type")) { 79 | if (action.getString("type").equalsIgnoreCase("$default")) { 80 | return false; 81 | } 82 | } 83 | } 84 | 85 | // But only trigger on UP motion 86 | if (event.getAction() == MotionEvent.ACTION_UP) { 87 | 88 | if(component.has("action")){ 89 | // if the current component contains an action, run that one 90 | JSONObject action = component.getJSONObject("action"); 91 | ((JasonViewActivity) context).call(action.toString(), new JSONObject().toString(), "{}", v.getContext()); 92 | } else { 93 | // otherwise, bubble up the event to the closest parent view with an 'action' attribute 94 | View cursor = v; 95 | while (cursor.getParent() != null) { 96 | JSONObject item = (JSONObject) (((View) cursor.getParent()).getTag()); 97 | if (item != null && (item.has("action") || item.has("href"))) { 98 | ((View) cursor.getParent()).performClick(); 99 | break; 100 | } else { 101 | cursor = (View) cursor.getParent(); 102 | } 103 | } 104 | } 105 | } 106 | return true; 107 | } catch (Exception e) { 108 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 109 | } 110 | return true; 111 | } 112 | }); 113 | } 114 | 115 | view.requestLayout(); 116 | return view; 117 | } catch (Exception e) { 118 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 119 | } 120 | } 121 | return new View(context); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Helper/JasonImageHelper.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Helper; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.graphics.Bitmap; 6 | import android.graphics.drawable.Drawable; 7 | import android.net.Uri; 8 | import android.os.Environment; 9 | import android.util.Log; 10 | 11 | import androidx.annotation.Nullable; 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.bumptech.glide.load.model.GlideUrl; 15 | import com.bumptech.glide.load.model.LazyHeaders; 16 | import com.bumptech.glide.request.target.CustomTarget; 17 | import com.bumptech.glide.request.transition.Transition; 18 | 19 | import org.json.JSONObject; 20 | 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.File; 23 | import java.io.FileOutputStream; 24 | import java.net.URI; 25 | import java.util.Iterator; 26 | 27 | 28 | public class JasonImageHelper { 29 | public interface JasonImageDownloadListener { 30 | public void onLoaded(byte[] data, Uri uri); 31 | } 32 | 33 | private JasonImageDownloadListener listener; 34 | private String url; 35 | private Context context; 36 | private byte[] data; 37 | 38 | public JasonImageHelper(String url, Context context) { 39 | // set null or default listener or accept as argument to constructor 40 | this.listener = null; 41 | this.url = url; 42 | this.context = context; 43 | } 44 | public JasonImageHelper(byte[] data, Context context){ 45 | this.listener = null; 46 | this.data = data; 47 | this.context = context; 48 | } 49 | public void setListener(JasonImageDownloadListener listener) { 50 | this.listener = listener; 51 | } 52 | 53 | public void load(){ 54 | try { 55 | File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "share_image_" + System.currentTimeMillis() + ".png"); 56 | FileOutputStream out = new FileOutputStream(file); 57 | out.write(this.data); 58 | out.close(); 59 | Uri bitmapUri = Uri.fromFile(file); 60 | this.listener.onLoaded(this.data, bitmapUri); 61 | } catch (Exception e) { 62 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 63 | } 64 | 65 | } 66 | 67 | public void fetch(){ 68 | 69 | try { 70 | // Constructing URL 71 | GlideUrl url; 72 | LazyHeaders.Builder builder = new LazyHeaders.Builder(); 73 | 74 | // Add session if included 75 | SharedPreferences pref = context.getSharedPreferences("session", 0); 76 | JSONObject session = null; 77 | URI uri_for_session = new URI(this.url.toLowerCase()); 78 | String session_domain = uri_for_session.getHost(); 79 | if(pref.contains(session_domain)){ 80 | String str = pref.getString(session_domain, null); 81 | session = new JSONObject(str); 82 | } 83 | // Attach Header from Session 84 | if(session != null && session.has("header")) { 85 | Iterator keys = session.getJSONObject("header").keys(); 86 | while (keys.hasNext()) { 87 | String key = (String) keys.next(); 88 | String val = session.getJSONObject("header").getString(key); 89 | builder.addHeader(key, val); 90 | } 91 | } 92 | 93 | url = new GlideUrl(this.url, builder.build()); 94 | if (this.url.matches("\\.gif")) { 95 | /* 96 | Glide 97 | .with(context) 98 | .load(url) 99 | .asGif() 100 | .into((ImageView)view); 101 | */ 102 | } else { 103 | int width = 512; 104 | int height = 384; 105 | 106 | Glide 107 | .with(context) 108 | .asBitmap() 109 | .load(url) 110 | .into(new CustomTarget(100,100) { 111 | @Override 112 | public void onStart() { 113 | 114 | } 115 | 116 | @Override 117 | public void onStop() { 118 | 119 | } 120 | 121 | @Override 122 | public void onDestroy() { 123 | 124 | } 125 | 126 | @Override 127 | public void onLoadStarted(@Nullable Drawable placeholder) { 128 | 129 | } 130 | 131 | @Override 132 | public void onLoadFailed(@Nullable Drawable errorDrawable) { 133 | 134 | } 135 | 136 | @Override 137 | public void onLoadCleared(@Nullable Drawable placeholder) { 138 | 139 | } 140 | 141 | @Override 142 | public void onResourceReady(Bitmap bitmap, Transition anim) { 143 | Uri bitmapUri = null; 144 | try { 145 | File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "share_image_" + System.currentTimeMillis() + ".png"); 146 | FileOutputStream out = new FileOutputStream(file); 147 | bitmap.compress(Bitmap.CompressFormat.PNG, 90, out); 148 | out.close(); 149 | bitmapUri = Uri.fromFile(file); 150 | 151 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 152 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); 153 | byte[] byteArray = stream.toByteArray(); 154 | 155 | listener.onLoaded(byteArray, bitmapUri); 156 | } catch (Exception e) { 157 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 158 | } 159 | 160 | } 161 | }); 162 | 163 | } 164 | } catch (Exception e){ 165 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Service/websocket/JasonWebsocketService.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Service.websocket; 2 | 3 | import android.util.Log; 4 | 5 | import com.jasonette.seed.Core.JasonViewActivity; 6 | import com.jasonette.seed.Launcher.Launcher; 7 | 8 | import org.json.JSONObject; 9 | 10 | import okhttp3.OkHttpClient; 11 | import okhttp3.Request; 12 | import okhttp3.Response; 13 | import okio.ByteString; 14 | import okhttp3.WebSocket; 15 | import okhttp3.WebSocketListener; 16 | 17 | /***************************************** 18 | 19 | ### Events: 20 | - There are 4 events: $websocket.onopen, $websocket.onclose, $websocket.onmessage, $websocket.onerror 21 | 22 | [1] $websocket.onopen 23 | - Triggered when $websocket.open action succeeds. 24 | - You can start sending messages after this event. 25 | - Response Payload: none 26 | 27 | [2] $websocket.onclose 28 | - Triggered when $websocket.close action succeeds or the socket closes 29 | - Response Payload: none 30 | 31 | [3] $websocket.onerror 32 | - Triggered when there's an error 33 | - Response Payload: 34 | { 35 | "$jason": { 36 | "error": [THE ERROR MESSAGE] 37 | } 38 | } 39 | 40 | [4] $websocket.onmessage 41 | - Triggered whenever there's an incoming message 42 | - Response Payload: 43 | { 44 | "$jason": { 45 | "message": [THE INCOMING MESSAGE STRING] 46 | } 47 | } 48 | 49 | *****************************************/ 50 | 51 | public class JasonWebsocketService { 52 | private final class JasonWebSocketListener extends WebSocketListener { 53 | private static final int NORMAL_CLOSURE_STATUS = 1000; 54 | @Override 55 | public void onOpen(WebSocket webSocket, Response response) { 56 | final JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 57 | context.runOnUiThread(new Runnable() { 58 | @Override 59 | public void run() { 60 | JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 61 | context.simple_trigger("$websocket.onopen", new JSONObject(), context); 62 | } 63 | }); 64 | } 65 | @Override 66 | public void onMessage(WebSocket webSocket, final String text) { 67 | final JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 68 | context.runOnUiThread(new Runnable() { 69 | @Override 70 | public void run() { 71 | try { 72 | JSONObject response = new JSONObject(); 73 | JSONObject message = new JSONObject(); 74 | message.put("message", text); 75 | message.put("type", "string"); 76 | response.put("$jason", message); 77 | context.simple_trigger("$websocket.onmessage", response, context); 78 | } catch (Exception e) { 79 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 80 | } 81 | } 82 | }); 83 | } 84 | @Override 85 | public void onMessage(WebSocket webSocket, final ByteString bytes) { 86 | final JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 87 | context.runOnUiThread(new Runnable() { 88 | @Override 89 | public void run() { 90 | try { 91 | JSONObject response = new JSONObject(); 92 | JSONObject message = new JSONObject(); 93 | message.put("message", bytes.hex()); 94 | message.put("type", "bytes"); 95 | response.put("$jason", message); 96 | context.simple_trigger("$websocket.onmessage", response, context); 97 | } catch (Exception e) { 98 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 99 | } 100 | } 101 | }); 102 | } 103 | @Override 104 | public void onClosing(WebSocket webSocket, int code, String reason) { 105 | final JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 106 | context.runOnUiThread(new Runnable() { 107 | @Override 108 | public void run() { 109 | JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 110 | context.simple_trigger("$websocket.onclose", new JSONObject(), context); 111 | } 112 | }); 113 | } 114 | @Override 115 | public void onFailure(WebSocket webSocket, final Throwable t, Response response) { 116 | final JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 117 | context.runOnUiThread(new Runnable() { 118 | @Override 119 | public void run() { 120 | try { 121 | JasonViewActivity context = ((JasonViewActivity) launcher.getCurrentContext()); 122 | JSONObject res = new JSONObject(); 123 | JSONObject message = new JSONObject(); 124 | message.put("error", t.getMessage()); 125 | res.put("$jason", message); 126 | context.simple_trigger("$websocket.onerror", res, context); 127 | } catch (Exception e) { 128 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 129 | } 130 | } 131 | }); 132 | } 133 | } 134 | 135 | private JasonWebSocketListener listener; 136 | private Launcher launcher; 137 | private WebSocket ws; 138 | private Thread thread; 139 | 140 | public JasonWebsocketService(Launcher launcherParam) { 141 | launcher = launcherParam; 142 | } 143 | 144 | public void open(final JSONObject action) { 145 | try { 146 | JSONObject options = action.getJSONObject("options"); 147 | String url = options.getString("url"); 148 | OkHttpClient client = launcher.getHttpClient(0); 149 | Request request = new Request.Builder().url(url).build(); 150 | listener = new JasonWebSocketListener(); 151 | ws = client.newWebSocket(request, listener); 152 | client.dispatcher().executorService().shutdown(); 153 | } catch (Exception e) { 154 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 155 | } 156 | } 157 | public void close() { 158 | ws.close(1000, "Goodbye!"); 159 | } 160 | public void send(JSONObject action) { 161 | try { 162 | JSONObject options = action.getJSONObject("options"); 163 | String text = options.getString("message"); 164 | ws.send(text); 165 | } catch (Exception e) { 166 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 167 | } 168 | } 169 | 170 | 171 | } 172 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Core/JasonParser.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Core; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.eclipsesource.v8.V8; 7 | import com.eclipsesource.v8.V8Array; 8 | import com.eclipsesource.v8.V8Object; 9 | import com.jasonette.seed.Helper.JasonHelper; 10 | 11 | import org.json.JSONObject; 12 | 13 | import java.util.concurrent.LinkedBlockingQueue; 14 | 15 | import timber.log.Timber; 16 | 17 | public class JasonParser { 18 | static JSONObject res; 19 | private static Context context; 20 | 21 | private static JasonParser instance = null; 22 | 23 | private JasonParser(){ 24 | this.listener = null; 25 | } 26 | 27 | public interface JasonParserListener { 28 | public void onFinished(JSONObject json); 29 | } 30 | private JasonParserListener listener; 31 | private V8 juice; 32 | public void setParserListener(JasonParserListener listener){ 33 | this.listener = listener; 34 | } 35 | 36 | 37 | private class Task { 38 | public String data_type; 39 | public JSONObject data; 40 | public Object template; 41 | public Task(String data_type, JSONObject data, Object template) { 42 | this.data_type = data_type; 43 | this.data = data; 44 | this.template = template; 45 | } 46 | } 47 | private LinkedBlockingQueue taskQueue = new LinkedBlockingQueue(); 48 | private void processTask(Task task) { 49 | try { 50 | Object template = task.template; 51 | JSONObject data = task.data; 52 | String data_type = task.data_type; 53 | 54 | // thread handling - acquire handle 55 | juice.getLocker().acquire(); 56 | Console console = new Console(); 57 | V8Object v8Console = new V8Object(juice); 58 | juice.add("console", v8Console); 59 | v8Console.registerJavaMethod(console, "log", "log", new Class[] { String.class }); 60 | v8Console.registerJavaMethod(console, "error", "error", new Class[] { String.class }); 61 | v8Console.registerJavaMethod(console, "trace", "trace", new Class[] {}); 62 | 63 | String templateJson = template.toString(); 64 | String dataJson = data.toString(); 65 | String val = "{}"; 66 | 67 | V8Object parser = juice.getObject("ST"); 68 | // Get global variables (excluding natively injected variables which will never be used in the template) 69 | String globals = juice.executeStringScript("JSON.stringify(Object.keys(this).filter(function(key){return ['ST', 'to_json', 'setImmediate', 'clearImmediate', 'console'].indexOf(key) === -1;}));"); 70 | if(data_type.equalsIgnoreCase("json")) { 71 | V8Array parameters = new V8Array(juice); 72 | parameters.push(templateJson); 73 | parameters.push(dataJson); 74 | parameters.push(globals); 75 | parameters.push(true); 76 | val = parser.executeStringFunction("transform", parameters); 77 | parameters.release(); 78 | } else { 79 | String raw_data = data.getString("$jason"); 80 | V8Array parameters = new V8Array(juice).push("html"); 81 | parameters.push(templateJson); 82 | parameters.push(raw_data); 83 | parameters.push(globals); 84 | parameters.push(true); 85 | val = juice.executeStringFunction("to_json", parameters); 86 | parameters.release(); 87 | } 88 | parser.release(); 89 | v8Console.release(); 90 | 91 | if (val.equalsIgnoreCase("null")) { 92 | res = new JSONObject(); 93 | } else { 94 | res = new JSONObject(val); 95 | } 96 | 97 | listener.onFinished(res); 98 | 99 | } catch (Exception e){ 100 | Timber.w(e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 101 | } 102 | 103 | // thread handling - release handle 104 | juice.getLocker().release(); 105 | 106 | } 107 | 108 | 109 | 110 | public static JasonParser getInstance(Context context){ 111 | if(instance == null) 112 | { 113 | instance = new JasonParser(); 114 | try { 115 | String js = JasonHelper.read_file("st", context); 116 | String xhtmljs = JasonHelper.read_file("xhtml", context); 117 | instance.juice = V8.createV8Runtime(); 118 | instance.juice.executeVoidScript(js); 119 | instance.juice.executeVoidScript(xhtmljs); 120 | instance.juice.getLocker().release(); 121 | } catch (Exception e){ 122 | Timber.w(e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 123 | } 124 | } 125 | return instance; 126 | } 127 | 128 | public static void inject(String js) { 129 | instance.juice.executeVoidScript(js); 130 | } 131 | public static void reset() { 132 | try { 133 | String js = JasonHelper.read_file("st", context); 134 | String xhtmljs = JasonHelper.read_file("xhtml", context); 135 | instance.juice = V8.createV8Runtime(); 136 | instance.juice.executeVoidScript(js); 137 | instance.juice.executeVoidScript(xhtmljs); 138 | instance.juice.getLocker().release(); 139 | } catch (Exception e){ 140 | Timber.w(e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 141 | } 142 | } 143 | 144 | 145 | private Thread thread; 146 | public void parse(final String data_type, final JSONObject data, final Object template, final Context context){ 147 | try{ 148 | taskQueue.put(new Task(data_type, data, template)); 149 | if(thread == null) { 150 | thread = new Thread(new Runnable() { 151 | @Override 152 | public void run() { 153 | while (true) { 154 | try { 155 | if (taskQueue.size() > 0) { 156 | processTask(taskQueue.take()); 157 | } 158 | if (taskQueue.size() == 0) { 159 | thread = null; 160 | break; 161 | } 162 | } catch (Exception e) { 163 | Timber.w(e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 164 | } 165 | } 166 | } 167 | }); 168 | thread.start(); 169 | } else { 170 | } 171 | } catch (Exception e){ 172 | Timber.w(e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 173 | } 174 | } 175 | } 176 | 177 | 178 | /** 179 | * Override for console to print javascript debug output in the Android Studio console 180 | */ 181 | class Console { 182 | public void log(final String message) { 183 | Timber.d(message); 184 | } 185 | public void error(final String message) { 186 | Timber.e(message); 187 | } 188 | public void trace() { 189 | Timber.e("Unable to reproduce JS stacktrace"); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Core/JasonRequire.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Core; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.net.Uri; 6 | import android.util.Log; 7 | 8 | import com.jasonette.seed.Helper.JasonHelper; 9 | 10 | import org.json.JSONArray; 11 | import org.json.JSONException; 12 | import org.json.JSONObject; 13 | 14 | import java.io.IOException; 15 | import java.net.URI; 16 | import java.util.Dictionary; 17 | import java.util.Hashtable; 18 | import java.util.Iterator; 19 | import java.util.concurrent.CountDownLatch; 20 | 21 | import okhttp3.MediaType; 22 | import okhttp3.Request; 23 | import okhttp3.Call; 24 | import okhttp3.Callback; 25 | import okhttp3.OkHttpClient; 26 | import okhttp3.RequestBody; 27 | import okhttp3.Response; 28 | 29 | public class JasonRequire implements Runnable{ 30 | final String URL; 31 | final Dictionary URLObject; 32 | final CountDownLatch latch; 33 | final Context context; 34 | final OkHttpClient client; 35 | final String method; 36 | final String keyURL; 37 | 38 | JSONObject private_refs; 39 | 40 | public JasonRequire(String url, CountDownLatch latch, JSONObject refs, OkHttpClient client, Context context) { 41 | this.URL = url.replace("\\", ""); 42 | this.keyURL = this.URL; 43 | this.latch = latch; 44 | this.private_refs = refs; 45 | this.context = context; 46 | this.client = client; 47 | this.URLObject = null; 48 | this.method = "get"; 49 | } 50 | 51 | /** 52 | * This constructor version takes an object instead of a string for the url 53 | * this url is typically from the mixin with [post] params used in JasonModel.java 54 | * @param url 55 | * @param latch 56 | * @param refs 57 | * @param client 58 | * @param context 59 | */ 60 | public JasonRequire(Dictionary url, CountDownLatch latch, JSONObject refs, OkHttpClient client, Context context) { 61 | this.URLObject = url; 62 | this.URL = ((String) url.get("parsed")).replace("\\", ""); 63 | this.method = (String) url.get("method"); 64 | this.keyURL = ((String) url.get("original")).replace("\\", "");; 65 | this.latch = latch; 66 | this.private_refs = refs; 67 | this.context = context; 68 | this.client = client; 69 | } 70 | 71 | public void run() { 72 | if(this.URL.contains("file://")) { 73 | local(); 74 | } else { 75 | remote(); 76 | } 77 | } 78 | private void local(){ 79 | Log.d("Debug", "Local file:// detected"); 80 | try { 81 | Runnable r = new Runnable() 82 | { 83 | @Override 84 | public void run() 85 | { 86 | Object json = JasonHelper.read_json(URL, context); 87 | try { 88 | private_refs.put(URL, json); 89 | } catch (Exception e) { 90 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 91 | } 92 | latch.countDown(); 93 | } 94 | }; 95 | Thread t = new Thread(r); 96 | t.start(); 97 | } catch (Exception e) { 98 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 99 | latch.countDown(); 100 | } 101 | } 102 | private void remote(){ 103 | Log.d("Debug", "Remote file detected"); 104 | Request request; 105 | Request.Builder builder = new Request.Builder(); 106 | 107 | // Session Handling 108 | try { 109 | SharedPreferences pref = context.getSharedPreferences("session", 0); 110 | JSONObject session = null; 111 | URI uri_for_session = new URI(this.URL); 112 | String session_domain = uri_for_session.getHost(); 113 | if(pref.contains(session_domain)){ 114 | String str = pref.getString(session_domain, null); 115 | session = new JSONObject(str); 116 | } 117 | 118 | // session.header 119 | if(session != null && session.has("header")) { 120 | Iterator keys = session.getJSONObject("header").keys(); 121 | while (keys.hasNext()) { 122 | String key = (String) keys.next(); 123 | String val = session.getJSONObject("header").getString(key); 124 | builder.addHeader(key, val); 125 | } 126 | } 127 | 128 | // session.body 129 | Uri.Builder b = Uri.parse(this.URL).buildUpon(); 130 | // Attach Params from Session 131 | if(session != null && session.has("body")) { 132 | Iterator keys = session.getJSONObject("body").keys(); 133 | while (keys.hasNext()) { 134 | String key = (String) keys.next(); 135 | String val = session.getJSONObject("body").getString(key); 136 | b.appendQueryParameter(key, val); 137 | } 138 | } 139 | 140 | Uri uri = b.build(); 141 | String url_with_session = uri.toString(); 142 | 143 | Log.d("Mixin", "Fetching: " + url_with_session); 144 | 145 | if(this.method.equals("post")) { 146 | Log.d("Mixin", "POST"); 147 | request = builder.url(url_with_session).post( 148 | RequestBody.create("{}", 149 | MediaType.parse("application/json") 150 | ) 151 | ).build(); 152 | 153 | } else { 154 | Log.d("Mixin", "GET"); 155 | request = builder 156 | .url(url_with_session) 157 | .build(); 158 | } 159 | 160 | // Actual call 161 | client.newCall(request).enqueue(new Callback() { 162 | @Override 163 | public void onFailure(Call call, IOException e) { 164 | latch.countDown(); 165 | e.printStackTrace(); 166 | } 167 | 168 | @Override 169 | public void onResponse(Call call, final Response response) throws IOException { 170 | if (!response.isSuccessful()) { 171 | latch.countDown(); 172 | throw new IOException("Unexpected code " + response); 173 | } 174 | try { 175 | String res = response.body().string(); 176 | Log.d("Body", res); 177 | // store the res under 178 | if(res.trim().startsWith("[")) { 179 | // array 180 | private_refs.put(keyURL, new JSONArray(res)); 181 | } else if(res.trim().startsWith("{")){ 182 | // object 183 | private_refs.put(keyURL, new JSONObject(res)); 184 | } else { 185 | // string 186 | private_refs.put(keyURL, res); 187 | } 188 | latch.countDown(); 189 | } catch (JSONException e) { 190 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 191 | } 192 | } 193 | }); 194 | 195 | } catch (Exception e){ 196 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Service/vision/JasonVisionService.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Service.vision; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import android.hardware.Camera; 7 | import android.os.Build; 8 | import androidx.core.app.ActivityCompat; 9 | import androidx.core.content.ContextCompat; 10 | import android.util.Log; 11 | import android.util.SparseArray; 12 | import android.view.Surface; 13 | import android.view.SurfaceHolder; 14 | import android.view.SurfaceView; 15 | 16 | import com.google.android.gms.vision.CameraSource; 17 | import com.google.android.gms.vision.Detector; 18 | import com.google.android.gms.vision.barcode.Barcode; 19 | import com.google.android.gms.vision.barcode.BarcodeDetector; 20 | import com.jasonette.seed.Core.JasonViewActivity; 21 | 22 | import org.json.JSONObject; 23 | 24 | 25 | /** 26 | * Created by realitix on 06/07/17. 27 | */ 28 | 29 | public class JasonVisionService { 30 | public static int FRONT = Camera.CameraInfo.CAMERA_FACING_FRONT; 31 | public static int BACK = Camera.CameraInfo.CAMERA_FACING_BACK; 32 | 33 | private Activity temp_context; 34 | private SurfaceHolder temp_holder; 35 | private int temp_side; 36 | 37 | private BarcodeDetector detector; 38 | private CameraSource cameraSource; 39 | private SurfaceView view; 40 | private int side; 41 | public boolean is_open; 42 | 43 | public JasonVisionService(Activity context) { 44 | initView(context); 45 | } 46 | 47 | public void setSide(final int side) { 48 | this.side = side; 49 | } 50 | 51 | public SurfaceView getView() { 52 | return view; 53 | } 54 | 55 | private void initView(final Activity context) { 56 | if (view != null) { 57 | return; 58 | } 59 | view = new SurfaceView(context); 60 | final SurfaceHolder holder = view.getHolder(); 61 | 62 | /* 63 | * Barcode detection 64 | * 65 | * When a code is recognized, this service: 66 | * 67 | * [1] triggers the event "$vision.onscan" with the following payload: 68 | * 69 | * { 70 | * "$jason": { 71 | * "type": "org.iso.QRCode", 72 | * "content": "hello world" 73 | * } 74 | * } 75 | * 76 | * the "type" attribute is different for iOS and Android. In case of Android it returns a number code specified at: 77 | * https://developers.google.com/android/reference/com/google/android/gms/vision/barcode/Barcode.html#constants 78 | * 79 | * [2] Then immediately stops scanning. 80 | * [3] To start scanning again, you need to call $vision.scan again 81 | * 82 | */ 83 | 84 | detector = new BarcodeDetector.Builder(context) 85 | .setBarcodeFormats(Barcode.ALL_FORMATS) 86 | .build(); 87 | detector.setProcessor(new Detector.Processor() { 88 | @Override 89 | public void release() { 90 | } 91 | 92 | @Override 93 | public void receiveDetections(Detector.Detections detections) { 94 | if (is_open) { 95 | final SparseArray detected_items = detections.getDetectedItems(); 96 | if (detected_items.size() != 0) { 97 | for (int i = 0; i < detected_items.size(); i++) { 98 | int key = detected_items.keyAt(i); 99 | final Barcode obj = detected_items.get(key); 100 | is_open = false; 101 | try { 102 | JSONObject payload = new JSONObject(); 103 | /* 104 | JSONArray corners = new JSONArray(); 105 | for (int j = 0; j < obj.cornerPoints.length; j++) { 106 | Point p = obj.cornerPoints[j]; 107 | JSONObject point = new JSONObject(); 108 | point.put("top", p.y); 109 | point.put("left", p.x); 110 | corners.put(point); 111 | } 112 | payload.put("corners", corners); 113 | */ 114 | payload.put("content", obj.rawValue); 115 | payload.put("type", obj.format); 116 | 117 | JSONObject response = new JSONObject(); 118 | response.put("$jason", payload); 119 | 120 | ((JasonViewActivity)context).simple_trigger("$vision.onscan", response, context); 121 | } catch (Exception e) { 122 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 123 | } 124 | return; 125 | } 126 | } 127 | } 128 | } 129 | }); 130 | holder.addCallback(new SurfaceHolder.Callback() { 131 | @Override 132 | public void surfaceCreated(SurfaceHolder surfaceHolder) { 133 | startCamera(context, surfaceHolder, side); 134 | } 135 | @Override 136 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { 137 | 138 | } 139 | @Override 140 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 141 | stopCamera(); 142 | } 143 | }); 144 | } 145 | 146 | private void startCamera(final Activity context, SurfaceHolder holder, final int side) { 147 | try { 148 | 149 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 150 | if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { 151 | temp_context = context; 152 | temp_holder = holder; 153 | temp_side = side; 154 | ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.CAMERA}, 50); 155 | } else { 156 | openCamera(context, holder, side); 157 | } 158 | } else { 159 | openCamera(context, holder, side); 160 | } 161 | 162 | 163 | } catch (Exception e) { 164 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 165 | } 166 | } 167 | 168 | public void startVision(Activity context) { 169 | openCamera(context, temp_holder, temp_side); 170 | temp_context = null; 171 | temp_holder = null; 172 | temp_side = -1; 173 | } 174 | 175 | void openCamera(Activity context, SurfaceHolder holder, final int side) { 176 | try { 177 | if (cameraSource != null) { 178 | cameraSource.stop(); 179 | } 180 | cameraSource = new CameraSource 181 | .Builder(context, detector) 182 | .setFacing(side) 183 | .setAutoFocusEnabled(true) 184 | .build(); 185 | cameraSource.start(holder); 186 | 187 | ((JasonViewActivity)context).simple_trigger("$vision.ready", new JSONObject(), context); 188 | 189 | } catch (Exception e) { 190 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 191 | } 192 | } 193 | 194 | 195 | public void stopCamera() { 196 | cameraSource.stop(); 197 | } 198 | 199 | private int getVerticalCameraDisplayOrientation(Activity context, int cameraId) { 200 | Camera.CameraInfo info = new Camera.CameraInfo(); 201 | Camera.getCameraInfo(cameraId, info); 202 | int rotation = context.getWindowManager().getDefaultDisplay().getRotation(); 203 | int degrees = 0; 204 | switch (rotation) { 205 | case Surface.ROTATION_0: degrees = 0; break; 206 | case Surface.ROTATION_90: degrees = 90; break; 207 | case Surface.ROTATION_180: degrees = 180; break; 208 | case Surface.ROTATION_270: degrees = 270; break; 209 | } 210 | 211 | int result; 212 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 213 | result = (info.orientation + degrees) % 360; 214 | result = (360 - result) % 360; // compensate the mirror 215 | } else { // back-facing 216 | result = (info.orientation - degrees + 360) % 360; 217 | } 218 | 219 | return result; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /app/src/main/assets/timers: -------------------------------------------------------------------------------- 1 | /* Implementation of HTML Timers (setInterval/setTimeout) based on sleep. 2 | * 3 | * This file is provided under the following terms (MIT License): 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | * Copyright 2012 Kevin Locke 23 | */ 24 | /*jslint bitwise: true, evil: true */ 25 | 26 | /** 27 | * Adds methods to implement the HTML5 WindowTimers interface on a given 28 | * object. 29 | * 30 | * Adds the following methods: 31 | *
    32 | *
  • clearInterval
  • 33 | *
  • clearTimeout
  • 34 | *
  • setInterval
  • 35 | *
  • setTimeout
  • 36 | *
37 | * 38 | * See http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html 39 | * for the complete specification of these methods. 40 | * 41 | * Example Usage 42 | * Browser compatibility in Rhino: 43 | *
// Note:  "this" refers to the global object in this example
 44 |  * var timerLoop = makeWindowTimer(this, java.lang.Thread.sleep);
 45 |  *
 46 |  * // Run code which may add intervals/timeouts
 47 |  *
 48 |  * timerLoop();
 49 |  * 
50 | * 51 | * Browser compatibility in SpiderMonkey (smjs): 52 | *
// Note:  "this" refers to the global object in this example
 53 |  * var timerLoop = makeWindowTimer(this, function (ms) { sleep(ms / 1000); });
 54 |  *
 55 |  * // Run code which may add intervals/timeouts
 56 |  *
 57 |  * timerLoop();
 58 |  * 
59 | * 60 | * For more esoteric uses, timerLoop will return instead of sleeping if passed 61 | * true which will run only events which are pending at the moment 62 | * timerLoop is called: 63 | *
// Note:  "this" refers to the global object in this example
 64 |  * var timerLoop = makeWindowTimer(this, java.lang.Thread.sleep);
 65 |  *
 66 |  * // Run code which may add intervals/timeouts
 67 |  *
 68 |  * while (timerLoop(true)) {
 69 |  *     print("Still waiting...");
 70 |  *     // Do other work here, possibly adding more intervals/timeouts
 71 |  * }
 72 |  * 
73 | * 74 | * @param {Object} target Object to which the methods should be added. 75 | * @param {Function} sleep A function which sleeps for a specified number of 76 | * milliseconds. 77 | * @return {Function} The function which runs the scheduled timers. 78 | */ 79 | function makeWindowTimer(target, sleep) { 80 | "use strict"; 81 | 82 | var counter = 1, 83 | inCallback = false, 84 | // Map handle -> timer 85 | timersByHandle = {}, 86 | // Min-heap of timers by time then handle, index 0 unused 87 | timersByTime = [ null ]; 88 | 89 | /** Compares timers based on scheduled time and handle. */ 90 | function timerCompare(t1, t2) { 91 | // Note: Only need less-than for our uses 92 | return t1.time < t2.time ? -1 : 93 | (t1.time === t2.time && t1.handle < t2.handle ? -1 : 0); 94 | } 95 | 96 | /** Fix the heap invariant which may be violated at a given index */ 97 | function heapFixDown(heap, i, lesscmp) { 98 | var j, tmp; 99 | 100 | j = i * 2; 101 | while (j < heap.length) { 102 | if (j + 1 < heap.length && 103 | lesscmp(heap[j + 1], heap[j]) < 0) { 104 | j = j + 1; 105 | } 106 | 107 | if (lesscmp(heap[i], heap[j]) < 0) { 108 | break; 109 | } 110 | 111 | tmp = heap[j]; 112 | heap[j] = heap[i]; 113 | heap[i] = tmp; 114 | i = j; 115 | j = i * 2; 116 | } 117 | } 118 | 119 | /** Fix the heap invariant which may be violated at a given index */ 120 | function heapFixUp(heap, i, lesscmp) { 121 | var j, tmp; 122 | while (i > 1) { 123 | j = i >> 1; // Integer div by 2 124 | 125 | if (lesscmp(heap[j], heap[i]) < 0) { 126 | break; 127 | } 128 | 129 | tmp = heap[j]; 130 | heap[j] = heap[i]; 131 | heap[i] = tmp; 132 | i = j; 133 | } 134 | } 135 | 136 | /** Remove the minimum element from the heap */ 137 | function heapPop(heap, lesscmp) { 138 | heap[1] = heap[heap.length - 1]; 139 | heap.pop(); 140 | heapFixDown(heap, 1, lesscmp); 141 | } 142 | 143 | /** Create a timer and schedule code to run at a given time */ 144 | function addTimer(code, delay, repeat, argsIfFn) { 145 | var handle, timer; 146 | 147 | if (typeof code !== "function") { 148 | code = String(code); 149 | argsIfFn = null; 150 | } 151 | delay = Number(delay) || 0; 152 | if (inCallback) { 153 | delay = Math.max(delay, 4); 154 | } 155 | // Note: Must set handle after argument conversion to properly 156 | // handle conformance test in HTML5 spec. 157 | handle = counter; 158 | counter += 1; 159 | 160 | timer = { 161 | args: argsIfFn, 162 | cancel: false, 163 | code: code, 164 | handle: handle, 165 | repeat: repeat ? Math.max(delay, 4) : 0, 166 | time: new Date().getTime() + delay 167 | }; 168 | 169 | timersByHandle[handle] = timer; 170 | timersByTime.push(timer); 171 | heapFixUp(timersByTime, timersByTime.length - 1, timerCompare); 172 | 173 | return handle; 174 | } 175 | 176 | /** Cancel an existing timer */ 177 | function cancelTimer(handle, repeat) { 178 | var timer; 179 | 180 | if (timersByHandle.hasOwnProperty(handle)) { 181 | timer = timersByHandle[handle]; 182 | if (repeat === (timer.repeat > 0)) { 183 | timer.cancel = true; 184 | } 185 | } 186 | } 187 | 188 | function clearInterval(handle) { 189 | cancelTimer(handle, true); 190 | } 191 | target.clearInterval = clearInterval; 192 | 193 | function clearTimeout(handle) { 194 | cancelTimer(handle, false); 195 | } 196 | target.clearTimeout = clearTimeout; 197 | 198 | function setInterval(code, delay) { 199 | return addTimer( 200 | code, 201 | delay, 202 | true, 203 | Array.prototype.slice.call(arguments, 2) 204 | ); 205 | } 206 | target.setInterval = setInterval; 207 | 208 | function setTimeout(code, delay) { 209 | return addTimer( 210 | code, 211 | delay, 212 | false, 213 | Array.prototype.slice.call(arguments, 2) 214 | ); 215 | } 216 | target.setTimeout = setTimeout; 217 | 218 | return function timerLoop(nonblocking) { 219 | var now, timer; 220 | 221 | // Note: index 0 unused in timersByTime 222 | while (timersByTime.length > 1) { 223 | timer = timersByTime[1]; 224 | 225 | if (timer.cancel) { 226 | delete timersByHandle[timer.handle]; 227 | heapPop(timersByTime, timerCompare); 228 | } else { 229 | now = new Date().getTime(); 230 | if (timer.time <= now) { 231 | inCallback = true; 232 | try { 233 | if (typeof timer.code === "function") { 234 | timer.code.apply(undefined, timer.args); 235 | } else { 236 | eval(timer.code); 237 | } 238 | } finally { 239 | inCallback = false; 240 | } 241 | 242 | if (timer.repeat > 0 && !timer.cancel) { 243 | timer.time += timer.repeat; 244 | heapFixDown(timersByTime, 1, timerCompare); 245 | } else { 246 | delete timersByHandle[timer.handle]; 247 | heapPop(timersByTime, timerCompare); 248 | } 249 | } else if (!nonblocking) { 250 | sleep(timer.time - now); 251 | } else { 252 | return true; 253 | } 254 | } 255 | } 256 | 257 | return false; 258 | }; 259 | } 260 | 261 | if (typeof exports === "object") { 262 | exports.makeWindowTimer = makeWindowTimer; 263 | } 264 | 265 | // vi: set sts=4 sw=4 et : -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonTextareaComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.graphics.Typeface; 6 | import android.text.Editable; 7 | import android.text.InputType; 8 | import android.text.TextUtils; 9 | import android.text.TextWatcher; 10 | import android.util.Log; 11 | import android.view.Gravity; 12 | import android.view.KeyEvent; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.view.inputmethod.EditorInfo; 16 | import android.widget.EditText; 17 | import android.widget.TextView; 18 | 19 | import androidx.localbroadcastmanager.content.LocalBroadcastManager; 20 | 21 | import com.jasonette.seed.Core.JasonViewActivity; 22 | import com.jasonette.seed.Helper.JasonHelper; 23 | 24 | import org.json.JSONObject; 25 | 26 | public class JasonTextareaComponent { 27 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context context) { 28 | if(view == null){ 29 | return new EditText(context); 30 | } else { 31 | try { 32 | view = JasonComponent.build(view, component, parent, context); 33 | 34 | JSONObject style = JasonHelper.style(component, context); 35 | String type = component.getString("type"); 36 | 37 | if (style.has("color")) { 38 | int color = JasonHelper.parse_color(style.getString("color")); 39 | ((TextView)view).setTextColor(color); 40 | } 41 | if (style.has("color:placeholder")) { 42 | int color = JasonHelper.parse_color(style.getString("color:placeholder")); 43 | ((TextView)view).setHintTextColor(color); 44 | } 45 | 46 | if (style.has("font:android")){ 47 | String f = style.getString("font:android"); 48 | if(f.equalsIgnoreCase("bold")){ 49 | ((TextView) view).setTypeface(Typeface.DEFAULT_BOLD); 50 | } else if(f.equalsIgnoreCase("sans")){ 51 | ((TextView) view).setTypeface(Typeface.SANS_SERIF); 52 | } else if(f.equalsIgnoreCase("serif")){ 53 | ((TextView) view).setTypeface(Typeface.SERIF); 54 | } else if(f.equalsIgnoreCase("monospace")){ 55 | ((TextView) view).setTypeface(Typeface.MONOSPACE); 56 | } else if(f.equalsIgnoreCase("default")){ 57 | ((TextView) view).setTypeface(Typeface.DEFAULT); 58 | } else { 59 | try { 60 | Typeface font_type = Typeface.createFromAsset(context.getAssets(), "fonts/" + style.getString("font:android") + ".ttf"); 61 | ((TextView) view).setTypeface(font_type); 62 | } catch (Exception e) { 63 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 64 | } 65 | } 66 | } else if (style.has("font")){ 67 | if(style.getString("font").toLowerCase().contains("bold")) { 68 | if (style.getString("font").toLowerCase().contains("italic")) { 69 | ((TextView) view).setTypeface(Typeface.DEFAULT_BOLD, Typeface.ITALIC); 70 | } else { 71 | ((TextView) view).setTypeface(Typeface.DEFAULT_BOLD); 72 | } 73 | } else { 74 | if (style.getString("font").toLowerCase().contains("italic")) { 75 | ((TextView) view).setTypeface(Typeface.DEFAULT, Typeface.ITALIC); 76 | } else { 77 | ((TextView) view).setTypeface(Typeface.DEFAULT); 78 | } 79 | } 80 | } 81 | 82 | if (!style.has("height")) { 83 | ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams)view.getLayoutParams(); 84 | layoutParams.height = 300; 85 | view.setLayoutParams(layoutParams); 86 | } 87 | 88 | int g = 0; 89 | if (style.has("align")) { 90 | String align = style.getString("align"); 91 | if (align.equalsIgnoreCase("center")) { 92 | g = g | Gravity.CENTER_HORIZONTAL; 93 | ((TextView) view).setGravity(Gravity.CENTER_HORIZONTAL); 94 | } else if (align.equalsIgnoreCase("right")) { 95 | g = g | Gravity.RIGHT; 96 | ((TextView) view).setGravity(Gravity.RIGHT); 97 | } else { 98 | g = g | Gravity.LEFT; 99 | } 100 | 101 | if (align.equalsIgnoreCase("bottom")) { 102 | g = g | Gravity.BOTTOM; 103 | } else { 104 | g = g | Gravity.TOP; 105 | } 106 | } else { 107 | g = Gravity.TOP | Gravity.LEFT; 108 | } 109 | 110 | ((EditText)view).setGravity(g); 111 | 112 | if (style.has("size")) { 113 | ((EditText)view).setTextSize(Float.parseFloat(style.getString("size"))); 114 | } 115 | 116 | 117 | ((EditText)view).setEllipsize(TextUtils.TruncateAt.END); 118 | 119 | 120 | // placeholder 121 | if(component.has("placeholder")){ 122 | ((EditText)view).setHint(component.getString("placeholder")); 123 | } 124 | 125 | // default value 126 | if(component.has("value")){ 127 | ((EditText)view).setText(component.getString("value")); 128 | } 129 | 130 | // keyboard 131 | if(component.has("keyboard")){ 132 | String keyboard = component.getString("keyboard"); 133 | if(keyboard.equalsIgnoreCase("text")) { 134 | ((EditText) view).setInputType(InputType.TYPE_CLASS_TEXT); 135 | } else if(keyboard.equalsIgnoreCase("number")) { 136 | ((EditText) view).setInputType(InputType.TYPE_CLASS_NUMBER); 137 | } else if(keyboard.equalsIgnoreCase("decimal")) { 138 | ((EditText) view).setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_DECIMAL); 139 | } else if(keyboard.equalsIgnoreCase("phone")) { 140 | ((EditText) view).setInputType(InputType.TYPE_CLASS_PHONE); 141 | } else if(keyboard.equalsIgnoreCase("url")) { 142 | ((EditText) view).setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); 143 | } else if(keyboard.equalsIgnoreCase("email")) { 144 | ((EditText) view).setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); 145 | } 146 | } 147 | 148 | // Data binding 149 | if(component.has("name")){ 150 | ((EditText)view).addTextChangedListener(new TextWatcher() { 151 | @Override 152 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 153 | 154 | } 155 | 156 | @Override 157 | public void onTextChanged(CharSequence s, int start, int before, int count) { 158 | 159 | } 160 | 161 | @Override 162 | public void afterTextChanged(Editable s) { 163 | try { 164 | ((JasonViewActivity) context).model.var.put(component.getString("name"), s.toString()); 165 | if(component.has("on")){ 166 | JSONObject events = component.getJSONObject("on"); 167 | if(events.has("change")){ 168 | Intent intent = new Intent("call"); 169 | intent.putExtra("action", events.getJSONObject("change").toString()); 170 | LocalBroadcastManager.getInstance(context).sendBroadcast(intent); 171 | } 172 | } 173 | } catch (Exception e){ 174 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 175 | } 176 | } 177 | }); 178 | } 179 | ((EditText)view).setOnEditorActionListener(new TextView.OnEditorActionListener() { 180 | @Override 181 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 182 | if (actionId == EditorInfo.IME_ACTION_DONE) { 183 | try { 184 | if(component.has("name")) { 185 | ((JasonViewActivity) context).model.var.put(component.getString("name"), v.getText().toString()); 186 | } 187 | } catch (Exception e){ 188 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 189 | } 190 | } 191 | 192 | return false; 193 | } 194 | }); 195 | 196 | view.requestLayout(); 197 | return view; 198 | } catch (Exception e){ 199 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 200 | return new View(context); 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /app/src/main/java/com/jasonette/seed/Component/JasonComponent.java: -------------------------------------------------------------------------------- 1 | package com.jasonette.seed.Component; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.GradientDrawable; 5 | 6 | import androidx.core.content.ContextCompat; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.widget.LinearLayout; 10 | import android.widget.RelativeLayout; 11 | 12 | import com.jasonette.seed.Core.JasonViewActivity; 13 | import com.jasonette.seed.Helper.JasonHelper; 14 | import com.jasonette.seed.Section.JasonLayout; 15 | 16 | import org.json.JSONObject; 17 | 18 | public class JasonComponent { 19 | 20 | public static final String INTENT_ACTION_CALL = "call"; 21 | public static final String ACTION_PROP = "action"; 22 | public static final String DATA_PROP = "data"; 23 | public static final String HREF_PROP = "href"; 24 | public static final String TYPE_PROP = "type"; 25 | public static final String OPTIONS_PROP = "options"; 26 | 27 | public static View build(View view, final JSONObject component, final JSONObject parent, final Context root_context) { 28 | float width = 0; 29 | float height = 0; 30 | int corner_radius = 0; 31 | 32 | view.setTag(component); 33 | JSONObject style = JasonHelper.style(component, root_context); 34 | 35 | try{ 36 | if(parent == null) { 37 | // Layer type 38 | width = RelativeLayout.LayoutParams.WRAP_CONTENT; 39 | height = RelativeLayout.LayoutParams.WRAP_CONTENT; 40 | if (style.has("height")) { 41 | try { 42 | height = (int) JasonHelper.pixels(root_context, style.getString("height"), "vertical"); 43 | if (style.has("ratio")) { 44 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 45 | width = height * ratio; 46 | } 47 | } catch (Exception e) { 48 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 49 | } 50 | } 51 | if (style.has("width")) { 52 | try { 53 | width = (int) JasonHelper.pixels(root_context, style.getString("width"), "horizontal"); 54 | if (style.has("ratio")) { 55 | Float ratio = JasonHelper.ratio(style.getString("ratio")); 56 | height = width / ratio; 57 | } 58 | } catch (Exception e) { 59 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 60 | } 61 | } 62 | 63 | RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams((int)width, (int)height); 64 | view.setLayoutParams(layoutParams); 65 | } else { 66 | // Section item type 67 | LinearLayout.LayoutParams layoutParams = JasonLayout.autolayout(null, parent, component, root_context); 68 | view.setLayoutParams(layoutParams); 69 | } 70 | 71 | int bgcolor; 72 | if (style.has("background")) { 73 | int color = JasonHelper.parse_color(style.getString("background")); 74 | bgcolor = color; 75 | view.setBackgroundColor(color); 76 | } else { 77 | bgcolor = JasonHelper.parse_color("rgba(0,0,0,0)"); 78 | } 79 | if(style.has("opacity")) 80 | { 81 | try { 82 | float opacity = (float) style.getDouble("opacity"); 83 | view.setAlpha(opacity); 84 | } 85 | catch (Exception ex) { 86 | } 87 | } 88 | 89 | 90 | // padding 91 | int padding_left = (int)JasonHelper.pixels(root_context, "0", "horizontal"); 92 | int padding_right = (int)JasonHelper.pixels(root_context, "0", "horizontal"); 93 | int padding_top = (int)JasonHelper.pixels(root_context, "0", "horizontal"); 94 | int padding_bottom = (int)JasonHelper.pixels(root_context, "0", "horizontal"); 95 | if (style.has("padding")) { 96 | padding_left = (int)JasonHelper.pixels(root_context, style.getString("padding"), "horizontal"); 97 | padding_right = padding_left; 98 | padding_top = padding_left; 99 | padding_bottom = padding_left; 100 | } 101 | 102 | // overwrite if more specific values exist 103 | if (style.has("padding_left")) { 104 | padding_left = (int)JasonHelper.pixels(root_context, style.getString("padding_left"), "horizontal"); 105 | } 106 | if (style.has("padding_right")) { 107 | padding_right = (int)JasonHelper.pixels(root_context, style.getString("padding_right"), "horizontal"); 108 | } 109 | if (style.has("padding_top")) { 110 | padding_top = (int)JasonHelper.pixels(root_context, style.getString("padding_top"), "vertical"); 111 | } 112 | if (style.has("padding_bottom")) { 113 | padding_bottom = (int)JasonHelper.pixels(root_context, style.getString("padding_bottom"), "vertical"); 114 | } 115 | 116 | if (style.has("corner_radius")) { 117 | float corner = JasonHelper.pixels(root_context, style.getString("corner_radius"), "horizontal"); 118 | int color = ContextCompat.getColor(root_context, android.R.color.transparent); 119 | GradientDrawable cornerShape = new GradientDrawable(); 120 | cornerShape.setShape(GradientDrawable.RECTANGLE); 121 | if (style.has("background")) { 122 | color = JasonHelper.parse_color(style.getString("background")); 123 | } 124 | cornerShape.setColor(color); 125 | cornerShape.setCornerRadius(corner); 126 | 127 | // border + corner_radius handling 128 | if (style.has("border_width")){ 129 | int border_width = (int)JasonHelper.pixels(root_context, style.getString("border_width"), "horizontal"); 130 | if(border_width > 0){ 131 | int border_color; 132 | if (style.has("border_color")){ 133 | border_color = JasonHelper.parse_color(style.getString("border_color")); 134 | } else { 135 | border_color = JasonHelper.parse_color("#000000"); 136 | } 137 | cornerShape.setStroke(border_width, border_color); 138 | } 139 | } 140 | cornerShape.invalidateSelf(); 141 | view.setBackground(cornerShape); 142 | } else { 143 | // border handling (no corner radius) 144 | if (style.has("border_width")){ 145 | int border_width = (int)JasonHelper.pixels(root_context, style.getString("border_width"), "horizontal"); 146 | if(border_width > 0){ 147 | int border_color; 148 | if (style.has("border_color")){ 149 | border_color = JasonHelper.parse_color(style.getString("border_color")); 150 | } else { 151 | border_color = JasonHelper.parse_color("#000000"); 152 | } 153 | GradientDrawable cornerShape = new GradientDrawable(); 154 | cornerShape.setStroke(border_width, border_color); 155 | cornerShape.setColor(bgcolor); 156 | cornerShape.invalidateSelf(); 157 | view.setBackground(cornerShape); 158 | } 159 | } 160 | 161 | } 162 | 163 | view.setPadding(padding_left, padding_top, padding_right, padding_bottom); 164 | return view; 165 | 166 | } catch (Exception e){ 167 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 168 | return new View(root_context); 169 | } 170 | } 171 | public static void addListener(final View view, final Context root_context){ 172 | View.OnClickListener clickListener = new View.OnClickListener() { 173 | public void onClick(View v) { 174 | JSONObject component = (JSONObject)v.getTag(); 175 | try { 176 | if (component.has("action")) { 177 | JSONObject action = component.getJSONObject("action"); 178 | ((JasonViewActivity) root_context).call(action.toString(), new JSONObject().toString(), "{}", v.getContext()); 179 | } else if (component.has("href")) { 180 | JSONObject href = component.getJSONObject("href"); 181 | JSONObject action = new JSONObject().put("type", "$href").put("options", href); 182 | ((JasonViewActivity) root_context).call(action.toString(), new JSONObject().toString(), "{}", v.getContext()); 183 | } else { 184 | // NONE Explicitly stated. 185 | // Need to bubble up all the way to the root viewholder. 186 | View cursor = view; 187 | while(cursor.getParent() != null) { 188 | JSONObject item = (JSONObject)(((View)cursor.getParent()).getTag()); 189 | if (item!=null && (item.has("action") || item.has("href"))) { 190 | ((View)cursor.getParent()).performClick(); 191 | break; 192 | } else { 193 | cursor = (View) cursor.getParent(); 194 | } 195 | } 196 | } 197 | } catch (Exception e) { 198 | Log.d("Warning", e.getStackTrace()[0].getMethodName() + " : " + e.toString()); 199 | } 200 | } 201 | }; 202 | view.setOnClickListener(clickListener); 203 | } 204 | } 205 | --------------------------------------------------------------------------------