diff --git a/doric-android/.gitignore b/doric-android/.gitignore index 0df7064d..d432ee46 100644 --- a/doric-android/.gitignore +++ b/doric-android/.gitignore @@ -6,3 +6,6 @@ /build /captures .externalNativeBuild +.project +.classpath +.settings/ \ No newline at end of file diff --git a/doric-android/LICENSE b/doric-android/LICENSE new file mode 100644 index 00000000..97846020 --- /dev/null +++ b/doric-android/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2019] [Doric.Pub] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/doric-android/README.md b/doric-android/README.md new file mode 100644 index 00000000..7e468194 --- /dev/null +++ b/doric-android/README.md @@ -0,0 +1,3 @@ +# Doric Android SDK + +## Usages \ No newline at end of file diff --git a/doric-android/app/build.gradle b/doric-android/app/build.gradle index 3f343c91..eaadc388 100644 --- a/doric-android/app/build.gradle +++ b/doric-android/app/build.gradle @@ -23,7 +23,8 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation "com.google.android.material:material:1.0.0" - implementation project(':devkit') + implementation "pub.doric:core:${rootProject.ext.Version}" + implementation "pub.doric:devkit:${rootProject.ext.Version}" implementation 'com.github.bumptech.glide:glide:4.10.0' implementation 'com.github.bumptech.glide:annotations:4.10.0' implementation 'com.github.penfeizhou.android.animation:glide-plugin:1.3.1' diff --git a/doric-android/app/src/main/AndroidManifest.xml b/doric-android/app/src/main/AndroidManifest.xml index 3988de39..d35dadc4 100644 --- a/doric-android/app/src/main/AndroidManifest.xml +++ b/doric-android/app/src/main/AndroidManifest.xml @@ -21,15 +21,6 @@ - - - - - - - \ No newline at end of file diff --git a/doric-android/app/src/main/assets/demo b/doric-android/app/src/main/assets/demo deleted file mode 120000 index 6bd58cbd..00000000 --- a/doric-android/app/src/main/assets/demo +++ /dev/null @@ -1 +0,0 @@ -../../../../../demo/bundle/src \ No newline at end of file diff --git a/doric-android/app/src/main/assets/demo/.gitignore b/doric-android/app/src/main/assets/demo/.gitignore new file mode 100644 index 00000000..4c43fe68 --- /dev/null +++ b/doric-android/app/src/main/assets/demo/.gitignore @@ -0,0 +1 @@ +*.js \ No newline at end of file diff --git a/doric-android/app/src/main/java/pub/doric/demo/MainActivity.java b/doric-android/app/src/main/java/pub/doric/demo/MainActivity.java index b9153c44..0ead1f9a 100644 --- a/doric-android/app/src/main/java/pub/doric/demo/MainActivity.java +++ b/doric-android/app/src/main/java/pub/doric/demo/MainActivity.java @@ -33,7 +33,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import pub.doric.DoricActivity; import pub.doric.devkit.ui.DemoDebugActivity; import pub.doric.refresh.DoricSwipeLayout; import pub.doric.utils.DoricUtils; @@ -63,7 +62,6 @@ public class MainActivity extends AppCompatActivity { try { String[] demos = getAssets().list("demo"); List ret = new ArrayList<>(); - ret.add("Test"); for (String str : demos) { if (str.endsWith("js")) { ret.add(str); @@ -107,20 +105,9 @@ public class MainActivity extends AppCompatActivity { tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (data[position].contains("Test")) { - Intent intent = new Intent(tv.getContext(), PullableActivity.class); - tv.getContext().startActivity(intent); - return; - } - if (data[position].contains("NavigatorDemo")) { - Intent intent = new Intent(tv.getContext(), DoricActivity.class); - intent.putExtra("scheme", "assets://demo/" + data[position]); - intent.putExtra("alias", data[position]); - tv.getContext().startActivity(intent); - return; - } Intent intent = new Intent(tv.getContext(), DemoDebugActivity.class); - intent.putExtra("source", data[position]); + intent.putExtra("scheme", "assets://demo/" + data[position]); + intent.putExtra("alias", data[position]); tv.getContext().startActivity(intent); } }); diff --git a/doric-android/build.gradle b/doric-android/build.gradle index 143e9239..1dd61928 100644 --- a/doric-android/build.gradle +++ b/doric-android/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.2' + classpath 'com.android.tools.build:gradle:3.5.3' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -23,8 +23,41 @@ allprojects { } maven { url 'https://jitpack.io' } } + def needReplaceLocal = true + gradle.startParameter.taskNames.each { + if (it.contains('uploadArchives')) { + needReplaceLocal = false + } + } + if (needReplaceLocal) { + configurations.all { + resolutionStrategy { + dependencySubstitution { + substitute module('pub.doric:core') with project(":doric") + substitute module('pub.doric:devkit') with project(":devkit") + } + } + } + } } task clean(type: Delete) { delete rootProject.buildDir } +Properties properties = new Properties() +properties.load(project.rootProject.file('version.properties').newDataInputStream()) +ext { + Version = properties.version +} + +task publisCore dependsOn(":doric:uploadArchives") { + println "doric published" +} + +task publishDevKit dependsOn(publisCore, ":devkit:uploadArchives") { + println "devkit published" +} + +task PublishAll dependsOn(publisCore, publishDevKit) { + println "All published" +} diff --git a/doric-android/devkit/build.gradle b/doric-android/devkit/build.gradle index 76882f07..c610fbd3 100644 --- a/doric-android/devkit/build.gradle +++ b/doric-android/devkit/build.gradle @@ -1,7 +1,5 @@ apply plugin: 'com.android.library' -def projectHome = project.rootDir.getParent() + "/demo" - android { compileSdkVersion 29 buildToolsVersion "29.0.2" @@ -22,10 +20,6 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } - - debug { - buildConfigField "String", "PROJECT_HOME", "\"${projectHome}\"" - } } } @@ -33,7 +27,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.github.pengfeizhou:jsc4a:0.1.0' + implementation "pub.doric:core:${rootProject.ext.Version}" implementation 'androidx.appcompat:appcompat:1.1.0' implementation "com.google.android.material:material:1.0.0" implementation 'com.squareup.okhttp3:okhttp:4.2.2' @@ -42,11 +36,10 @@ dependencies { implementation 'com.github.tbruyelle:rxpermissions:0.10.2' implementation "io.reactivex.rxjava2:rxjava:2.2.15" api 'org.greenrobot:eventbus:3.1.1' - implementation 'com.lahm.library:easy-protector-release:1.1.0' - api 'org.nanohttpd:nanohttpd:2.3.1' - api project(':doric') - + implementation 'com.lahm.library:easy-protector-release:1.1.1' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } + +apply from: rootProject.file('scripts/upload.gradle') \ No newline at end of file diff --git a/doric-android/devkit/pom.properties b/doric-android/devkit/pom.properties new file mode 100644 index 00000000..cbd78aa8 --- /dev/null +++ b/doric-android/devkit/pom.properties @@ -0,0 +1,3 @@ +name=DoricDevKit +groupId=pub.doric +artifactId=devkit \ No newline at end of file diff --git a/doric-android/devkit/src/main/AndroidManifest.xml b/doric-android/devkit/src/main/AndroidManifest.xml index 70d23e89..db8ffc55 100644 --- a/doric-android/devkit/src/main/AndroidManifest.xml +++ b/doric-android/devkit/src/main/AndroidManifest.xml @@ -2,9 +2,12 @@ package="pub.doric.devkit"> + - + diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/DoricContextDebuggable.java b/doric-android/devkit/src/main/java/pub/doric/devkit/DoricContextDebuggable.java index 1ff0b20a..b98ef9ab 100644 --- a/doric-android/devkit/src/main/java/pub/doric/devkit/DoricContextDebuggable.java +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/DoricContextDebuggable.java @@ -1,14 +1,17 @@ package pub.doric.devkit; import pub.doric.DoricContext; +import pub.doric.DoricContextManager; import pub.doric.DoricNativeDriver; public class DoricContextDebuggable { private DoricContext doricContext; private DoricDebugDriver doricDebugDriver; + public boolean isDebugging = false; - public DoricContextDebuggable(DoricContext doricContext) { - this.doricContext = doricContext; + public DoricContextDebuggable(String contextId) { + this.doricContext = DoricContextManager.getContext(contextId); + isDebugging = true; } public void startDebug() { @@ -22,8 +25,13 @@ public class DoricContextDebuggable { } public void stopDebug() { + isDebugging = false; doricDebugDriver.destroy(); doricContext.setDriver(DoricNativeDriver.getInstance()); doricContext.reInit(); } + + public DoricContext getContext() { + return doricContext; + } } diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/WSClient.java b/doric-android/devkit/src/main/java/pub/doric/devkit/WSClient.java index dd81e587..cbc5cd9c 100644 --- a/doric-android/devkit/src/main/java/pub/doric/devkit/WSClient.java +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/WSClient.java @@ -79,6 +79,7 @@ public class WSClient extends WebSocketListener { } break; case "SWITCH_TO_DEBUG": { + String contextId = jsonObject.optString("contextId"); EventBus.getDefault().post(new EnterDebugEvent()); } break; diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/event/EnterDebugEvent.java b/doric-android/devkit/src/main/java/pub/doric/devkit/event/EnterDebugEvent.java index 5e02b0d0..13684c82 100644 --- a/doric-android/devkit/src/main/java/pub/doric/devkit/event/EnterDebugEvent.java +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/event/EnterDebugEvent.java @@ -1,4 +1,5 @@ package pub.doric.devkit.event; public class EnterDebugEvent { + } diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/event/StartDebugEvent.java b/doric-android/devkit/src/main/java/pub/doric/devkit/event/StartDebugEvent.java new file mode 100644 index 00000000..9e5b754e --- /dev/null +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/event/StartDebugEvent.java @@ -0,0 +1,13 @@ +package pub.doric.devkit.event; + +public class StartDebugEvent { + private String contextId; + + public StartDebugEvent(String contextId) { + this.contextId = contextId; + } + + public String getContextId() { + return contextId; + } +} diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/event/StopDebugEvent.java b/doric-android/devkit/src/main/java/pub/doric/devkit/event/StopDebugEvent.java new file mode 100644 index 00000000..db373cee --- /dev/null +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/event/StopDebugEvent.java @@ -0,0 +1,5 @@ +package pub.doric.devkit.event; + +public class StopDebugEvent { + +} diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/remote/RemoteJSExecutor.java b/doric-android/devkit/src/main/java/pub/doric/devkit/remote/RemoteJSExecutor.java index 73039511..6db72af6 100644 --- a/doric-android/devkit/src/main/java/pub/doric/devkit/remote/RemoteJSExecutor.java +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/remote/RemoteJSExecutor.java @@ -25,7 +25,7 @@ import okhttp3.WebSocket; import okhttp3.WebSocketListener; import pub.doric.devkit.DevKit; import pub.doric.devkit.IStatusCallback; -import pub.doric.devkit.event.QuitDebugEvent; +import pub.doric.devkit.event.StopDebugEvent; public class RemoteJSExecutor { private final WebSocket webSocket; @@ -59,7 +59,7 @@ public class RemoteJSExecutor { System.out.println("remote js executor eof"); LockSupport.unpark(current); - EventBus.getDefault().post(new QuitDebugEvent()); + EventBus.getDefault().post(new StopDebugEvent()); } } diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DebugContextPanel.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DebugContextPanel.java index 3e372463..eec9e225 100644 --- a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DebugContextPanel.java +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DebugContextPanel.java @@ -15,12 +15,14 @@ import androidx.fragment.app.DialogFragment; import com.google.gson.JsonObject; +import org.greenrobot.eventbus.EventBus; + import pub.doric.DoricContext; import pub.doric.DoricContextManager; -import pub.doric.devkit.BuildConfig; import pub.doric.devkit.DoricDev; import pub.doric.devkit.IDevKit; import pub.doric.devkit.R; +import pub.doric.devkit.event.StartDebugEvent; public class DebugContextPanel extends DialogFragment { @@ -69,9 +71,9 @@ public class DebugContextPanel extends DialogFragment { cell.findViewById(R.id.debug_text_view).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + EventBus.getDefault().post(new StartDebugEvent(doricContext.getContextId())); JsonObject jsonObject = new JsonObject(); jsonObject.addProperty("contextId", doricContext.getContextId()); - jsonObject.addProperty("projectHome", BuildConfig.PROJECT_HOME); jsonObject.addProperty("source", doricContext.getSource().replace(".js", ".ts")); DoricDev.sendDevCommand(IDevKit.Command.DEBUG, jsonObject); dismissAllowingStateLoss(); diff --git a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DemoDebugActivity.java b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DemoDebugActivity.java index 135e4cf0..06feba1e 100644 --- a/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DemoDebugActivity.java +++ b/doric-android/devkit/src/main/java/pub/doric/devkit/ui/DemoDebugActivity.java @@ -17,46 +17,36 @@ package pub.doric.devkit.ui; import android.os.Bundle; import android.view.KeyEvent; -import android.view.ViewGroup; import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import pub.doric.DoricActivity; import pub.doric.DoricContext; import pub.doric.DoricContextManager; -import pub.doric.DoricPanel; import pub.doric.devkit.DoricContextDebuggable; import pub.doric.devkit.event.EnterDebugEvent; -import pub.doric.devkit.event.QuitDebugEvent; import pub.doric.devkit.event.ReloadEvent; +import pub.doric.devkit.event.StartDebugEvent; +import pub.doric.devkit.event.StopDebugEvent; import pub.doric.devkit.util.SensorManagerHelper; -import pub.doric.utils.DoricUtils; /** * @Description: pub.doric.demo * @Author: pengfei.zhou * @CreateDate: 2019-11-19 */ -public class DemoDebugActivity extends AppCompatActivity { - private DoricContext doricContext; +public class DemoDebugActivity extends DoricActivity { private SensorManagerHelper sensorHelper; private DoricContextDebuggable doricContextDebuggable; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - String source = getIntent().getStringExtra("source"); - DoricPanel doricPanel = new DoricPanel(this); - addContentView(doricPanel, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - doricPanel.config(DoricUtils.readAssetFile("demo/" + source), source); - doricContext = doricPanel.getDoricContext(); - doricContextDebuggable = new DoricContextDebuggable(doricContext); sensorHelper = new SensorManagerHelper(this); sensorHelper.setOnShakeListener(new SensorManagerHelper.OnShakeListener() { @Override @@ -73,34 +63,49 @@ public class DemoDebugActivity extends AppCompatActivity { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); - EventBus.getDefault().register(this); } + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + EventBus.getDefault().unregister(this); + } + @Override protected void onDestroy() { super.onDestroy(); - EventBus.getDefault().unregister(this); sensorHelper.stop(); } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onStartDebugEvent(StartDebugEvent startDebugEvent) { + doricContextDebuggable = new DoricContextDebuggable(startDebugEvent.getContextId()); + } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEnterDebugEvent(EnterDebugEvent enterDebugEvent) { doricContextDebuggable.startDebug(); } @Subscribe(threadMode = ThreadMode.MAIN) - public void onReloadEvent(ReloadEvent reloadEvent) { - for (DoricContext context : DoricContextManager.aliveContexts()) { - if (reloadEvent.source.contains(context.getSource())) { - context.reload(reloadEvent.script); - } - } + public void onQuitDebugEvent(StopDebugEvent quitDebugEvent) { + doricContextDebuggable.stopDebug(); } @Subscribe(threadMode = ThreadMode.MAIN) - public void onQuitDebugEvent(QuitDebugEvent quitDebugEvent) { - doricContextDebuggable.stopDebug(); + public void onReloadEvent(ReloadEvent reloadEvent) { + for (DoricContext context : DoricContextManager.aliveContexts()) { + if (reloadEvent.source.contains(context.getSource())) { + if (doricContextDebuggable != null && + doricContextDebuggable.isDebugging && + doricContextDebuggable.getContext().getContextId().equals(context.getContextId())) { + System.out.println("is debugging context id: " + context.getContextId()); + } else { + context.reload(reloadEvent.script); + } + } + } } @Override diff --git a/doric-android/doric/build.gradle b/doric-android/doric/build.gradle index 1b925089..cdb16cf7 100644 --- a/doric-android/doric/build.gradle +++ b/doric-android/doric/build.gradle @@ -25,35 +25,31 @@ android { afterEvaluate { buildJSBundle.exec() - buildDemo.exec() - //buildDebugger.exec() } task buildJSBundle(type: Exec) { - workingDir project.rootDir.getParent() + "/js-framework" - commandLine 'npm', 'run', 'build' + commandLine 'sh', project.rootDir.getParent() + "/bundle.sh" } -task buildDemo(type: Exec) { - workingDir project.rootDir.getParent() + "/demo" - commandLine 'npm', 'run', 'build' -} - -task buildDebugger(type: Exec) { - workingDir project.rootDir.getParent() + "/debugger" - commandLine 'npm', 'run', 'build' -} dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' - api 'com.github.pengfeizhou:jsc4a:0.1.0' + api 'com.github.penfeizhou:jsc4a:0.1.0' implementation 'com.squareup.okhttp3:okhttp:4.2.2' implementation 'com.github.penfeizhou.android.animation:glide-plugin:1.3.1' + implementation 'jp.wasabeef:glide-transformations:4.1.0' implementation 'com.google.code.gson:gson:2.8.6' implementation "com.google.android.material:material:1.0.0" - testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + + + def nav_version = "2.1.0" + + implementation "androidx.navigation:navigation-fragment:$nav_version" + implementation "androidx.navigation:navigation-ui:$nav_version" } + +apply from: rootProject.file('scripts/upload.gradle') \ No newline at end of file diff --git a/doric-android/doric/pom.properties b/doric-android/doric/pom.properties new file mode 100644 index 00000000..20fbf2a9 --- /dev/null +++ b/doric-android/doric/pom.properties @@ -0,0 +1,4 @@ +name=Doric +groupId=pub.doric +artifactId=core +version=0.1.0 \ No newline at end of file diff --git a/doric-android/doric/src/main/AndroidManifest.xml b/doric-android/doric/src/main/AndroidManifest.xml index 44741555..f777c8f1 100644 --- a/doric-android/doric/src/main/AndroidManifest.xml +++ b/doric-android/doric/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ - mHeadNodes = new HashMap<>(); @@ -72,10 +72,11 @@ public class DoricContext { return mHeadNodes.get(id); } - protected DoricContext(Context context, String contextId, String source) { + protected DoricContext(Context context, String contextId, String source, String extra) { this.mContext = context; this.mContextId = contextId; this.source = source; + this.extra = extra; } public String getSource() { @@ -86,22 +87,25 @@ public class DoricContext { return script; } - public static DoricContext create(Context context, String script, String source) { - DoricContext doricContext = DoricContextManager.getInstance().createContext(context, script, source); + public static DoricContext create(Context context, String script, String source, String extra) { + DoricContext doricContext = DoricContextManager.getInstance().createContext(context, script, source, extra); doricContext.script = script; + doricContext.extra = extra; return doricContext; } public void init(float width, float height) { this.initParams = new JSONBuilder() .put("width", width) - .put("height", height).toJSONObject(); - callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams); + .put("height", height) + .toJSONObject(); + callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams, extra); callEntity(DoricConstant.DORIC_ENTITY_CREATE); } public void reInit() { - callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams); + this.mRootNode.setId(""); + callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams, extra); callEntity(DoricConstant.DORIC_ENTITY_CREATE); } @@ -165,7 +169,8 @@ public class DoricContext { this.script = script; this.mRootNode.setId(""); getDriver().createContext(mContextId, script, source); - callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams); + callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams, extra); + onShow(); } public void onShow() { diff --git a/doric-android/doric/src/main/java/pub/doric/DoricContextManager.java b/doric-android/doric/src/main/java/pub/doric/DoricContextManager.java index 3176d4b8..a3046720 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricContextManager.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricContextManager.java @@ -48,9 +48,9 @@ public class DoricContextManager { return Inner.sInstance; } - DoricContext createContext(Context context, final String script, final String source) { + DoricContext createContext(Context context, final String script, final String source, String extra) { final String contextId = String.valueOf(counter.incrementAndGet()); - final DoricContext doricContext = new DoricContext(context, contextId, source); + final DoricContext doricContext = new DoricContext(context, contextId, source, extra); doricContextMap.put(contextId, doricContext); doricContext.getDriver().createContext(contextId, script, source); return doricContext; diff --git a/doric-android/doric/src/main/java/pub/doric/DoricFragment.java b/doric-android/doric/src/main/java/pub/doric/DoricFragment.java index e64117b3..3d7f5661 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricFragment.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricFragment.java @@ -20,18 +20,19 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - -import pub.doric.navigator.IDoricNavigator; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; /** * @Description: pub.doric * @Author: pengfei.zhou * @CreateDate: 2019-11-23 */ -public class DoricFragment extends Fragment implements IDoricNavigator { +public class DoricFragment extends Fragment { public static DoricFragment newInstance(String scheme, String alias) { Bundle args = new Bundle(); @@ -42,6 +43,23 @@ public class DoricFragment extends Fragment implements IDoricNavigator { return fragment; } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + OnBackPressedCallback callback = new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + NavController navController = Navigation.findNavController(requireActivity(), R.id.nav_host); + if (!navController.popBackStack()) { + if (getActivity() != null) { + getActivity().finish(); + } + } + } + }; + requireActivity().getOnBackPressedDispatcher().addCallback(this, callback); + } + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -51,34 +69,6 @@ public class DoricFragment extends Fragment implements IDoricNavigator { @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - Bundle argument = getArguments(); - if (argument != null) { - String alias = argument.getString("alias"); - String scheme = argument.getString("scheme"); - push(scheme, alias); - } } - @Override - public void push(String scheme, String alias) { - getChildFragmentManager().beginTransaction() - .add(R.id.root, DoricPanelFragment.newInstance(scheme, alias)) - .addToBackStack(scheme) - .commit(); - } - - @Override - public void pop() { - if (canPop()) { - getChildFragmentManager().popBackStack(); - } else { - if (getActivity() != null) { - getActivity().finish(); - } - } - } - - public boolean canPop() { - return getChildFragmentManager().getBackStackEntryCount() > 1; - } } diff --git a/doric-android/doric/src/main/java/pub/doric/DoricPanel.java b/doric-android/doric/src/main/java/pub/doric/DoricPanel.java index a9e578c5..f8893534 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricPanel.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricPanel.java @@ -54,8 +54,8 @@ public class DoricPanel extends FrameLayout implements LifecycleObserver { } - public void config(String script, String alias) { - DoricContext doricContext = DoricContext.create(getContext(), script, alias); + public void config(String script, String alias, String extra) { + DoricContext doricContext = DoricContext.create(getContext(), script, alias, extra); config(doricContext); } diff --git a/doric-android/doric/src/main/java/pub/doric/DoricPanelFragment.java b/doric-android/doric/src/main/java/pub/doric/DoricPanelFragment.java index 12653e8d..473b0e1b 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricPanelFragment.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricPanelFragment.java @@ -23,6 +23,8 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; import pub.doric.async.AsyncResult; import pub.doric.loader.DoricJSLoaderManager; @@ -35,18 +37,8 @@ import pub.doric.utils.DoricLog; * @Author: pengfei.zhou * @CreateDate: 2019-11-23 */ -public class DoricPanelFragment extends Fragment { +public class DoricPanelFragment extends Fragment implements IDoricNavigator { private DoricPanel doricPanel; - private BaseDoricNavBar navBar; - - public static DoricPanelFragment newInstance(String scheme, String alias) { - Bundle args = new Bundle(); - args.putString("scheme", scheme); - args.putString("alias", alias); - DoricPanelFragment fragment = new DoricPanelFragment(); - fragment.setArguments(args); - return fragment; - } @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -58,23 +50,26 @@ public class DoricPanelFragment extends Fragment { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); doricPanel = view.findViewById(R.id.doric_panel); - navBar = view.findViewById(R.id.doric_nav_bar); Bundle argument = getArguments(); + if (argument == null) { + if (getActivity() != null && getActivity().getIntent() != null) { + argument = getActivity().getIntent().getExtras(); + } + } if (argument == null) { DoricLog.e("DoricPanelFragment argument is null"); return; } final String alias = argument.getString("alias"); String scheme = argument.getString("scheme"); + final String extra = argument.getString("extra"); DoricJSLoaderManager.getInstance().loadJSBundle(scheme).setCallback(new AsyncResult.Callback() { @Override public void onResult(String result) { - doricPanel.config(result, alias); + doricPanel.config(result, alias, extra); DoricContext context = doricPanel.getDoricContext(); - Fragment fragment = getParentFragment(); - if (fragment instanceof IDoricNavigator) { - context.setDoricNavigator((IDoricNavigator) fragment); - } + context.setDoricNavigator(DoricPanelFragment.this); + BaseDoricNavBar navBar = requireActivity().getWindow().getDecorView().findViewById(R.id.doric_nav_bar); context.setDoricNavBar(navBar); } @@ -89,4 +84,41 @@ public class DoricPanelFragment extends Fragment { } }); } + + @Override + public void push(String scheme, String alias, String extra) { + Bundle argument = new Bundle(); + argument.putString("scheme", scheme); + argument.putString("alias", alias); + argument.putString("extra", extra); + getNavController() + .navigate(R.id.action_doricPanelFragment_to_doricPanelFragment, argument); + } + + @Override + public void pop() { + getNavController().popBackStack(); + } + + private NavController getNavController() { + return Navigation.findNavController(getView()); + } + + @Override + public void onResume() { + super.onResume(); + doricPanel.onActivityResume(); + } + + @Override + public void onPause() { + super.onPause(); + doricPanel.onActivityPause(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + doricPanel.onActivityDestroy(); + } } diff --git a/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java b/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java index 75f087e5..b95669ed 100644 --- a/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java +++ b/doric-android/doric/src/main/java/pub/doric/DoricRegistry.java @@ -30,6 +30,7 @@ import pub.doric.plugin.StoragePlugin; import pub.doric.refresh.RefreshableNode; import pub.doric.shader.HLayoutNode; import pub.doric.shader.ImageNode; +import pub.doric.shader.InputNode; import pub.doric.shader.ScrollerNode; import pub.doric.shader.flowlayout.FlowLayoutItemNode; import pub.doric.shader.flowlayout.FlowLayoutNode; @@ -40,6 +41,7 @@ import pub.doric.shader.StackNode; import pub.doric.shader.TextNode; import pub.doric.shader.VLayoutNode; import pub.doric.shader.ViewNode; +import pub.doric.shader.slider.NestedSliderNode; import pub.doric.shader.slider.SlideItemNode; import pub.doric.shader.slider.SliderNode; import pub.doric.utils.DoricMetaInfo; @@ -106,6 +108,8 @@ public class DoricRegistry { this.registerViewNode(RefreshableNode.class); this.registerViewNode(FlowLayoutNode.class); this.registerViewNode(FlowLayoutItemNode.class); + this.registerViewNode(InputNode.class); + this.registerViewNode(NestedSliderNode.class); initRegistry(this); } diff --git a/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java b/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java index a4bf460f..336c5c2d 100644 --- a/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java +++ b/doric-android/doric/src/main/java/pub/doric/engine/DoricJSEngine.java @@ -15,6 +15,9 @@ */ package pub.doric.engine; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -22,11 +25,13 @@ import android.os.Message; import android.text.TextUtils; import com.github.pengfeizhou.jscore.JSDecoder; +import com.github.pengfeizhou.jscore.JSONBuilder; import com.github.pengfeizhou.jscore.JavaFunction; import com.github.pengfeizhou.jscore.JavaValue; import java.util.ArrayList; +import pub.doric.Doric; import pub.doric.DoricRegistry; import pub.doric.extension.bridge.DoricBridgeExtension; import pub.doric.extension.timer.DoricTimerExtension; @@ -73,6 +78,27 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time } private void injectGlobal() { + String appName = ""; + String appVersion = ""; + Context context = Doric.application(); + try { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo( + context.getPackageName(), 0); + int labelRes = packageInfo.applicationInfo.labelRes; + appName = context.getResources().getString(labelRes); + appVersion = packageInfo.versionName; + } catch (Exception e) { + e.printStackTrace(); + } + mDoricJSE.injectGlobalJSObject(DoricConstant.INJECT_ENVIRONMENT, new JavaValue(new JSONBuilder() + .put("platform", "Android") + .put("platformVersion", String.valueOf(android.os.Build.VERSION.SDK_INT)) + .put("appName", appName) + .put("appVersion", appVersion) + .put("screenWidth", DoricUtils.px2dp(DoricUtils.getScreenWidth())) + .put("screenHeight", DoricUtils.px2dp(DoricUtils.getScreenHeight())) + .toJSONObject())); mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_LOG, new JavaFunction() { @Override public JavaValue exec(JSDecoder[] args) { diff --git a/doric-android/doric/src/main/java/pub/doric/navigator/IDoricNavigator.java b/doric-android/doric/src/main/java/pub/doric/navigator/IDoricNavigator.java index e260cd5f..a89c6e99 100644 --- a/doric-android/doric/src/main/java/pub/doric/navigator/IDoricNavigator.java +++ b/doric-android/doric/src/main/java/pub/doric/navigator/IDoricNavigator.java @@ -21,7 +21,7 @@ package pub.doric.navigator; * @CreateDate: 2019-11-23 */ public interface IDoricNavigator { - void push(String scheme, String alias); + void push(String scheme, String alias, String extra); void pop(); } diff --git a/doric-android/doric/src/main/java/pub/doric/plugin/NavBarPlugin.java b/doric-android/doric/src/main/java/pub/doric/plugin/NavBarPlugin.java index f1b9a7b8..ff21d8d0 100644 --- a/doric-android/doric/src/main/java/pub/doric/plugin/NavBarPlugin.java +++ b/doric-android/doric/src/main/java/pub/doric/plugin/NavBarPlugin.java @@ -61,13 +61,6 @@ public class NavBarPlugin extends DoricJavaPlugin { JSObject jsObject = jsDecoder.decode().asObject(); boolean hidden = jsObject.getProperty("hidden").asBoolean().value(); navBar.setHidden(hidden); - View v = getDoricContext().getRootNode().getNodeView(); - ViewGroup.LayoutParams params = v.getLayoutParams(); - if (params instanceof ViewGroup.MarginLayoutParams) { - ((ViewGroup.MarginLayoutParams) params).topMargin = - hidden ? 0 - : ((View) navBar).getMeasuredHeight(); - } promise.resolve(); } catch (ArchiveException e) { e.printStackTrace(); diff --git a/doric-android/doric/src/main/java/pub/doric/plugin/NavigatorPlugin.java b/doric-android/doric/src/main/java/pub/doric/plugin/NavigatorPlugin.java index 372529ec..cb28c162 100644 --- a/doric-android/doric/src/main/java/pub/doric/plugin/NavigatorPlugin.java +++ b/doric-android/doric/src/main/java/pub/doric/plugin/NavigatorPlugin.java @@ -18,10 +18,14 @@ package pub.doric.plugin; import com.github.pengfeizhou.jscore.ArchiveException; import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; +import com.github.pengfeizhou.jscore.JavaValue; +import pub.doric.Doric; import pub.doric.DoricContext; import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; import pub.doric.navigator.IDoricNavigator; import pub.doric.utils.ThreadMode; @@ -37,25 +41,47 @@ public class NavigatorPlugin extends DoricJavaPlugin { } @DoricMethod(thread = ThreadMode.UI) - public void push(JSDecoder jsDecoder) { + public void push(JSDecoder jsDecoder, DoricPromise promise) { IDoricNavigator navigator = getDoricContext().getDoricNavigator(); if (navigator != null) { try { JSObject jsObject = jsDecoder.decode().asObject(); + String scheme = jsObject.getProperty("scheme").asString().value(); + String alias = scheme; + String extra = ""; + JSValue config = jsObject.getProperty("config"); + if (config.isObject()) { + JSValue aliasJS = config.asObject().getProperty("alias"); + if (aliasJS.isString()) { + alias = aliasJS.asString().value(); + } + JSValue extraJS = config.asObject().getProperty("extra"); + if (extraJS.isString()) { + extra = extraJS.asString().value(); + } + } navigator.push(jsObject.getProperty("scheme").asString().value(), - jsObject.getProperty("alias").asString().value() + alias, + extra ); + promise.resolve(); } catch (ArchiveException e) { e.printStackTrace(); + promise.reject(new JavaValue(e.getLocalizedMessage())); } + } else { + promise.reject(new JavaValue("Navigator not implemented")); } } @DoricMethod(thread = ThreadMode.UI) - public void pop() { + public void pop(DoricPromise promise) { IDoricNavigator navigator = getDoricContext().getDoricNavigator(); if (navigator != null) { navigator.pop(); + promise.resolve(); + } else { + promise.reject(new JavaValue("Navigator not implemented")); } } } diff --git a/doric-android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java b/doric-android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java index ec36fb34..072d5cd0 100644 --- a/doric-android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java +++ b/doric-android/doric/src/main/java/pub/doric/plugin/NetworkPlugin.java @@ -17,7 +17,6 @@ package pub.doric.plugin; import android.text.TextUtils; -import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSONBuilder; import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; @@ -51,7 +50,8 @@ import pub.doric.extension.bridge.DoricPromise; */ @DoricPlugin(name = "network") public class NetworkPlugin extends DoricJavaPlugin { - private OkHttpClient okHttpClient = new OkHttpClient(); + private OkHttpClient okHttpClient = new OkHttpClient.Builder() + .build(); public NetworkPlugin(DoricContext doricContext) { super(doricContext); @@ -79,9 +79,10 @@ public class NetworkPlugin extends DoricJavaPlugin { MediaType mediaType = MediaType.parse(TextUtils.isEmpty(contentType) ? "application/json; charset=utf-8" : contentType); RequestBody requestBody = HttpMethod.permitsRequestBody(method) ? RequestBody.create(mediaType, dataVal.isString() ? dataVal.asString().value() : "") : null; Request.Builder requestBuilder = new Request.Builder(); - requestBuilder.url(url) - .headers(headers) - .method(method, requestBody); + requestBuilder = requestBuilder.url(url).headers(headers); + if (HttpMethod.permitsRequestBody(method.toUpperCase())) { + requestBuilder = requestBuilder.method(method, requestBody); + } if (timeoutVal.isNumber() && okHttpClient.connectTimeoutMillis() != timeoutVal.asNumber().toLong()) { okHttpClient = okHttpClient.newBuilder().connectTimeout(timeoutVal.asNumber().toLong(), TimeUnit.MILLISECONDS).build(); } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/GroupNode.java b/doric-android/doric/src/main/java/pub/doric/shader/GroupNode.java index bf6a958a..c940865c 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/GroupNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/GroupNode.java @@ -31,8 +31,8 @@ import java.util.ArrayList; * @CreateDate: 2019-07-20 */ public abstract class GroupNode extends SuperNode { - private ArrayList mChildNodes = new ArrayList<>(); - private ArrayList mChildViewIds = new ArrayList<>(); + protected ArrayList mChildNodes = new ArrayList<>(); + protected ArrayList mChildViewIds = new ArrayList<>(); public GroupNode(DoricContext doricContext) { super(doricContext); @@ -57,7 +57,7 @@ public abstract class GroupNode extends SuperNode { configChildNode(); } - private void configChildNode() { + protected void configChildNode() { for (int idx = 0; idx < mChildViewIds.size(); idx++) { String id = mChildViewIds.get(idx); JSObject model = getSubModel(id); diff --git a/doric-android/doric/src/main/java/pub/doric/shader/ImageNode.java b/doric-android/doric/src/main/java/pub/doric/shader/ImageNode.java index 9157c80d..240ae2f4 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/ImageNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/ImageNode.java @@ -17,11 +17,7 @@ package pub.doric.shader; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; - -import androidx.annotation.Nullable; - import android.text.TextUtils; import android.util.Base64; import android.widget.ImageView; @@ -30,18 +26,21 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.load.DataSource; import com.bumptech.glide.load.engine.GlideException; import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.Target; - -import pub.doric.DoricContext; -import pub.doric.extension.bridge.DoricPlugin; -import pub.doric.utils.DoricUtils; - import com.github.pengfeizhou.jscore.JSONBuilder; +import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; import java.util.regex.Matcher; import java.util.regex.Pattern; +import androidx.annotation.Nullable; +import jp.wasabeef.glide.transformations.BlurTransformation; +import pub.doric.DoricContext; +import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.utils.DoricUtils; + /** * @Description: com.github.penfeizhou.doric.widget * @Author: pengfei.zhou @@ -50,6 +49,7 @@ import java.util.regex.Pattern; @DoricPlugin(name = "Image") public class ImageNode extends ViewNode { private String loadCallbackId = ""; + private boolean isBlur; public ImageNode(DoricContext doricContext) { super(doricContext); @@ -60,11 +60,29 @@ public class ImageNode extends ViewNode { return new ImageView(getContext()); } + @Override + public void blend(JSObject jsObject) { + if(jsObject != null) { + JSValue jsValue = jsObject.getProperty("isBlur"); + if(jsValue.isBoolean()) { + isBlur = jsValue.asBoolean().value(); + } + } + super.blend(jsObject); + } + @Override protected void blend(ImageView view, String name, JSValue prop) { switch (name) { case "imageUrl": + RequestOptions options; + if(isBlur) { + options = RequestOptions.bitmapTransform(new BlurTransformation(25, 3)); + } else { + options = new RequestOptions(); + } Glide.with(getContext()).load(prop.asString().value()) + .apply(options) .listener(new RequestListener() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { diff --git a/doric-android/doric/src/main/java/pub/doric/shader/InputNode.java b/doric-android/doric/src/main/java/pub/doric/shader/InputNode.java new file mode 100644 index 00000000..6c924b44 --- /dev/null +++ b/doric-android/doric/src/main/java/pub/doric/shader/InputNode.java @@ -0,0 +1,153 @@ +/* + * Copyright [2019] [Doric.Pub] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pub.doric.shader; + +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.EditText; + +import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; + +import pub.doric.DoricContext; +import pub.doric.extension.bridge.DoricMethod; +import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; + +/** + * @Description: pub.doric.shader + * @Author: pengfei.zhou + * @CreateDate: 2019-12-06 + */ +@DoricPlugin(name = "Input") +public class InputNode extends ViewNode implements TextWatcher, View.OnFocusChangeListener { + private String onTextChangeId; + private String onFocusChangeId; + + public InputNode(DoricContext doricContext) { + super(doricContext); + } + + @Override + protected EditText build() { + EditText editText = new EditText(getContext()); + editText.addTextChangedListener(this); + editText.setOnFocusChangeListener(this); + return editText; + } + + @Override + protected void blend(EditText view, String name, JSValue prop) { + switch (name) { + case "text": + view.setText(prop.asString().toString()); + break; + case "textSize": + view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, prop.asNumber().toFloat()); + break; + case "textColor": + view.setTextColor(prop.asNumber().toInt()); + break; + case "textAlignment": + view.setGravity(prop.asNumber().toInt() | Gravity.CENTER_VERTICAL); + break; + case "hintText": + view.setHint(prop.asString().toString()); + break; + case "hintTextColor": + view.setHintTextColor(prop.asNumber().toInt()); + break; + case "multiline": + if (prop.asBoolean().value()) { + view.setInputType(view.getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE); + } else { + view.setInputType(view.getInputType() & ~InputType.TYPE_TEXT_FLAG_MULTI_LINE); + } + break; + case "onTextChange": + if (prop.isString()) { + onTextChangeId = prop.asString().value(); + } else { + onTextChangeId = null; + } + break; + case "onFocusChange": + if (prop.isString()) { + onFocusChangeId = prop.asString().value(); + } else { + onFocusChangeId = null; + } + break; + default: + super.blend(view, name, prop); + break; + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (!TextUtils.isEmpty(onTextChangeId)) { + callJSResponse(onTextChangeId, s.toString()); + } + } + + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!TextUtils.isEmpty(onFocusChangeId)) { + callJSResponse(onFocusChangeId, hasFocus); + } + } + + @DoricMethod + public String getText() { + return mView.getText().toString(); + } + + @DoricMethod + public void setSelection(JSObject jsObject, DoricPromise doricPromise) { + int start = jsObject.getProperty("start").asNumber().toInt(); + int end = jsObject.getProperty("end").asNumber().toInt(); + mView.setSelection(start, end); + doricPromise.resolve(); + } + + @DoricMethod + public void requestFocus(DoricPromise promise) { + mView.requestFocus(); + promise.resolve(); + } + + @DoricMethod + public void releaseFocus(DoricPromise promise) { + mView.clearFocus(); + promise.resolve(); + } +} diff --git a/doric-android/doric/src/main/java/pub/doric/shader/SuperNode.java b/doric-android/doric/src/main/java/pub/doric/shader/SuperNode.java index bf8ed08c..b03d55aa 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/SuperNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/SuperNode.java @@ -85,6 +85,10 @@ public abstract class SuperNode extends ViewNode { subNodes.clear(); } + public void removeSubModel(String id) { + subNodes.remove(id); + } + protected abstract void blendSubNode(JSObject subProperties); protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) { diff --git a/doric-android/doric/src/main/java/pub/doric/shader/TextNode.java b/doric-android/doric/src/main/java/pub/doric/shader/TextNode.java index 78f95906..a8de13c2 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/TextNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/TextNode.java @@ -39,6 +39,7 @@ public class TextNode extends ViewNode { protected TextView build() { TextView tv = new TextView(getContext()); tv.setGravity(Gravity.CENTER); + tv.setMaxLines(1); return tv; } @@ -57,7 +58,8 @@ public class TextNode extends ViewNode { case "textAlignment": view.setGravity(prop.asNumber().toInt() | Gravity.CENTER_VERTICAL); break; - case "numberOfLines": + case "maxLines": + view.setMaxLines(prop.asNumber().toInt()); break; default: super.blend(view, name, prop); diff --git a/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java b/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java index 664253fc..955a5378 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java @@ -37,6 +37,7 @@ import androidx.annotation.NonNull; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.interpolator.view.animation.LinearOutSlowInInterpolator; +import pub.doric.Doric; import pub.doric.DoricContext; import pub.doric.DoricRegistry; import pub.doric.async.AsyncResult; @@ -55,6 +56,8 @@ import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; import com.github.pengfeizhou.jscore.JavaValue; +import org.json.JSONObject; + import java.util.LinkedList; /** @@ -214,6 +217,7 @@ public abstract class ViewNode extends DoricContextHolder { if (prop.isObject()) { requireDoricLayer().setBorder(DoricUtils.dp2px(prop.asObject().getProperty("width").asNumber().toFloat()), prop.asObject().getProperty("color").asNumber().toInt()); + requireDoricLayer().invalidate(); } break; case "alpha": @@ -339,6 +343,16 @@ public abstract class ViewNode extends DoricContextHolder { setRotation(prop.asNumber().toFloat()); } break; + case "padding": + if (prop.isObject()) { + setPadding(prop.asObject()); + } + break; + case "hidden": + if (prop.isBoolean()) { + getNodeView().setVisibility(prop.asBoolean().value() ? View.GONE : View.VISIBLE); + } + break; default: break; } @@ -409,6 +423,19 @@ public abstract class ViewNode extends DoricContextHolder { } } + protected void setPadding(JSObject paddings) { + JSValue left = paddings.getProperty("left"); + JSValue right = paddings.getProperty("right"); + JSValue top = paddings.getProperty("top"); + JSValue bottom = paddings.getProperty("bottom"); + mView.setPadding( + left.isNumber() ? DoricUtils.dp2px(left.asNumber().toFloat()) : 0, + top.isNumber() ? DoricUtils.dp2px(top.asNumber().toFloat()) : 0, + right.isNumber() ? DoricUtils.dp2px(right.asNumber().toFloat()) : 0, + bottom.isNumber() ? DoricUtils.dp2px(bottom.asNumber().toFloat()) : 0 + ); + } + private void blendLayoutConfig(JSObject jsObject) { JSValue margin = jsObject.getProperty("margin"); JSValue widthSpec = jsObject.getProperty("widthSpec"); @@ -478,7 +505,7 @@ public abstract class ViewNode extends DoricContextHolder { if (mLayoutParams.width >= 0) { return DoricUtils.px2dp(mLayoutParams.width); } else { - return mView.getMeasuredWidth(); + return DoricUtils.px2dp(mView.getMeasuredWidth()); } } @@ -487,7 +514,7 @@ public abstract class ViewNode extends DoricContextHolder { if (mLayoutParams.width >= 0) { return DoricUtils.px2dp(mLayoutParams.height); } else { - return mView.getMeasuredHeight(); + return DoricUtils.px2dp(mView.getMeasuredHeight()); } } @@ -826,4 +853,14 @@ public abstract class ViewNode extends DoricContextHolder { return 0; } } + + @DoricMethod + public JSONObject getLocationOnScreen() { + int[] position = new int[2]; + getNodeView().getLocationOnScreen(position); + return new JSONBuilder() + .put("x", DoricUtils.px2dp(position[0])) + .put("y", DoricUtils.px2dp(position[1])) + .toJSONObject(); + } } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java index 00c2f9fa..ea9001e0 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java @@ -22,6 +22,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.StaggeredGridLayoutManager; import com.github.pengfeizhou.jscore.JSArray; import com.github.pengfeizhou.jscore.JSDecoder; @@ -65,15 +66,28 @@ class FlowAdapter extends RecyclerView.Adapter { holder.flowLayoutItemNode.setId(jsObject.getProperty("id").asString().value()); holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject()); } + if (position >= this.itemCount) { + this.flowLayoutNode.callJSResponse(this.flowLayoutNode.onLoadMoreFuncId); + + StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + holder.itemView.getLayoutParams().height + ); + layoutParams.setFullSpan(true); + holder.itemView.setLayoutParams(layoutParams); + } } @Override public int getItemCount() { - return itemCount; + return this.itemCount + (this.flowLayoutNode.loadMore ? 1 : 0); } @Override public int getItemViewType(int position) { + if (position >= itemCount) { + return Integer.MAX_VALUE; + } JSValue value = getItemModel(position); if (value.isObject()) { if (value.asObject().getProperty("identifier").isString()) { @@ -84,6 +98,9 @@ class FlowAdapter extends RecyclerView.Adapter { } private JSValue getItemModel(final int position) { + if (position >= this.itemCount) { + return this.flowLayoutNode.getSubModel(this.flowLayoutNode.loadMoreViewId); + } String id = itemValues.get(position); if (TextUtils.isEmpty(id)) { AsyncResult asyncResult = flowLayoutNode.callJSResponse( diff --git a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java index f2b46b09..c01c543c 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowLayoutNode.java @@ -41,15 +41,38 @@ public class FlowLayoutNode extends SuperNode { private final FlowAdapter flowAdapter; private final StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager( 2, - StaggeredGridLayoutManager.VERTICAL); + StaggeredGridLayoutManager.VERTICAL) { + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + return super.scrollVerticallyBy(dy, recycler, state); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + @Override + public void onScrollStateChanged(int state) { + try { + super.onScrollStateChanged(state); + } catch (Exception e) { + e.printStackTrace(); + } + } + }; private int columnSpace = 0; private int rowSpace = 0; + private Rect padding = new Rect(); private final RecyclerView.ItemDecoration spacingItemDecoration = new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { outRect.set(columnSpace / 2, rowSpace / 2, columnSpace / 2, rowSpace / 2); } }; + String onLoadMoreFuncId; + boolean loadMore = false; + String loadMoreViewId; public FlowLayoutNode(DoricContext doricContext) { super(doricContext); @@ -80,11 +103,9 @@ public class FlowLayoutNode extends SuperNode { switch (name) { case "columnSpace": columnSpace = DoricUtils.dp2px(prop.asNumber().toFloat()); - mView.setPadding(-columnSpace / 2, mView.getPaddingTop(), -columnSpace / 2, mView.getPaddingBottom()); break; case "rowSpace": rowSpace = DoricUtils.dp2px(prop.asNumber().toFloat()); - mView.setPadding(mView.getPaddingLeft(), -rowSpace / 2, mView.getPaddingRight(), -rowSpace / 2); break; case "columnCount": staggeredGridLayoutManager.setSpanCount(prop.asNumber().toInt()); @@ -93,23 +114,54 @@ public class FlowLayoutNode extends SuperNode { this.flowAdapter.itemCount = prop.asNumber().toInt(); break; case "renderItem": - this.flowAdapter.renderItemFuncId = prop.asString().value(); - // If reset renderItem,should reset native cache. - this.flowAdapter.itemValues.clear(); - clearSubModel(); + String funcId = prop.asString().value(); + if (!funcId.equals(this.flowAdapter.renderItemFuncId)) { + this.flowAdapter.renderItemFuncId = funcId; + // If reset renderItem,should reset native cache. + for (int index = 0; index < this.flowAdapter.itemValues.size(); index++) { + removeSubModel(this.flowAdapter.itemValues.valueAt(index)); + } + this.flowAdapter.itemValues.clear(); + } break; case "batchCount": this.flowAdapter.batchCount = prop.asNumber().toInt(); break; + case "onLoadMore": + this.onLoadMoreFuncId = prop.asString().value(); + break; + case "loadMoreView": + this.loadMoreViewId = prop.asString().value(); + break; + case "loadMore": + this.loadMore = prop.asBoolean().value(); + break; default: super.blend(view, name, prop); break; } } + @Override + protected void setPadding(JSObject jsObject) { + JSValue left = jsObject.getProperty("left"); + JSValue right = jsObject.getProperty("right"); + JSValue top = jsObject.getProperty("top"); + JSValue bottom = jsObject.getProperty("bottom"); + padding.left = left.isNumber() ? DoricUtils.dp2px(left.asNumber().toFloat()) : 0; + padding.top = top.isNumber() ? DoricUtils.dp2px(top.asNumber().toFloat()) : 0; + padding.right = right.isNumber() ? DoricUtils.dp2px(right.asNumber().toFloat()) : 0; + padding.bottom = bottom.isNumber() ? DoricUtils.dp2px(bottom.asNumber().toFloat()) : 0; + } + @Override public void blend(JSObject jsObject) { super.blend(jsObject); + mView.setPadding( + padding.left - columnSpace / 2, + padding.top - rowSpace / 2, + padding.right - columnSpace / 2, + padding.bottom - rowSpace / 2); if (mView != null) { mView.post(new Runnable() { @Override diff --git a/doric-android/doric/src/main/java/pub/doric/shader/list/ListAdapter.java b/doric-android/doric/src/main/java/pub/doric/shader/list/ListAdapter.java index 5b96a3c0..955d42d0 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/list/ListAdapter.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/list/ListAdapter.java @@ -16,7 +16,6 @@ package pub.doric.shader.list; import android.text.TextUtils; -import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; @@ -40,10 +39,6 @@ import pub.doric.shader.ViewNode; class ListAdapter extends RecyclerView.Adapter { private final ListNode listNode; - String renderItemFuncId; - int itemCount = 0; - int batchCount = 15; - SparseArray itemValues = new SparseArray<>(); ListAdapter(ListNode listNode) { this.listNode = listNode; @@ -60,22 +55,28 @@ class ListAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) { JSValue jsValue = getItemModel(position); - if (jsValue.isObject()) { + if (jsValue != null && jsValue.isObject()) { JSObject jsObject = jsValue.asObject(); holder.listItemNode.setId(jsObject.getProperty("id").asString().value()); holder.listItemNode.blend(jsObject.getProperty("props").asObject()); } + if (position >= this.listNode.itemCount) { + this.listNode.callJSResponse(this.listNode.onLoadMoreFuncId); + } } @Override public int getItemCount() { - return itemCount; + return this.listNode.itemCount + (this.listNode.loadMore ? 1 : 0); } @Override public int getItemViewType(int position) { + if (position >= this.listNode.itemCount) { + return Integer.MAX_VALUE; + } JSValue value = getItemModel(position); - if (value.isObject()) { + if (value != null && value.isObject()) { if (value.asObject().getProperty("identifier").isString()) { return value.asObject().getProperty("identifier").asString().value().hashCode(); } @@ -84,12 +85,15 @@ class ListAdapter extends RecyclerView.Adapter { } private JSValue getItemModel(final int position) { - String id = itemValues.get(position); + if (position >= this.listNode.itemCount) { + return this.listNode.getSubModel(this.listNode.loadMoreViewId); + } + String id = listNode.itemValues.get(position); if (TextUtils.isEmpty(id)) { AsyncResult asyncResult = listNode.callJSResponse( "renderBunchedItems", position, - batchCount); + listNode.batchCount); try { JSDecoder jsDecoder = asyncResult.synchronous().get(); JSValue result = jsDecoder.decode(); @@ -98,10 +102,10 @@ class ListAdapter extends RecyclerView.Adapter { for (int i = 0; i < jsArray.size(); i++) { JSObject itemModel = jsArray.get(i).asObject(); String itemId = itemModel.getProperty("id").asString().value(); - itemValues.put(i + position, itemId); + listNode.itemValues.put(i + position, itemId); listNode.setSubModel(itemId, itemModel); } - return listNode.getSubModel(itemValues.get(position)); + return listNode.getSubModel(listNode.itemValues.get(position)); } } catch (Exception e) { e.printStackTrace(); @@ -119,8 +123,8 @@ class ListAdapter extends RecyclerView.Adapter { void blendSubNode(JSObject subProperties) { - for (int i = 0; i < itemValues.size(); i++) { - if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) { + for (int i = 0; i < listNode.itemValues.size(); i++) { + if (subProperties.getProperty("id").asString().value().equals(listNode.itemValues.valueAt(i))) { notifyItemChanged(i); } } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java b/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java index 3349c844..4c2316c2 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/list/ListNode.java @@ -15,6 +15,7 @@ */ package pub.doric.shader.list; +import android.util.SparseArray; import android.view.View; import androidx.recyclerview.widget.LinearLayoutManager; @@ -36,6 +37,13 @@ import pub.doric.shader.ViewNode; @DoricPlugin(name = "List") public class ListNode extends SuperNode { private final ListAdapter listAdapter; + private String renderItemFuncId; + String onLoadMoreFuncId; + int itemCount = 0; + int batchCount = 15; + SparseArray itemValues = new SparseArray<>(); + boolean loadMore = false; + String loadMoreViewId; public ListNode(DoricContext doricContext) { super(doricContext); @@ -82,16 +90,30 @@ public class ListNode extends SuperNode { protected void blend(RecyclerView view, String name, JSValue prop) { switch (name) { case "itemCount": - this.listAdapter.itemCount = prop.asNumber().toInt(); + this.itemCount = prop.asNumber().toInt(); break; case "renderItem": - this.listAdapter.renderItemFuncId = prop.asString().value(); - // If reset renderItem,should reset native cache. - this.listAdapter.itemValues.clear(); - clearSubModel(); + String funcId = prop.asString().value(); + if (!funcId.equals(this.renderItemFuncId)) { + this.renderItemFuncId = funcId; + // If reset renderItem,should reset native cache. + for (int index = 0; index < this.itemValues.size(); index++) { + removeSubModel(this.itemValues.valueAt(index)); + } + this.itemValues.clear(); + } + break; + case "onLoadMore": + this.onLoadMoreFuncId = prop.asString().value(); + break; + case "loadMoreView": + this.loadMoreViewId = prop.asString().value(); break; case "batchCount": - this.listAdapter.batchCount = prop.asNumber().toInt(); + this.batchCount = prop.asNumber().toInt(); + break; + case "loadMore": + this.loadMore = prop.asBoolean().value(); break; default: super.blend(view, name, prop); diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/NestedSliderNode.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/NestedSliderNode.java new file mode 100644 index 00000000..204164ef --- /dev/null +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/NestedSliderNode.java @@ -0,0 +1,207 @@ +/* + * Copyright [2019] [Doric.Pub] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package pub.doric.shader.slider; + +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.github.pengfeizhou.jscore.JSObject; +import com.github.pengfeizhou.jscore.JSValue; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; + +import pub.doric.DoricContext; +import pub.doric.extension.bridge.DoricMethod; +import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; +import pub.doric.shader.GroupNode; +import pub.doric.shader.ViewNode; + +/** + * @Description: pub.doric.shader.slider + * @Author: pengfei.zhou + * @CreateDate: 2019-12-07 + */ +@DoricPlugin(name = "NestedSlider") +public class NestedSliderNode extends GroupNode implements ViewPager.OnPageChangeListener { + private ArrayList slideItems = new ArrayList<>(); + private String onPageSlidedFuncId; + + public NestedSliderNode(DoricContext doricContext) { + super(doricContext); + } + + @Override + protected ViewPager build() { + ViewPager viewPager = new ViewPager(getContext()); + viewPager.setAdapter(new PagerAdapter() { + @Override + public int getCount() { + return slideItems.size(); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NotNull Object object) { + return view == object; + } + + @Override + public void destroyItem(@NotNull ViewGroup container, int position, @NotNull Object object) { + container.removeView(slideItems.get(position)); + } + + @NotNull + @Override + public Object instantiateItem(@NotNull ViewGroup container, int position) { + container.addView(slideItems.get(position), 0); + return slideItems.get(position); + } + }); + viewPager.addOnPageChangeListener(this); + return viewPager; + } + + @Override + protected void blend(ViewPager view, String name, JSValue prop) { + switch (name) { + case "onPageSlided": + this.onPageSlidedFuncId = prop.asString().toString(); + break; + default: + super.blend(view, name, prop); + break; + } + } + + @Override + protected void configChildNode() { + for (int idx = 0; idx < mChildViewIds.size(); idx++) { + String id = mChildViewIds.get(idx); + JSObject model = getSubModel(id); + String type = model.getProperty("type").asString().value(); + if (idx < mChildNodes.size()) { + ViewNode oldNode = mChildNodes.get(idx); + if (id.equals(oldNode.getId())) { + //The same,skip + } else { + if (mReusable) { + if (oldNode.getType().equals(type)) { + //Same type,can be reused + oldNode.setId(id); + oldNode.blend(model.getProperty("props").asObject()); + } else { + //Replace this view + mChildNodes.remove(idx); + slideItems.remove(oldNode.getNodeView()); + ViewNode newNode = ViewNode.create(getDoricContext(), type); + newNode.setId(id); + newNode.init(this); + newNode.blend(model.getProperty("props").asObject()); + mChildNodes.add(idx, newNode); + slideItems.add(idx, newNode.getNodeView()); + } + } else { + //Find in remain nodes + int position = -1; + for (int start = idx + 1; start < mChildNodes.size(); start++) { + ViewNode node = mChildNodes.get(start); + if (id.equals(node.getId())) { + //Found + position = start; + break; + } + } + if (position >= 0) { + //Found swap idx,position + ViewNode reused = mChildNodes.remove(position); + ViewNode abandoned = mChildNodes.remove(idx); + mChildNodes.set(idx, reused); + mChildNodes.set(position, abandoned); + //View swap index + slideItems.remove(reused.getNodeView()); + slideItems.add(idx, reused.getNodeView()); + slideItems.remove(abandoned.getNodeView()); + slideItems.add(position, abandoned.getNodeView()); + } else { + //Not found,insert + ViewNode newNode = ViewNode.create(getDoricContext(), type); + newNode.setId(id); + newNode.init(this); + newNode.blend(model.getProperty("props").asObject()); + + mChildNodes.add(idx, newNode); + slideItems.add(idx, newNode.getNodeView()); + } + } + } + } else { + //Insert + ViewNode newNode = ViewNode.create(getDoricContext(), type); + newNode.setId(id); + newNode.init(this); + newNode.blend(model.getProperty("props").asObject()); + mChildNodes.add(newNode); + slideItems.add(idx, newNode.getNodeView()); + } + } + int size = mChildNodes.size(); + for (int idx = mChildViewIds.size(); idx < size; idx++) { + ViewNode viewNode = mChildNodes.remove(mChildViewIds.size()); + slideItems.remove(viewNode.getNodeView()); + } + mView.getAdapter().notifyDataSetChanged(); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + if (!TextUtils.isEmpty(onPageSlidedFuncId)) { + callJSResponse(onPageSlidedFuncId, position); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + + @DoricMethod + public void slidePage(JSObject params, DoricPromise promise) { + int page = params.getProperty("page").asNumber().toInt(); + boolean smooth = params.getProperty("smooth").asBoolean().value(); + mView.setCurrentItem(page, smooth); + if (!TextUtils.isEmpty(onPageSlidedFuncId)) { + callJSResponse(onPageSlidedFuncId, page); + } + promise.resolve(); + } + + @DoricMethod + public int getSlidedPage() { + return mView.getCurrentItem(); + } +} diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java index f68d38ba..31755d26 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideAdapter.java @@ -43,7 +43,7 @@ class SlideAdapter extends RecyclerView.Adapter { int itemCount = 0; int batchCount = 3; SparseArray itemValues = new SparseArray<>(); - + String renderPageFuncId; SlideAdapter(SliderNode sliderNode) { this.sliderNode = sliderNode; } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideItemNode.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideItemNode.java index dbd25c9e..0d61bb52 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideItemNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/SlideItemNode.java @@ -15,6 +15,7 @@ */ package pub.doric.shader.slider; +import android.view.ViewGroup; import android.widget.FrameLayout; import com.github.pengfeizhou.jscore.JSObject; @@ -50,7 +51,7 @@ public class SlideItemNode extends StackNode { @Override public void blend(JSObject jsObject) { super.blend(jsObject); - getNodeView().getLayoutParams().width = getLayoutParams().width; - getNodeView().getLayoutParams().height = getLayoutParams().height; + getNodeView().getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT; + getNodeView().getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; } } diff --git a/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java b/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java index 8cfceaa5..c08756fa 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/slider/SliderNode.java @@ -15,8 +15,10 @@ */ package pub.doric.shader.slider; +import android.text.TextUtils; import android.view.View; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.PagerSnapHelper; import androidx.recyclerview.widget.RecyclerView; @@ -25,7 +27,9 @@ import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSValue; import pub.doric.DoricContext; +import pub.doric.extension.bridge.DoricMethod; import pub.doric.extension.bridge.DoricPlugin; +import pub.doric.extension.bridge.DoricPromise; import pub.doric.shader.SuperNode; import pub.doric.shader.ViewNode; @@ -37,6 +41,8 @@ import pub.doric.shader.ViewNode; @DoricPlugin(name = "Slider") public class SliderNode extends SuperNode { private final SlideAdapter slideAdapter; + private String onPageSlidedFuncId; + private int lastPosition = 0; public SliderNode(DoricContext doricContext) { super(doricContext); @@ -47,12 +53,32 @@ public class SliderNode extends SuperNode { protected RecyclerView build() { RecyclerView recyclerView = new RecyclerView(getContext()); - LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); + final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); recyclerView.setLayoutManager(layoutManager); - PagerSnapHelper mPagerSnapHelper = new PagerSnapHelper(); - mPagerSnapHelper.attachToRecyclerView(recyclerView); + final PagerSnapHelper snapHelper = new PagerSnapHelper(); + snapHelper.attachToRecyclerView(recyclerView); recyclerView.setAdapter(this.slideAdapter); + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + View view = snapHelper.findSnapView(layoutManager); + if (view != null && !TextUtils.isEmpty(onPageSlidedFuncId)) { + int position = layoutManager.getPosition(view); + if (position != lastPosition) { + callJSResponse(onPageSlidedFuncId, position); + } + lastPosition = position; + } + } + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + } + }); return recyclerView; } @@ -109,17 +135,46 @@ public class SliderNode extends SuperNode { case "itemCount": this.slideAdapter.itemCount = prop.asNumber().toInt(); break; - case "renderItem": + case "renderPage": // If reset renderItem,should reset native cache. - this.slideAdapter.itemValues.clear(); - clearSubModel(); + String funcId = prop.asString().value(); + if (!funcId.equals(this.slideAdapter.renderPageFuncId)) { + this.slideAdapter.itemValues.clear(); + clearSubModel(); + this.slideAdapter.renderPageFuncId = funcId; + } break; case "batchCount": this.slideAdapter.batchCount = prop.asNumber().toInt(); break; + case "onPageSlided": + this.onPageSlidedFuncId = prop.asString().toString(); + break; default: super.blend(view, name, prop); break; } } + + @DoricMethod + public void slidePage(JSObject params, DoricPromise promise) { + int page = params.getProperty("page").asNumber().toInt(); + boolean smooth = params.getProperty("smooth").asBoolean().value(); + if (smooth) { + mView.smoothScrollToPosition(page); + } else { + mView.scrollToPosition(page); + } + if (!TextUtils.isEmpty(onPageSlidedFuncId)) { + callJSResponse(onPageSlidedFuncId, page); + lastPosition = page; + } + promise.resolve(); + } + + @DoricMethod + public int getSlidedPage() { + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mView.getLayoutManager(); + return linearLayoutManager != null ? linearLayoutManager.findFirstVisibleItemPosition() : 0; + } } diff --git a/doric-android/doric/src/main/java/pub/doric/utils/DoricConstant.java b/doric-android/doric/src/main/java/pub/doric/utils/DoricConstant.java index 08a814b1..5fabf45a 100644 --- a/doric-android/doric/src/main/java/pub/doric/utils/DoricConstant.java +++ b/doric-android/doric/src/main/java/pub/doric/utils/DoricConstant.java @@ -26,6 +26,8 @@ public class DoricConstant { public static final String DORIC_MODULE_LIB = "doric"; + public static final String INJECT_ENVIRONMENT = "Environment"; + public static final String INJECT_LOG = "nativeLog"; public static final String INJECT_REQUIRE = "nativeRequire"; public static final String INJECT_TIMER_SET = "nativeSetTimer"; diff --git a/doric-android/doric/src/main/java/pub/doric/widget/HVScrollView.java b/doric-android/doric/src/main/java/pub/doric/widget/HVScrollView.java index 7b86cd49..82456d55 100644 --- a/doric-android/doric/src/main/java/pub/doric/widget/HVScrollView.java +++ b/doric-android/doric/src/main/java/pub/doric/widget/HVScrollView.java @@ -669,6 +669,11 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent, super.requestDisallowInterceptTouchEvent(disallowIntercept); } + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return super.dispatchTouchEvent(ev); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { /* @@ -715,8 +720,12 @@ public class HVScrollView extends FrameLayout implements NestedScrollingParent, final int y = (int) ev.getY(pointerIndex); final int xDiff = Math.abs(x - mLastMotionX); final int yDiff = Math.abs(y - mLastMotionY); - if ((xDiff > mTouchSlop && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_HORIZONTAL) == 0) - || (yDiff > mTouchSlop && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0)) { + if ((xDiff > mTouchSlop + && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_HORIZONTAL) == 0 + && canScrollHorizontally()) + || (yDiff > mTouchSlop + && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0 + && canScrollVertically())) { mIsBeingDragged = true; mLastMotionX = x; mLastMotionY = y; diff --git a/doric-android/doric/src/main/res/layout/doric_fragment.xml b/doric-android/doric/src/main/res/layout/doric_fragment.xml index 7af124c8..539a884b 100644 --- a/doric-android/doric/src/main/res/layout/doric_fragment.xml +++ b/doric-android/doric/src/main/res/layout/doric_fragment.xml @@ -1,7 +1,21 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/doric-android/doric/src/main/res/layout/doric_framgent_panel.xml b/doric-android/doric/src/main/res/layout/doric_framgent_panel.xml index 237f04c3..dba2e8e6 100644 --- a/doric-android/doric/src/main/res/layout/doric_framgent_panel.xml +++ b/doric-android/doric/src/main/res/layout/doric_framgent_panel.xml @@ -1,18 +1,7 @@ - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/doric-android/doric/src/main/res/navigation/doric_navigation.xml b/doric-android/doric/src/main/res/navigation/doric_navigation.xml new file mode 100644 index 00000000..61bff7b0 --- /dev/null +++ b/doric-android/doric/src/main/res/navigation/doric_navigation.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/doric-android/scripts/maven.properties b/doric-android/scripts/maven.properties new file mode 100644 index 00000000..135efee8 --- /dev/null +++ b/doric-android/scripts/maven.properties @@ -0,0 +1,2 @@ +git=https://github.com/doric-pub/doric +site=https://github.com/doric-pub/doric diff --git a/doric-android/scripts/upload.gradle b/doric-android/scripts/upload.gradle new file mode 100644 index 00000000..e45c5b6d --- /dev/null +++ b/doric-android/scripts/upload.gradle @@ -0,0 +1,39 @@ +apply plugin: 'maven' + +Properties properties = new Properties() +properties.load(project.rootProject.file('scripts/maven.properties').newDataInputStream()) +properties.load(new FileInputStream("${projectDir}/pom.properties")) +properties.load(project.rootProject.file('local.properties').newDataInputStream()) + +apply plugin: 'maven' + +uploadArchives { + repositories { + mavenDeployer { + pom.groupId = properties.groupId + pom.artifactId = properties.artifactId + pom.version = rootProject.ext.Version + + pom.project { + description 'git rev-parse HEAD'.execute([], projectDir).text.trim() + } + + repository(url: properties.releasesRepository + properties.name) { + authentication(userName: properties.user, password: properties.apiKey) + } + } + } +} + +task uploadAar { + dependsOn clean, uploadArchives +} + +//task androidSourcesJar(type: Jar) { +// classifier = "sources" +// from android.sourceSets.main.java.sourceFiles +//} +// +//artifacts { +// archives androidSourcesJar +//} \ No newline at end of file diff --git a/doric-android/version.properties b/doric-android/version.properties new file mode 100644 index 00000000..dab6df0d --- /dev/null +++ b/doric-android/version.properties @@ -0,0 +1 @@ +version=0.1.2 \ No newline at end of file