rename dir

This commit is contained in:
pengfei.zhou
2019-12-21 20:17:45 +08:00
parent feb322ec8d
commit 1d98479c07
169 changed files with 0 additions and 0 deletions

11
doric-android/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
*.iml
.gradle
/local.properties
/.idea/
.DS_Store
/build
/captures
.externalNativeBuild
.project
.classpath
.settings/

201
doric-android/LICENSE Normal file
View File

@@ -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.

3
doric-android/README.md Normal file
View File

@@ -0,0 +1,3 @@
# Doric Android SDK
## Usages

1
doric-android/app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,34 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
applicationId "pub.doric.demo"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation "com.google.android.material:material:1.0.0"
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'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0'
}

21
doric-android/app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pub.doric.demo">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".PullableActivity"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1 @@
*.js

View File

@@ -0,0 +1,28 @@
/*
* 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.demo;
import pub.doric.DoricComponent;
import pub.doric.DoricLibrary;
import pub.doric.DoricRegistry;
@DoricComponent
public class DemoLibrary extends DoricLibrary {
@Override
public void load(DoricRegistry registry) {
registry.registerNativePlugin(DemoPlugin.class);
}
}

View File

@@ -0,0 +1,49 @@
/*
* 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.demo;
import android.widget.Toast;
import com.github.pengfeizhou.jscore.JavaValue;
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.plugin.DoricJavaPlugin;
import pub.doric.utils.ThreadMode;
@DoricPlugin(name = "demo")
public class DemoPlugin extends DoricJavaPlugin {
public DemoPlugin(DoricContext doricContext) {
super(doricContext);
}
@DoricMethod(thread = ThreadMode.UI)
public void test() {
Toast.makeText(getDoricContext().getContext(), "test", Toast.LENGTH_SHORT).show();
}
@DoricMethod(thread = ThreadMode.UI)
public void testPromise(boolean b, DoricPromise doricPromise) {
if (b) {
doricPromise.resolve(new JavaValue("resolved by me"));
} else {
doricPromise.reject(new JavaValue("rejected by me"));
}
Toast.makeText(getDoricContext().getContext(), "test", Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,121 @@
/*
* 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.demo;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import pub.doric.devkit.ui.DemoDebugActivity;
import pub.doric.refresh.DoricSwipeLayout;
import pub.doric.utils.DoricUtils;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final DoricSwipeLayout swipeLayout = findViewById(R.id.swipe_layout);
swipeLayout.setOnRefreshListener(new DoricSwipeLayout.OnRefreshListener() {
@Override
public void onRefresh() {
swipeLayout.setRefreshing(false);
}
});
swipeLayout.setBackgroundColor(Color.YELLOW);
swipeLayout.getRefreshView().setBackgroundColor(Color.RED);
TextView textView = new TextView(this);
textView.setText("This is header");
swipeLayout.getRefreshView().setContent(textView);
RecyclerView recyclerView = findViewById(R.id.root);
recyclerView.setBackgroundColor(Color.WHITE);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
try {
String[] demos = getAssets().list("demo");
List<String> ret = new ArrayList<>();
for (String str : demos) {
if (str.endsWith("js")) {
ret.add(str);
}
}
recyclerView.setAdapter(new MyAdapter(ret.toArray(new String[0])));
} catch (IOException e) {
e.printStackTrace();
}
}
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final String[] data;
public MyAdapter(String[] demos) {
this.data = demos;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
TextView textView = new TextView(parent.getContext());
textView.setGravity(Gravity.CENTER);
textView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
DoricUtils.dp2px(50)));
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
return new RecyclerView.ViewHolder(textView) {
@Override
public String toString() {
return super.toString();
}
};
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
final TextView tv = (TextView) holder.itemView;
tv.setText(data[position]);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(tv.getContext(), DemoDebugActivity.class);
intent.putExtra("scheme", "assets://demo/" + data[position]);
intent.putExtra("alias", data[position]);
tv.getContext().startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return data.length;
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* 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.demo;
import android.app.Application;
import pub.doric.Doric;
import pub.doric.DoricRegistry;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Doric.init(this);
DoricRegistry.register(new DemoLibrary());
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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.demo;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public class MyGlideModule extends AppGlideModule {
}

View File

@@ -0,0 +1,28 @@
package pub.doric.demo;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.FrameLayout;
import pub.doric.refresh.DoricSwipeLayout;
public class PullableActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pullable);
final DoricSwipeLayout swipeRefreshLayout = findViewById(R.id.swipe_layout);
FrameLayout frameLayout = new FrameLayout(this);
frameLayout.setBackgroundColor(Color.YELLOW);
swipeRefreshLayout.addView(frameLayout);
swipeRefreshLayout.setOnRefreshListener(new DoricSwipeLayout.OnRefreshListener() {
@Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(false);
}
});
}
}

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<pub.doric.refresh.DoricSwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
</pub.doric.refresh.DoricSwipeLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PullableActivity">
<pub.doric.refresh.DoricSwipeLayout
android:id="@+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Doric</string>
</resources>

View File

@@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

View File

@@ -0,0 +1,63 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
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
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://dl.bintray.com/osborn/Android"
}
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"
}

1
doric-android/devkit/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,45 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro'
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
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'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'cn.bingoogolapple:bga-qrcode-zbar:1.3.7'
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.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')

View File

View File

@@ -0,0 +1,3 @@
name=DoricDevKit
groupId=pub.doric
artifactId=devkit

21
doric-android/devkit/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,27 @@
package pub.doric.devkit;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("pub.doric.devkit.test", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pub.doric.devkit">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<application>
<activity android:name=".ui.ScanQRCodeActivity" />
<activity
android:name=".ui.DemoDebugActivity"
android:theme="@style/Theme.Design.Light.NoActionBar" />
</application>
</manifest>

View File

@@ -0,0 +1,44 @@
package pub.doric.devkit;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class DevKit implements IDevKit {
public static boolean isRunningInEmulator = false;
public static String ip = "";
private static Gson gson = new Gson();
private static class Inner {
private static final DevKit sInstance = new DevKit();
}
private DevKit() {
}
public static DevKit getInstance() {
return Inner.sInstance;
}
private WSClient wsClient;
@Override
public void connectDevKit(String url) {
wsClient = new WSClient(url);
}
@Override
public void sendDevCommand(IDevKit.Command command, JsonObject jsonObject) {
JsonObject result = new JsonObject();
result.addProperty("cmd", command.toString());
result.add("data", jsonObject);
wsClient.send(gson.toJson(result));
}
@Override
public void disconnectDevKit() {
wsClient.close();
wsClient = null;
}
}

View File

@@ -0,0 +1,37 @@
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(String contextId) {
this.doricContext = DoricContextManager.getContext(contextId);
isDebugging = true;
}
public void startDebug() {
doricDebugDriver = new DoricDebugDriver(new IStatusCallback() {
@Override
public void start() {
doricContext.setDriver(doricDebugDriver);
doricContext.reInit();
}
});
}
public void stopDebug() {
isDebugging = false;
doricDebugDriver.destroy();
doricContext.setDriver(DoricNativeDriver.getInstance());
doricContext.reInit();
}
public DoricContext getContext() {
return doricContext;
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.devkit;
import android.os.Handler;
import android.os.Looper;
import com.github.pengfeizhou.jscore.JSDecoder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import pub.doric.DoricRegistry;
import pub.doric.IDoricDriver;
import pub.doric.async.AsyncCall;
import pub.doric.async.AsyncResult;
import pub.doric.engine.DoricJSEngine;
import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricLog;
import pub.doric.utils.ThreadMode;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricDebugDriver implements IDoricDriver {
private final DoricJSEngine doricJSEngine;
private final ExecutorService mBridgeExecutor;
private final Handler mUIHandler;
private final Handler mJSHandler;
public DoricDebugDriver(IStatusCallback statusCallback) {
doricJSEngine = new DoricDebugJSEngine(statusCallback);
mBridgeExecutor = Executors.newCachedThreadPool();
mUIHandler = new Handler(Looper.getMainLooper());
mJSHandler = doricJSEngine.getJSHandler();
}
@Override
public AsyncResult<JSDecoder> invokeContextEntityMethod(final String contextId, final String method, final Object... args) {
final Object[] nArgs = new Object[args.length + 2];
nArgs[0] = contextId;
nArgs[1] = method;
if (args.length > 0) {
System.arraycopy(args, 0, nArgs, 2, args.length);
}
return invokeDoricMethod(DoricConstant.DORIC_CONTEXT_INVOKE, nArgs);
}
@Override
public AsyncResult<JSDecoder> invokeDoricMethod(final String method, final Object... args) {
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<JSDecoder>() {
@Override
public JSDecoder call() {
try {
return doricJSEngine.invokeDoricMethod(method, args);
} catch (Exception e) {
DoricLog.e("invokeDoricMethod(%s,...),error is %s", method, e.getLocalizedMessage());
return new JSDecoder(null);
}
}
});
}
@Override
public <T> AsyncResult<T> asyncCall(Callable<T> callable, ThreadMode threadMode) {
switch (threadMode) {
case JS:
return AsyncCall.ensureRunInHandler(mJSHandler, callable);
case UI:
return AsyncCall.ensureRunInHandler(mUIHandler, callable);
case INDEPENDENT:
default:
return AsyncCall.ensureRunInExecutor(mBridgeExecutor, callable);
}
}
@Override
public AsyncResult<Boolean> createContext(final String contextId, final String script, final String source) {
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
@Override
public Boolean call() {
try {
doricJSEngine.prepareContext(contextId, script, source);
return true;
} catch (Exception e) {
DoricLog.e("createContext %s error is %s", source, e.getLocalizedMessage());
return false;
}
}
});
}
@Override
public AsyncResult<Boolean> destroyContext(final String contextId) {
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
@Override
public Boolean call() {
try {
doricJSEngine.destroyContext(contextId);
return true;
} catch (Exception e) {
DoricLog.e("destroyContext %s error is %s", contextId, e.getLocalizedMessage());
return false;
}
}
});
}
@Override
public DoricRegistry getRegistry() {
return doricJSEngine.getRegistry();
}
public void destroy() {
doricJSEngine.teardown();
mBridgeExecutor.shutdown();
}
}

View File

@@ -0,0 +1,19 @@
package pub.doric.devkit;
import pub.doric.devkit.remote.DoricRemoteJSExecutor;
import pub.doric.engine.DoricJSEngine;
public class DoricDebugJSEngine extends DoricJSEngine {
private IStatusCallback statusCallback;
public DoricDebugJSEngine(IStatusCallback statusCallback) {
super();
this.statusCallback = statusCallback;
}
@Override
protected void initJSEngine() {
mDoricJSE = new DoricRemoteJSExecutor(statusCallback);
}
}

View File

@@ -0,0 +1,17 @@
package pub.doric.devkit;
import com.google.gson.JsonObject;
public class DoricDev {
public static void connectDevKit(String url) {
DevKit.getInstance().connectDevKit(url);
}
public static void sendDevCommand(IDevKit.Command command, JsonObject jsonObject) {
DevKit.getInstance().sendDevCommand(command, jsonObject);
}
public static void disconnectDevKit() {
DevKit.getInstance().disconnectDevKit();
}
}

View File

@@ -0,0 +1,16 @@
package pub.doric.devkit;
import com.google.gson.JsonObject;
public interface IDevKit {
enum Command {
DEBUG, HOT_RELOAD
}
void connectDevKit(String url);
void sendDevCommand(IDevKit.Command command, JsonObject jsonObject);
void disconnectDevKit();
}

View File

@@ -0,0 +1,5 @@
package pub.doric.devkit;
public interface IStatusCallback {
void start();
}

View File

@@ -0,0 +1,120 @@
/*
* 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.devkit;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.EOFException;
import java.net.ConnectException;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import pub.doric.devkit.event.ConnectExceptionEvent;
import pub.doric.devkit.event.EOFExceptionEvent;
import pub.doric.devkit.event.EnterDebugEvent;
import pub.doric.devkit.event.OpenEvent;
import pub.doric.devkit.event.ReloadEvent;
import pub.doric.devkit.ui.DevPanel;
/**
* @Description: com.github.penfeizhou.doric.dev
* @Author: pengfei.zhou
* @CreateDate: 2019-08-03
*/
public class WSClient extends WebSocketListener {
private final WebSocket webSocket;
public WSClient(String url) {
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder().url(url).build();
webSocket = okHttpClient.newWebSocket(request, this);
}
public void close() {
webSocket.close(-1, "Close");
}
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
DevPanel.isDevConnected = true;
EventBus.getDefault().post(new OpenEvent());
}
@Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
try {
JSONObject jsonObject = new JSONObject(text);
String cmd = jsonObject.optString("cmd");
switch (cmd) {
case "RELOAD": {
String source = jsonObject.optString("source");
String script = jsonObject.optString("script");
EventBus.getDefault().post(new ReloadEvent(source, script));
}
break;
case "SWITCH_TO_DEBUG": {
String contextId = jsonObject.optString("contextId");
EventBus.getDefault().post(new EnterDebugEvent());
}
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
super.onClosing(webSocket, code, reason);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
if (t instanceof EOFException) {
DevPanel.isDevConnected = false;
EventBus.getDefault().post(new EOFExceptionEvent());
} else if (t instanceof ConnectException) {
DevPanel.isDevConnected = false;
EventBus.getDefault().post(new ConnectExceptionEvent());
}
}
public void send(String command) {
webSocket.send(command);
}
}

View File

@@ -0,0 +1,4 @@
package pub.doric.devkit.event;
public class ConnectExceptionEvent {
}

View File

@@ -0,0 +1,4 @@
package pub.doric.devkit.event;
public class EOFExceptionEvent {
}

View File

@@ -0,0 +1,5 @@
package pub.doric.devkit.event;
public class EnterDebugEvent {
}

View File

@@ -0,0 +1,4 @@
package pub.doric.devkit.event;
public class OpenEvent {
}

View File

@@ -0,0 +1,11 @@
package pub.doric.devkit.event;
public class ReloadEvent {
public String source;
public String script;
public ReloadEvent(String source, String script) {
this.source = source;
this.script = script;
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,5 @@
package pub.doric.devkit.event;
public class StopDebugEvent {
}

View File

@@ -0,0 +1,63 @@
/*
* 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.devkit.remote;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSRuntimeException;
import com.github.pengfeizhou.jscore.JavaFunction;
import com.github.pengfeizhou.jscore.JavaValue;
import pub.doric.devkit.IStatusCallback;
import pub.doric.engine.IDoricJSE;
public class DoricRemoteJSExecutor implements IDoricJSE {
private final RemoteJSExecutor mRemoteJSExecutor;
public DoricRemoteJSExecutor(IStatusCallback statusCallback) {
this.mRemoteJSExecutor = new RemoteJSExecutor(statusCallback);
}
@Override
public String loadJS(String script, String source) throws JSRuntimeException {
return mRemoteJSExecutor.loadJS(script, source);
}
@Override
public JSDecoder evaluateJS(String script, String source, boolean hashKey) throws JSRuntimeException {
return mRemoteJSExecutor.evaluateJS(script, source, hashKey);
}
@Override
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
mRemoteJSExecutor.injectGlobalJSFunction(name, javaFunction);
}
@Override
public void injectGlobalJSObject(String name, JavaValue javaValue) {
mRemoteJSExecutor.injectGlobalJSObject(name, javaValue);
}
@Override
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) throws JSRuntimeException {
return mRemoteJSExecutor.invokeMethod(objectName, functionName, javaValues, hashKey);
}
@Override
public void teardown() {
mRemoteJSExecutor.destroy();
}
}

View File

@@ -0,0 +1,148 @@
package pub.doric.devkit.remote;
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 org.greenrobot.eventbus.EventBus;
import org.jetbrains.annotations.NotNull;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.EOFException;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import pub.doric.devkit.DevKit;
import pub.doric.devkit.IStatusCallback;
import pub.doric.devkit.event.StopDebugEvent;
public class RemoteJSExecutor {
private final WebSocket webSocket;
private final Map<String, JavaFunction> globalFunctions = new HashMap<>();
private JSDecoder temp;
public RemoteJSExecutor(final IStatusCallback statusCallback) {
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build();
final Request request = new Request.Builder().url("ws://" + DevKit.ip + ":2080").build();
final Thread current = Thread.currentThread();
webSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
LockSupport.unpark(current);
statusCallback.start();
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
if (t instanceof ConnectException) {
// 连接remote异常
LockSupport.unpark(current);
throw new RuntimeException("remote js executor cannot connect");
} else if (t instanceof EOFException) {
// 被远端强制断开
System.out.println("remote js executor eof");
LockSupport.unpark(current);
EventBus.getDefault().post(new StopDebugEvent());
}
}
@Override
public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
try {
JSONObject jsonObject = new JSONObject(text);
String cmd = jsonObject.optString("cmd");
switch (cmd) {
case "injectGlobalJSFunction": {
String name = jsonObject.optString("name");
JSONArray arguments = jsonObject.optJSONArray("arguments");
assert arguments != null;
JSDecoder[] decoders = new JSDecoder[arguments.length()];
for (int i = 0; i < arguments.length(); i++) {
Object o = arguments.get(i);
decoders[i] = new JSDecoder(new ValueBuilder(o).build());
}
globalFunctions.get(name).exec(decoders);
}
break;
case "invokeMethod": {
try {
Object result = jsonObject.opt("result");
ValueBuilder vb = new ValueBuilder(result);
temp = new JSDecoder(vb.build());
System.out.println(result);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
LockSupport.unpark(current);
}
}
break;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
});
LockSupport.park(current);
}
public String loadJS(String script, String source) {
return null;
}
public JSDecoder evaluateJS(String script, String source, boolean hashKey) {
return null;
}
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
globalFunctions.put(name, javaFunction);
webSocket.send(new JSONBuilder().put("cmd", "injectGlobalJSFunction")
.put("name", name).toString()
);
}
public void injectGlobalJSObject(String name, JavaValue javaValue) {
}
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) {
JSONArray jsonArray = new JSONArray();
for (JavaValue javaValue : javaValues) {
jsonArray.put(new JSONBuilder()
.put("type", javaValue.getType())
.put("value", javaValue.getValue())
.toJSONObject());
}
webSocket.send(new JSONBuilder()
.put("cmd", "invokeMethod")
.put("objectName", objectName)
.put("functionName", functionName)
.put("javaValues", jsonArray)
.put("hashKey", hashKey)
.toString());
LockSupport.park(Thread.currentThread());
return temp;
}
public void destroy() {
webSocket.close(1000, "destroy");
}
}

View File

@@ -0,0 +1,91 @@
package pub.doric.devkit.remote;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
/**
* Created by pengfei.zhou on 2018/4/17.
*/
public class ValueBuilder {
private final Object val;
private void writeBoolean(ByteArrayOutputStream output, boolean b) {
output.write((byte) (b ? 1 : 0));
}
private void writeInt(ByteArrayOutputStream output, int i) {
output.write((byte) (i >>> 24));
output.write((byte) (i >>> 16));
output.write((byte) (i >>> 8));
output.write((byte) i);
}
private void writeDouble(ByteArrayOutputStream output, double d) {
long l = Double.doubleToRawLongBits(d);
output.write((byte) (l >>> 56));
output.write((byte) (l >>> 48));
output.write((byte) (l >>> 40));
output.write((byte) (l >>> 32));
output.write((byte) (l >>> 24));
output.write((byte) (l >>> 16));
output.write((byte) (l >>> 8));
output.write((byte) l);
}
private void writeString(ByteArrayOutputStream output, String S) {
byte[] buf;
try {
buf = S.getBytes("UTF-8");
} catch (Exception e) {
buf = new byte[0];
}
int i = buf.length;
writeInt(output, i);
output.write(buf, 0, i);
}
private void write(ByteArrayOutputStream output, Object O) {
if (O instanceof Number) {
output.write((byte) 'D');
writeDouble(output, Double.valueOf(String.valueOf(O)));
} else if (O instanceof String) {
output.write((byte) 'S');
writeString(output, (String) O);
} else if (O instanceof Boolean) {
output.write((byte) 'B');
writeBoolean(output, (Boolean) O);
} else if (O instanceof JSONObject) {
output.write((byte) 'O');
//writeBoolean(output, (Boolean) O);
Iterator<String> iterator = ((JSONObject) O).keys();
while (iterator.hasNext()) {
String key = iterator.next();
write(output, key);
write(output, ((JSONObject) O).opt(key));
}
output.write((byte) 'N');
} else if (O instanceof JSONArray) {
output.write((byte) 'A');
writeInt(output, ((JSONArray) O).length());
for (int i = 0; i < ((JSONArray) O).length(); i++) {
write(output, ((JSONArray) O).opt(i));
}
} else {
output.write((byte) 'N');
}
}
public ValueBuilder(Object o) {
this.val = o;
}
public byte[] build() {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
write(outputStream, val);
return outputStream.toByteArray();
}
}

View File

@@ -0,0 +1,86 @@
package pub.doric.devkit.ui;
import android.app.Dialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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.DoricDev;
import pub.doric.devkit.IDevKit;
import pub.doric.devkit.R;
import pub.doric.devkit.event.StartDebugEvent;
public class DebugContextPanel extends DialogFragment {
public DebugContextPanel() {
}
@Nullable
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState
) {
return inflater.inflate(R.layout.layout_debug_context, container, false);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
LinearLayout container = getView().findViewById(R.id.container);
LayoutInflater inflater = LayoutInflater.from(getContext());
for (final DoricContext doricContext : DoricContextManager.aliveContexts()) {
View cell = inflater.inflate(R.layout.layout_debug_context_cell, container, false);
TextView contextIdTextView = cell.findViewById(R.id.context_id_text_view);
contextIdTextView.setText(doricContext.getContextId());
TextView sourceTextView = cell.findViewById(R.id.source_text_view);
sourceTextView.setText(doricContext.getSource());
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("source", doricContext.getSource().replace(".js", ".ts"));
DoricDev.sendDevCommand(IDevKit.Command.DEBUG, jsonObject);
dismissAllowingStateLoss();
}
});
container.addView(cell);
}
}
}

View File

@@ -0,0 +1,118 @@
/*
* 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.devkit.ui;
import android.os.Bundle;
import android.view.KeyEvent;
import androidx.annotation.Nullable;
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.devkit.DoricContextDebuggable;
import pub.doric.devkit.event.EnterDebugEvent;
import pub.doric.devkit.event.ReloadEvent;
import pub.doric.devkit.event.StartDebugEvent;
import pub.doric.devkit.event.StopDebugEvent;
import pub.doric.devkit.util.SensorManagerHelper;
/**
* @Description: pub.doric.demo
* @Author: pengfei.zhou
* @CreateDate: 2019-11-19
*/
public class DemoDebugActivity extends DoricActivity {
private SensorManagerHelper sensorHelper;
private DoricContextDebuggable doricContextDebuggable;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sensorHelper = new SensorManagerHelper(this);
sensorHelper.setOnShakeListener(new SensorManagerHelper.OnShakeListener() {
@Override
public void onShake() {
Fragment devPanel = getSupportFragmentManager().findFragmentByTag("DevPanel");
if (devPanel != null && devPanel.isAdded()) {
return;
}
new DevPanel().show(getSupportFragmentManager(), "DevPanel");
}
});
}
@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();
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 onQuitDebugEvent(StopDebugEvent quitDebugEvent) {
doricContextDebuggable.stopDebug();
}
@Subscribe(threadMode = ThreadMode.MAIN)
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
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_MENU == event.getKeyCode()) {
new DevPanel().show(getSupportFragmentManager(), "DevPanel");
}
return super.onKeyDown(keyCode, event);
}
}

View File

@@ -0,0 +1,136 @@
package pub.doric.devkit.ui;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.lahm.library.EasyProtectorLib;
import com.lahm.library.EmulatorCheckCallback;
import com.tbruyelle.rxpermissions2.RxPermissions;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import pub.doric.devkit.DevKit;
import pub.doric.devkit.DoricDev;
import pub.doric.devkit.R;
import pub.doric.devkit.event.ConnectExceptionEvent;
import pub.doric.devkit.event.EOFExceptionEvent;
import pub.doric.devkit.event.OpenEvent;
public class DevPanel extends BottomSheetDialogFragment {
public static boolean isDevConnected = false;
public DevPanel() {
}
@Nullable
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState
) {
return inflater.inflate(R.layout.layout_dev, container, false);
}
@Override
public void onStart() {
super.onStart();
updateUI();
getView().findViewById(R.id.connect_dev_kit_text_view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (DevKit.isRunningInEmulator) {
DevKit.ip = "10.0.2.2";
DoricDev.connectDevKit("ws://" + DevKit.ip + ":7777");
} else {
final RxPermissions rxPermissions = new RxPermissions(DevPanel.this);
Disposable disposable = rxPermissions
.request(Manifest.permission.CAMERA)
.subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean grant) throws Exception {
if (grant) {
Intent intent = new Intent(getContext(), ScanQRCodeActivity.class);
getContext().startActivity(intent);
}
}
});
}
}
});
getView().findViewById(R.id.debug_text_view).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DebugContextPanel debugContextPanel = new DebugContextPanel();
debugContextPanel.show(getActivity().getSupportFragmentManager(), "DebugContextPanel");
dismissAllowingStateLoss();
}
});
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
EventBus.getDefault().register(this);
DevKit.isRunningInEmulator = EasyProtectorLib.checkIsRunningInEmulator(context, new EmulatorCheckCallback() {
@Override
public void findEmulator(String emulatorInfo) {
System.out.println(emulatorInfo);
}
});
}
@Override
public void onDetach() {
super.onDetach();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onOpenEvent(OpenEvent openEvent) {
updateUI();
Toast.makeText(getContext(), "dev kit connected", Toast.LENGTH_LONG).show();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEOFEvent(EOFExceptionEvent eofExceptionEvent) {
updateUI();
Toast.makeText(getContext(), "dev kit eof exception", Toast.LENGTH_LONG).show();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onConnectExceptionEvent(ConnectExceptionEvent connectExceptionEvent) {
updateUI();
Toast.makeText(getContext(), "dev kit connection exception", Toast.LENGTH_LONG).show();
}
private void updateUI() {
if (isDevConnected) {
getView().findViewById(R.id.connect_dev_kit_text_view).setVisibility(View.GONE);
getView().findViewById(R.id.debug_text_view).setVisibility(View.VISIBLE);
getView().findViewById(R.id.hot_reload_text_view).setVisibility(View.VISIBLE);
} else {
getView().findViewById(R.id.connect_dev_kit_text_view).setVisibility(View.VISIBLE);
getView().findViewById(R.id.debug_text_view).setVisibility(View.GONE);
getView().findViewById(R.id.hot_reload_text_view).setVisibility(View.GONE);
}
}
}

View File

@@ -0,0 +1,79 @@
package pub.doric.devkit.ui;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import cn.bingoogolapple.qrcode.core.QRCodeView;
import cn.bingoogolapple.qrcode.zbar.ZBarView;
import pub.doric.devkit.DevKit;
import pub.doric.devkit.DoricDev;
import pub.doric.devkit.R;
public class ScanQRCodeActivity extends AppCompatActivity implements QRCodeView.Delegate {
private ZBarView mZbarView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_scan_qrcode);
mZbarView = findViewById(R.id.zbar_view);
mZbarView.setDelegate(this);
}
@Override
protected void onStart() {
super.onStart();
mZbarView.startCamera();
mZbarView.startSpotAndShowRect();
}
@Override
protected void onStop() {
super.onStop();
mZbarView.stopCamera();
super.onStop();
}
@Override
protected void onDestroy() {
mZbarView.onDestroy();
super.onDestroy();
}
@Override
public void onScanQRCodeSuccess(String result) {
setTitle("扫描结果为:" + result);
DevKit.ip = result;
Toast.makeText(this, "dev kit connecting to " + result, Toast.LENGTH_LONG).show();
DoricDev.connectDevKit("ws://" + result + ":7777");
finish();
}
@Override
public void onCameraAmbientBrightnessChanged(boolean isDark) {
String tipText = mZbarView.getScanBoxView().getTipText();
String ambientBrightnessTip = "\n环境过暗请打开闪光灯";
if (isDark) {
if (!tipText.contains(ambientBrightnessTip)) {
mZbarView.getScanBoxView().setTipText(tipText + ambientBrightnessTip);
}
} else {
if (tipText.contains(ambientBrightnessTip)) {
tipText = tipText.substring(0, tipText.indexOf(ambientBrightnessTip));
mZbarView.getScanBoxView().setTipText(tipText);
}
}
}
@Override
public void onScanQRCodeOpenCameraError() {
System.out.println();
}
}

View File

@@ -0,0 +1,113 @@
package pub.doric.devkit.util;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
public class SensorManagerHelper implements SensorEventListener {
// 速度阈值,当摇晃速度达到这值后产生作用
private final int SPEED_SHRESHOLD = 5000;
// 两次检测的时间间隔
private final int UPTATE_INTERVAL_TIME = 50;
// 传感器管理器
private SensorManager sensorManager;
// 传感器
private Sensor sensor;
// 重力感应监听器
private OnShakeListener onShakeListener;
// 上下文对象context
private Context context;
// 手机上一个位置时重力感应坐标
private float lastX;
private float lastY;
private float lastZ;
// 上次检测时间
private long lastUpdateTime;
public SensorManagerHelper(Context context) {
this.context = context;
start();
}
/**
* 开始检测
*/
public void start() {
// 获得传感器管理器
sensorManager = (SensorManager) context
.getSystemService(Context.SENSOR_SERVICE);
if (sensorManager != null) {
// 获得重力传感器
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
// 注册
if (sensor != null) {
sensorManager.registerListener(this, sensor,
SensorManager.SENSOR_DELAY_GAME);
}
}
/**
* 停止检测
*/
public void stop() {
sensorManager.unregisterListener(this);
}
/**
* 摇晃监听接口
*/
public interface OnShakeListener {
void onShake();
}
/**
* 设置重力感应监听器
*/
public void setOnShakeListener(OnShakeListener listener) {
onShakeListener = listener;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
/**
* 重力感应器感应获得变化数据
* android.hardware.SensorEventListener#onSensorChanged(android.hardware
* .SensorEvent)
*/
@Override
public void onSensorChanged(SensorEvent event) {
// 现在检测时间
long currentUpdateTime = System.currentTimeMillis();
// 两次检测的时间间隔
long timeInterval = currentUpdateTime - lastUpdateTime;
// 判断是否达到了检测时间间隔
if (timeInterval < UPTATE_INTERVAL_TIME) return;
// 现在的时间变成last时间
lastUpdateTime = currentUpdateTime;
// 获得x,y,z坐标
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
// 获得x,y,z的变化值
float deltaX = x - lastX;
float deltaY = y - lastY;
float deltaZ = z - lastZ;
// 将现在的坐标变成last坐标
lastX = x;
lastY = y;
lastZ = z;
double speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ
* deltaZ)
/ timeInterval * 10000;
// 达到速度阀值,发出提示
if (speed >= SPEED_SHRESHOLD) {
onShakeListener.onShake();
}
}
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/context_id_text_view"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="1"
android:gravity="center"
android:singleLine="true"
android:text="{Context Id}"
android:textSize="20sp" />
<TextView
android:id="@+id/source_text_view"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="3"
android:gravity="center"
android:singleLine="true"
android:text="{Source}"
android:textSize="20sp" />
<TextView
android:id="@+id/debug_text_view"
android:layout_width="0dp"
android:layout_height="60dp"
android:layout_weight="1"
android:background="#ff0000"
android:gravity="center"
android:singleLine="true"
android:text="Debug"
android:textColor="#ffffff"
android:textSize="20sp" />
</LinearLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/divider"
android:orientation="vertical"
android:showDividers="middle">
<TextView
android:id="@+id/connect_dev_kit_text_view"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="Dev Kit"
android:textSize="20sp" />
<TextView
android:id="@+id/debug_text_view"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="Debug via Visual Studio Code"
android:textSize="20sp"
android:visibility="gone" />
<TextView
android:id="@+id/hot_reload_text_view"
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:text="Enable Hot Reload"
android:textSize="20sp"
android:visibility="gone" />
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cn.bingoogolapple.qrcode.zbar.ZBarView
android:id="@+id/zbar_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:qrcv_animTime="1000"
app:qrcv_borderColor="@android:color/white"
app:qrcv_borderSize="1dp"
app:qrcv_cornerColor="@android:color/white"
app:qrcv_cornerLength="20dp"
app:qrcv_cornerSize="3dp"
app:qrcv_isShowDefaultScanLineDrawable="true"
app:qrcv_maskColor="#33FFFFFF"
app:qrcv_rectWidth="200dp"
app:qrcv_scanLineColor="@android:color/white"
app:qrcv_topOffset="90dp" />
</LinearLayout>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">devkit</string>
</resources>

1
doric-android/doric/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,55 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
consumerProguardFiles 'proguard-rules.pro'
}
}
}
afterEvaluate {
buildJSBundle.exec()
}
task buildJSBundle(type: Exec) {
commandLine 'sh', project.rootDir.getParent() + "/bundle.sh"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.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')

View File

@@ -0,0 +1,4 @@
name=Doric
groupId=pub.doric
artifactId=core
version=0.1.0

31
doric-android/doric/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,31 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class com.github.penfeizhou.doric.extension.bridge.DoricPlugin
-keep class com.github.penfeizhou.doric.extension.bridge.DoricMethod
-keep @com.github.penfeizhou.doric.extension.bridge.DoricPlugin class * {*;}
-keepclasseswithmembers @com.github.penfeizhou.doric.extension.bridge.DoricPlugin class * {*;}
-keep class * {
@com.github.penfeizhou.doric.extension.bridge.DoricMethod <fields>;
}

View File

@@ -0,0 +1,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pub.doric">
<application>
<activity
android:name="pub.doric.DoricActivity"
android:theme="@style/Theme.Design.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1 @@
*.js

View File

@@ -0,0 +1,35 @@
/*
* 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;
import android.app.Application;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class Doric {
private static Application sApplication;
public static void init(Application application) {
sApplication = application;
}
public static Application application() {
return sApplication;
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
/**
* @Description: pub.doric.demo
* @Author: pengfei.zhou
* @CreateDate: 2019-11-19
*/
public class DoricActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.doric_activity);
if (savedInstanceState == null) {
String scheme = getIntent().getStringExtra("scheme");
String alias = getIntent().getStringExtra("alias");
DoricFragment doricFragment = DoricFragment.newInstance(scheme, alias);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, doricFragment)
.commit();
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoricComponent {
String name() default "";
}

View File

@@ -0,0 +1,214 @@
/*
* 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;
import android.animation.AnimatorSet;
import android.content.Context;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSONBuilder;
import org.json.JSONObject;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import pub.doric.async.AsyncResult;
import pub.doric.navbar.IDoricNavBar;
import pub.doric.navigator.IDoricNavigator;
import pub.doric.plugin.DoricJavaPlugin;
import pub.doric.shader.RootNode;
import pub.doric.shader.ViewNode;
import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricMetaInfo;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricContext {
private final String mContextId;
private final Map<String, DoricJavaPlugin> mPluginMap = new HashMap<>();
private final Context mContext;
private RootNode mRootNode = new RootNode(this);
private final String source;
private String script;
private String extra;
private JSONObject initParams;
private IDoricDriver doricDriver;
private final Map<String, ViewNode> mHeadNodes = new HashMap<>();
public Collection<ViewNode> allHeadNodes() {
return mHeadNodes.values();
}
public void addHeadNode(ViewNode viewNode) {
mHeadNodes.put(viewNode.getId(), viewNode);
}
public void removeHeadNode(ViewNode viewNode) {
mHeadNodes.remove(viewNode.getId());
}
public ViewNode targetViewNode(String id) {
if (id.equals(mRootNode.getId())) {
return mRootNode;
}
return mHeadNodes.get(id);
}
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() {
return source;
}
public String getScript() {
return script;
}
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, extra);
callEntity(DoricConstant.DORIC_ENTITY_CREATE);
}
public void reInit() {
this.mRootNode.setId("");
callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams, extra);
callEntity(DoricConstant.DORIC_ENTITY_CREATE);
}
public AsyncResult<JSDecoder> callEntity(String methodName, Object... args) {
return getDriver().invokeContextEntityMethod(mContextId, methodName, args);
}
public IDoricDriver getDriver() {
if (doricDriver == null) {
doricDriver = DoricNativeDriver.getInstance();
}
return doricDriver;
}
public void setDriver(IDoricDriver doricDriver) {
this.doricDriver = doricDriver;
}
public RootNode getRootNode() {
return mRootNode;
}
public Context getContext() {
return mContext;
}
public String getContextId() {
return mContextId;
}
public void teardown() {
callEntity(DoricConstant.DORIC_ENTITY_DESTROY);
DoricContextManager.getInstance().destroyContext(this).setCallback(new AsyncResult.Callback<Boolean>() {
@Override
public void onResult(Boolean result) {
}
@Override
public void onError(Throwable t) {
}
@Override
public void onFinish() {
mPluginMap.clear();
}
});
}
public DoricJavaPlugin obtainPlugin(DoricMetaInfo<DoricJavaPlugin> doricMetaInfo) {
DoricJavaPlugin plugin = mPluginMap.get(doricMetaInfo.getName());
if (plugin == null) {
plugin = doricMetaInfo.createInstance(this);
mPluginMap.put(doricMetaInfo.getName(), plugin);
}
return plugin;
}
public void reload(String script) {
this.script = script;
this.mRootNode.setId("");
getDriver().createContext(mContextId, script, source);
callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams, extra);
onShow();
}
public void onShow() {
callEntity(DoricConstant.DORIC_ENTITY_SHOW);
}
public void onHidden() {
callEntity(DoricConstant.DORIC_ENTITY_HIDDEN);
}
private IDoricNavigator doricNavigator;
public void setDoricNavigator(IDoricNavigator doricNavigator) {
this.doricNavigator = doricNavigator;
}
public IDoricNavigator getDoricNavigator() {
return this.doricNavigator;
}
private IDoricNavBar doricNavBar;
public void setDoricNavBar(IDoricNavBar navBar) {
this.doricNavBar = navBar;
}
public IDoricNavBar getDoricNavBar() {
return this.doricNavBar;
}
private AnimatorSet animatorSet;
public void setAnimatorSet(AnimatorSet animatorSet) {
this.animatorSet = animatorSet;
}
public AnimatorSet getAnimatorSet() {
return this.animatorSet;
}
}

View File

@@ -0,0 +1,91 @@
/*
* 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;
import android.content.Context;
import pub.doric.async.AsyncResult;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description: com.github.penfeizhou.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-19
*/
public class DoricContextManager {
private final AtomicInteger counter = new AtomicInteger();
private final Map<String, DoricContext> doricContextMap = new ConcurrentHashMap<>();
private static class Inner {
private static final DoricContextManager sInstance = new DoricContextManager();
}
private DoricContextManager() {
}
public static DoricContextManager getInstance() {
return Inner.sInstance;
}
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, extra);
doricContextMap.put(contextId, doricContext);
doricContext.getDriver().createContext(contextId, script, source);
return doricContext;
}
AsyncResult<Boolean> destroyContext(final DoricContext context) {
final AsyncResult<Boolean> result = new AsyncResult<>();
context.getDriver().destroyContext(context.getContextId()).setCallback(new AsyncResult.Callback<Boolean>() {
@Override
public void onResult(Boolean b) {
result.setResult(b);
}
@Override
public void onError(Throwable t) {
result.setError(t);
}
@Override
public void onFinish() {
doricContextMap.remove(context.getContextId());
}
});
return result;
}
public static DoricContext getContext(String contextId) {
return getInstance().doricContextMap.get(contextId);
}
public static Set<String> getKeySet() {
return getInstance().doricContextMap.keySet();
}
public static Collection<DoricContext> aliveContexts() {
return getInstance().doricContextMap.values();
}
}

View File

@@ -0,0 +1,74 @@
/*
* 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;
import android.os.Bundle;
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 androidx.navigation.NavController;
import androidx.navigation.Navigation;
/**
* @Description: pub.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-11-23
*/
public class DoricFragment extends Fragment {
public static DoricFragment newInstance(String scheme, String alias) {
Bundle args = new Bundle();
args.putString("scheme", scheme);
args.putString("alias", alias);
DoricFragment fragment = new DoricFragment();
fragment.setArguments(args);
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) {
return inflater.inflate(R.layout.doric_fragment, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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;
/**
* @Description: com.github.penfeizhou.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-20
*/
public abstract class DoricLibrary {
public abstract void load(DoricRegistry registry);
}

View File

@@ -0,0 +1,135 @@
/*
* 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;
import android.os.Handler;
import android.os.Looper;
import com.github.pengfeizhou.jscore.JSDecoder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import pub.doric.async.AsyncCall;
import pub.doric.async.AsyncResult;
import pub.doric.engine.DoricJSEngine;
import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricLog;
import pub.doric.utils.ThreadMode;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricNativeDriver implements IDoricDriver {
private final DoricJSEngine doricJSEngine;
private final ExecutorService mBridgeExecutor;
private final Handler mUIHandler;
private final Handler mJSHandler;
private static class Inner {
private static final DoricNativeDriver sInstance = new DoricNativeDriver();
}
private DoricNativeDriver() {
doricJSEngine = new DoricJSEngine();
mBridgeExecutor = Executors.newCachedThreadPool();
mUIHandler = new Handler(Looper.getMainLooper());
mJSHandler = doricJSEngine.getJSHandler();
}
public static DoricNativeDriver getInstance() {
return Inner.sInstance;
}
@Override
public AsyncResult<JSDecoder> invokeContextEntityMethod(final String contextId, final String method, final Object... args) {
final Object[] nArgs = new Object[args.length + 2];
nArgs[0] = contextId;
nArgs[1] = method;
if (args.length > 0) {
System.arraycopy(args, 0, nArgs, 2, args.length);
}
return invokeDoricMethod(DoricConstant.DORIC_CONTEXT_INVOKE, nArgs);
}
@Override
public AsyncResult<JSDecoder> invokeDoricMethod(final String method, final Object... args) {
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<JSDecoder>() {
@Override
public JSDecoder call() {
try {
return doricJSEngine.invokeDoricMethod(method, args);
} catch (Exception e) {
DoricLog.e("invokeDoricMethod(%s,...),error is %s", method, e.getLocalizedMessage());
return new JSDecoder(null);
}
}
});
}
@Override
public <T> AsyncResult<T> asyncCall(Callable<T> callable, ThreadMode threadMode) {
switch (threadMode) {
case JS:
return AsyncCall.ensureRunInHandler(mJSHandler, callable);
case UI:
return AsyncCall.ensureRunInHandler(mUIHandler, callable);
case INDEPENDENT:
default:
return AsyncCall.ensureRunInExecutor(mBridgeExecutor, callable);
}
}
@Override
public AsyncResult<Boolean> createContext(final String contextId, final String script, final String source) {
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
@Override
public Boolean call() {
try {
doricJSEngine.prepareContext(contextId, script, source);
return true;
} catch (Exception e) {
DoricLog.e("createContext %s error is %s", source, e.getLocalizedMessage());
return false;
}
}
});
}
@Override
public AsyncResult<Boolean> destroyContext(final String contextId) {
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
@Override
public Boolean call() {
try {
doricJSEngine.destroyContext(contextId);
return true;
} catch (Exception e) {
DoricLog.e("destroyContext %s error is %s", contextId, e.getLocalizedMessage());
return false;
}
}
});
}
@Override
public DoricRegistry getRegistry() {
return doricJSEngine.getRegistry();
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.OnLifecycleEvent;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import pub.doric.utils.DoricUtils;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricPanel extends FrameLayout implements LifecycleObserver {
private DoricContext mDoricContext;
public DoricPanel(@NonNull Context context) {
this(context, null);
}
public DoricPanel(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DoricPanel(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (getContext() instanceof LifecycleOwner) {
((LifecycleOwner) getContext()).getLifecycle().addObserver(this);
}
}
public void config(String script, String alias, String extra) {
DoricContext doricContext = DoricContext.create(getContext(), script, alias, extra);
config(doricContext);
}
public void config(DoricContext doricContext) {
mDoricContext = doricContext;
mDoricContext.getRootNode().setRootView(this);
if (getMeasuredState() != 0) {
mDoricContext.init(DoricUtils.px2dp(getMeasuredWidth()), DoricUtils.px2dp(getMeasuredHeight()));
}
if (getContext() instanceof LifecycleOwner
&& ((LifecycleOwner) getContext()).getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
mDoricContext.onShow();
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
}
public DoricContext getDoricContext() {
return mDoricContext;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (oldw != w || oldh != h) {
if (mDoricContext != null) {
mDoricContext.init(DoricUtils.px2dp(w), DoricUtils.px2dp(h));
}
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onActivityResume() {
if (mDoricContext != null) {
mDoricContext.onShow();
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onActivityPause() {
if (mDoricContext != null) {
mDoricContext.onHidden();
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onActivityDestroy() {
if (mDoricContext != null) {
mDoricContext.teardown();
}
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
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;
import pub.doric.navbar.BaseDoricNavBar;
import pub.doric.navigator.IDoricNavigator;
import pub.doric.utils.DoricLog;
/**
* @Description: pub.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-11-23
*/
public class DoricPanelFragment extends Fragment implements IDoricNavigator {
private DoricPanel doricPanel;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.doric_framgent_panel, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
doricPanel = view.findViewById(R.id.doric_panel);
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<String>() {
@Override
public void onResult(String result) {
doricPanel.config(result, alias, extra);
DoricContext context = doricPanel.getDoricContext();
context.setDoricNavigator(DoricPanelFragment.this);
BaseDoricNavBar navBar = requireActivity().getWindow().getDecorView().findViewById(R.id.doric_nav_bar);
context.setDoricNavBar(navBar);
}
@Override
public void onError(Throwable t) {
DoricLog.e("DoricPanelFragment load JS error:" + t.getLocalizedMessage());
}
@Override
public void onFinish() {
}
});
}
@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();
}
}

View File

@@ -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;
import android.text.TextUtils;
import pub.doric.loader.DoricAssetJSLoader;
import pub.doric.loader.DoricHttpJSLoader;
import pub.doric.loader.IDoricJSLoader;
import pub.doric.plugin.AnimatePlugin;
import pub.doric.plugin.NavBarPlugin;
import pub.doric.plugin.NavigatorPlugin;
import pub.doric.plugin.NetworkPlugin;
import pub.doric.plugin.PopoverPlugin;
import pub.doric.plugin.ShaderPlugin;
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;
import pub.doric.shader.list.ListItemNode;
import pub.doric.shader.list.ListNode;
import pub.doric.shader.RootNode;
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;
import pub.doric.plugin.DoricJavaPlugin;
import pub.doric.plugin.ModalPlugin;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description: pub.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-20
*/
public class DoricRegistry {
private static Map<String, String> bundles = new ConcurrentHashMap<>();
private static Set<DoricLibrary> doricLibraries = new HashSet<>();
private static Set<IDoricJSLoader> jsLoaders = new HashSet<>();
static {
addJSLoader(new DoricAssetJSLoader());
addJSLoader(new DoricHttpJSLoader());
}
private Map<String, DoricMetaInfo<DoricJavaPlugin>> pluginInfoMap = new HashMap<>();
private Map<String, DoricMetaInfo<ViewNode>> nodeInfoMap = new HashMap<>();
private static void initRegistry(DoricRegistry doricRegistry) {
for (DoricLibrary library : doricLibraries) {
library.load(doricRegistry);
}
}
public static void register(DoricLibrary doricLibrary) {
doricLibraries.add(doricLibrary);
}
public DoricRegistry() {
this.registerNativePlugin(ShaderPlugin.class);
this.registerNativePlugin(ModalPlugin.class);
this.registerNativePlugin(NetworkPlugin.class);
this.registerNativePlugin(StoragePlugin.class);
this.registerNativePlugin(NavigatorPlugin.class);
this.registerNativePlugin(NavBarPlugin.class);
this.registerNativePlugin(PopoverPlugin.class);
this.registerNativePlugin(AnimatePlugin.class);
this.registerViewNode(RootNode.class);
this.registerViewNode(TextNode.class);
this.registerViewNode(ImageNode.class);
this.registerViewNode(StackNode.class);
this.registerViewNode(VLayoutNode.class);
this.registerViewNode(HLayoutNode.class);
this.registerViewNode(ListNode.class);
this.registerViewNode(ListItemNode.class);
this.registerViewNode(ScrollerNode.class);
this.registerViewNode(SliderNode.class);
this.registerViewNode(SlideItemNode.class);
this.registerViewNode(RefreshableNode.class);
this.registerViewNode(FlowLayoutNode.class);
this.registerViewNode(FlowLayoutItemNode.class);
this.registerViewNode(InputNode.class);
this.registerViewNode(NestedSliderNode.class);
initRegistry(this);
}
public void registerJSBundle(String name, String bundle) {
bundles.put(name, bundle);
}
public void registerNativePlugin(Class<? extends DoricJavaPlugin> pluginClass) {
DoricMetaInfo<DoricJavaPlugin> doricMetaInfo = new DoricMetaInfo<>(pluginClass);
if (!TextUtils.isEmpty(doricMetaInfo.getName())) {
pluginInfoMap.put(doricMetaInfo.getName(), doricMetaInfo);
}
}
public DoricMetaInfo<DoricJavaPlugin> acquirePluginInfo(String name) {
return pluginInfoMap.get(name);
}
public void registerViewNode(Class<? extends ViewNode> pluginClass) {
DoricMetaInfo<ViewNode> doricMetaInfo = new DoricMetaInfo<>(pluginClass);
if (!TextUtils.isEmpty(doricMetaInfo.getName())) {
nodeInfoMap.put(doricMetaInfo.getName(), doricMetaInfo);
}
}
public DoricMetaInfo<ViewNode> acquireViewNodeInfo(String name) {
return nodeInfoMap.get(name);
}
public String acquireJSBundle(String name) {
return bundles.get(name);
}
public static void addJSLoader(IDoricJSLoader jsLoader) {
jsLoaders.add(jsLoader);
}
public static Collection<IDoricJSLoader> getJSLoaders() {
return jsLoaders;
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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;
import com.github.pengfeizhou.jscore.JSDecoder;
import java.util.concurrent.Callable;
import pub.doric.async.AsyncResult;
import pub.doric.utils.ThreadMode;
/**
* @Description: com.github.penfeizhou.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-19
*/
public interface IDoricDriver {
AsyncResult<JSDecoder> invokeContextEntityMethod(final String contextId, final String method, final Object... args);
AsyncResult<JSDecoder> invokeDoricMethod(final String method, final Object... args);
<T> AsyncResult<T> asyncCall(Callable<T> callable, ThreadMode threadMode);
AsyncResult<Boolean> createContext(final String contextId, final String script, final String source);
AsyncResult<Boolean> destroyContext(final String contextId);
DoricRegistry getRegistry();
}

View File

@@ -0,0 +1,70 @@
/*
* 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.async;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
/**
* @Description: com.github.penfeizhou.doric.async
* @Author: pengfei.zhou
* @CreateDate: 2019-07-19
*/
public class AsyncCall {
public static <T> AsyncResult<T> ensureRunInHandler(Handler handler, final Callable<T> callable) {
final AsyncResult<T> asyncResult = new AsyncResult<>();
if (Looper.myLooper() == handler.getLooper()) {
try {
asyncResult.setResult(callable.call());
} catch (Exception e) {
e.printStackTrace();
asyncResult.setError(e);
}
} else {
handler.post(new Runnable() {
@Override
public void run() {
try {
asyncResult.setResult(callable.call());
} catch (Exception e) {
e.printStackTrace();
asyncResult.setError(e);
}
}
});
}
return asyncResult;
}
public static <T> AsyncResult<T> ensureRunInExecutor(ExecutorService executorService, final Callable<T> callable) {
final AsyncResult<T> asyncResult = new AsyncResult<>();
executorService.execute(new Runnable() {
@Override
public void run() {
try {
asyncResult.setResult(callable.call());
} catch (Exception e) {
asyncResult.setError(e);
}
}
});
return asyncResult;
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.async;
/**
* @Description: com.github.penfeizhou.doric.async
* @Author: pengfei.zhou
* @CreateDate: 2019-07-19
*/
public class AsyncResult<R> {
private static Object EMPTY = new Object();
private Object result = EMPTY;
private Callback<R> callback = null;
public AsyncResult() {
}
public AsyncResult(R r) {
this.result = r;
}
public void setResult(R result) {
this.result = result;
if (this.callback != null) {
this.callback.onResult(result);
this.callback.onFinish();
}
}
public void setError(Throwable result) {
this.result = result;
if (this.callback != null) {
this.callback.onError(result);
this.callback.onFinish();
}
}
public boolean hasResult() {
return result != EMPTY;
}
public R getResult() {
return (R) result;
}
public void setCallback(Callback<R> callback) {
this.callback = callback;
if (result instanceof Throwable) {
this.callback.onError((Throwable) result);
this.callback.onFinish();
} else if (result != EMPTY) {
this.callback.onResult((R) result);
this.callback.onFinish();
}
}
public SettableFuture<R> synchronous() {
final SettableFuture<R> settableFuture = new SettableFuture<>();
setCallback(new Callback<R>() {
@Override
public void onResult(R result) {
settableFuture.set(result);
}
@Override
public void onError(Throwable t) {
settableFuture.set(null);
}
@Override
public void onFinish() {
}
});
return settableFuture;
}
public interface Callback<R> {
void onResult(R result);
void onError(Throwable t);
void onFinish();
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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.async;
/**
* @Description: com.github.penfeizhou.doric.async
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* A super simple Future-like class that can safely notify another Thread when a value is ready.
* Does not support setting errors or canceling.
*/
public class SettableFuture<T> {
private final CountDownLatch mReadyLatch = new CountDownLatch(1);
private volatile
T mResult;
/**
* Sets the result. If another thread has called {@link #get}, they will immediately receive the
* value. Must only be called once.
*/
public void set(T result) {
if (mReadyLatch.getCount() == 0) {
throw new RuntimeException("Result has already been set!");
}
mResult = result;
mReadyLatch.countDown();
}
/**
* Wait up to the timeout time for another Thread to set a value on this future. If a value has
* already been set, this method will return immediately.
* <p>
* NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you
* are in the 1% of cases where you actually want to handle that.
*/
public T get(long timeoutMS) {
try {
if (!mReadyLatch.await(timeoutMS, TimeUnit.MILLISECONDS)) {
throw new TimeoutException();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return mResult;
}
public T get() {
try {
mReadyLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return mResult;
}
public static class TimeoutException extends RuntimeException {
public TimeoutException() {
super("Timed out waiting for future");
}
}
}

View File

@@ -0,0 +1,254 @@
/*
* 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.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;
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;
import pub.doric.utils.DoricConstant;
import pub.doric.utils.DoricLog;
import pub.doric.utils.DoricUtils;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.TimerCallback {
private HandlerThread handlerThread;
private final Handler mJSHandler;
private final DoricBridgeExtension mDoricBridgeExtension = new DoricBridgeExtension();
protected IDoricJSE mDoricJSE;
private final DoricTimerExtension mTimerExtension;
private final DoricRegistry mDoricRegistry = new DoricRegistry();
public DoricJSEngine() {
handlerThread = new HandlerThread(this.getClass().getSimpleName());
handlerThread.start();
Looper looper = handlerThread.getLooper();
mJSHandler = new Handler(looper, this);
mJSHandler.post(new Runnable() {
@Override
public void run() {
initJSEngine();
injectGlobal();
initDoricRuntime();
}
});
mTimerExtension = new DoricTimerExtension(looper, this);
}
public Handler getJSHandler() {
return mJSHandler;
}
protected void initJSEngine() {
mDoricJSE = new DoricNativeJSExecutor();
}
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) {
try {
String type = args[0].string();
String message = args[1].string();
switch (type) {
case "w":
DoricLog.suffix_w("_js", message);
break;
case "e":
DoricLog.suffix_e("_js", message);
break;
default:
DoricLog.suffix_d("_js", message);
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_EMPTY, new JavaFunction() {
@Override
public JavaValue exec(JSDecoder[] args) {
return null;
}
});
mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_REQUIRE, new JavaFunction() {
@Override
public JavaValue exec(JSDecoder[] args) {
try {
String name = args[0].string();
String content = mDoricRegistry.acquireJSBundle(name);
if (TextUtils.isEmpty(content)) {
DoricLog.e("require js bundle:%s is empty", name);
return new JavaValue(false);
}
mDoricJSE.loadJS(packageModuleScript(name, content), "Module://" + name);
return new JavaValue(true);
} catch (Exception e) {
e.printStackTrace();
return new JavaValue(false);
}
}
});
mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_TIMER_SET, new JavaFunction() {
@Override
public JavaValue exec(JSDecoder[] args) {
try {
mTimerExtension.setTimer(
args[0].number().longValue(),
args[1].number().longValue(),
args[2].bool());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_TIMER_CLEAR, new JavaFunction() {
@Override
public JavaValue exec(JSDecoder[] args) {
try {
mTimerExtension.clearTimer(args[0].number().longValue());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
mDoricJSE.injectGlobalJSFunction(DoricConstant.INJECT_BRIDGE, new JavaFunction() {
@Override
public JavaValue exec(JSDecoder[] args) {
try {
String contextId = args[0].string();
String module = args[1].string();
String method = args[2].string();
String callbackId = args[3].string();
JSDecoder jsDecoder = args[4];
return mDoricBridgeExtension.callNative(contextId, module, method, callbackId, jsDecoder);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
}
private void initDoricRuntime() {
loadBuiltinJS(DoricConstant.DORIC_BUNDLE_SANDBOX);
String libName = DoricConstant.DORIC_MODULE_LIB;
String libJS = DoricUtils.readAssetFile(DoricConstant.DORIC_BUNDLE_LIB);
mDoricJSE.loadJS(packageModuleScript(libName, libJS), "Module://" + libName);
}
@Override
public boolean handleMessage(Message msg) {
return false;
}
public void teardown() {
mDoricJSE.teardown();
mTimerExtension.teardown();
handlerThread.quit();
mJSHandler.removeCallbacksAndMessages(null);
}
private void loadBuiltinJS(String assetName) {
String script = DoricUtils.readAssetFile(assetName);
mDoricJSE.loadJS(script, "Assets://" + assetName);
}
public void prepareContext(final String contextId, final String script, final String source) {
mDoricJSE.loadJS(packageContextScript(contextId, script), "Context://" + source);
}
public void destroyContext(final String contextId) {
mDoricJSE.loadJS(String.format(DoricConstant.TEMPLATE_CONTEXT_DESTROY, contextId), "_Context://" + contextId);
}
private String packageContextScript(String contextId, String content) {
return String.format(DoricConstant.TEMPLATE_CONTEXT_CREATE, content, contextId, contextId, contextId);
}
private String packageModuleScript(String moduleName, String content) {
return String.format(DoricConstant.TEMPLATE_MODULE, moduleName, content);
}
public JSDecoder invokeDoricMethod(final String method, final Object... args) {
ArrayList<JavaValue> values = new ArrayList<>();
for (Object arg : args) {
values.add(DoricUtils.toJavaValue(arg));
}
return mDoricJSE.invokeMethod(DoricConstant.GLOBAL_DORIC, method,
values.toArray(new JavaValue[values.size()]), false);
}
@Override
public void callback(long timerId) {
try {
invokeDoricMethod(DoricConstant.DORIC_TIMER_CALLBACK, timerId);
} catch (Exception e) {
e.printStackTrace();
DoricLog.e("Timer Callback error:%s", e.getLocalizedMessage());
}
}
public DoricRegistry getRegistry() {
return mDoricRegistry;
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.engine;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSExecutor;
import com.github.pengfeizhou.jscore.JSRuntimeException;
import com.github.pengfeizhou.jscore.JavaFunction;
import com.github.pengfeizhou.jscore.JavaValue;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricNativeJSExecutor implements IDoricJSE {
private final JSExecutor mJSExecutor;
public DoricNativeJSExecutor() {
this.mJSExecutor = JSExecutor.create();
}
@Override
public String loadJS(String script, String source) throws JSRuntimeException {
return mJSExecutor.loadJS(script, source);
}
@Override
public JSDecoder evaluateJS(String script, String source, boolean hashKey) throws JSRuntimeException {
return mJSExecutor.evaluateJS(script, source, hashKey);
}
@Override
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
mJSExecutor.injectGlobalJSFunction(name, javaFunction);
}
@Override
public void injectGlobalJSObject(String name, JavaValue javaValue) {
mJSExecutor.injectGlobalJSObject(name, javaValue);
}
@Override
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) throws JSRuntimeException {
return mJSExecutor.invokeMethod(objectName, functionName, javaValues, hashKey);
}
@Override
public void teardown() {
mJSExecutor.destroy();
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.engine;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSRuntimeException;
import com.github.pengfeizhou.jscore.JavaFunction;
import com.github.pengfeizhou.jscore.JavaValue;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public interface IDoricJSE {
/**
* 执行JS语句
*
* @param script 执行的JS语句
* @param source 该JS语句对应的文件名在输出错误的堆栈信息时有用
* @return 返回JS语句的执行结果以String形式返回
* @throws JSRuntimeException 如果执行的脚本有异常会抛出包含堆栈的JSRuntimeException
*/
String loadJS(String script, String source) throws JSRuntimeException;
/**
* 执行JS语句
*
* @param script 执行的JS语句
* @param source 该JS语句对应的文件名在输出错误的堆栈信息时有用
* @param hashKey 是否在返回对象序列化时将key hash化
* @return 返回JS语句的执行结果以二进制数据的形式返回
* @throws JSRuntimeException 如果执行的脚本有异常会抛出包含堆栈的JSRuntimeException
*/
JSDecoder evaluateJS(String script, String source, boolean hashKey) throws JSRuntimeException;
/**
* 向JS注入全局方法由java实现
*
* @param name js的方法名
* @param javaFunction java中对应的实现类
*/
void injectGlobalJSFunction(String name, JavaFunction javaFunction);
/**
* 向JS注入全局变量
*
* @param name js中的变量名
* @param javaValue 注入的全局变量按Value进行组装
*/
void injectGlobalJSObject(String name, JavaValue javaValue);
/**
* 执行JS某个方法
*
* @param objectName 执行的方法所属的变量名如果方法为全局方法该参数传null
* @param functionName 执行的方法名
* @param javaValues 方法需要的参数列表,按数组传入
* @param hashKey 是否在返回对象序列化时将key hash化
* @throws JSRuntimeException 如果执行的方法有异常会抛出包含堆栈的JSRuntimeException
*/
JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) throws JSRuntimeException;
void teardown();
}

View File

@@ -0,0 +1,101 @@
/*
* 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.extension.bridge;
import pub.doric.DoricContext;
import pub.doric.async.AsyncResult;
import pub.doric.plugin.DoricJavaPlugin;
import pub.doric.DoricContextManager;
import pub.doric.utils.DoricLog;
import pub.doric.utils.DoricMetaInfo;
import pub.doric.utils.DoricUtils;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JavaValue;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricBridgeExtension {
public DoricBridgeExtension() {
}
public JavaValue callNative(String contextId, String module, String methodName, final String callbackId, final JSDecoder jsDecoder) {
final DoricContext context = DoricContextManager.getContext(contextId);
DoricMetaInfo<DoricJavaPlugin> pluginInfo = context.getDriver().getRegistry().acquirePluginInfo(module);
if (pluginInfo == null) {
DoricLog.e("Cannot find plugin class:%s", module);
return new JavaValue(false);
}
final DoricJavaPlugin doricJavaPlugin = context.obtainPlugin(pluginInfo);
if (doricJavaPlugin == null) {
DoricLog.e("Cannot obtain plugin instance:%s,method:%", module);
return new JavaValue(false);
}
final Method method = pluginInfo.getMethod(methodName);
if (method == null) {
DoricLog.e("Cannot find plugin method in class:%s,method:%s", module, methodName);
return new JavaValue(false);
}
DoricMethod doricMethod = method.getAnnotation(DoricMethod.class);
if (doricMethod == null) {
DoricLog.e("Cannot find DoricMethod annotation in class:%s,method:%s", module, methodName);
return new JavaValue(false);
}
Callable<JavaValue> callable = new Callable<JavaValue>() {
@Override
public JavaValue call() throws Exception {
Class[] classes = method.getParameterTypes();
Object ret;
if (classes.length == 0) {
ret = method.invoke(doricJavaPlugin);
} else if (classes.length == 1) {
ret = method.invoke(doricJavaPlugin, createParam(context, classes[0], callbackId, jsDecoder));
} else {
ret = method.invoke(doricJavaPlugin,
createParam(context, classes[0], callbackId, jsDecoder),
createParam(context, classes[1], callbackId, jsDecoder));
}
return DoricUtils.toJavaValue(ret);
}
};
AsyncResult<JavaValue> asyncResult = context.getDriver().asyncCall(callable, doricMethod.thread());
if (asyncResult.hasResult()) {
return asyncResult.getResult();
}
return new JavaValue(true);
}
private Object createParam(DoricContext context, Class clz, String callbackId, JSDecoder jsDecoder) {
if (clz == DoricPromise.class) {
return new DoricPromise(context, callbackId);
} else {
try {
return DoricUtils.toJavaObject(clz, jsDecoder);
} catch (Exception e) {
DoricLog.e("createParam error:%s", e.getLocalizedMessage());
}
return null;
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.extension.bridge;
import pub.doric.utils.ThreadMode;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoricMethod {
String name() default "";
ThreadMode thread() default ThreadMode.INDEPENDENT;
}

View File

@@ -0,0 +1,34 @@
/*
* 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.extension.bridge;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoricPlugin {
String name();
}

View File

@@ -0,0 +1,56 @@
/*
* 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.extension.bridge;
import pub.doric.DoricContext;
import pub.doric.utils.DoricConstant;
import com.github.pengfeizhou.jscore.JavaValue;
/**
* @Description: com.github.penfeizhou.doric.extension.bridge
* @Author: pengfei.zhou
* @CreateDate: 2019-07-19
*/
public class DoricPromise {
private final DoricContext context;
private final String callbackId;
public DoricPromise(DoricContext context, String callbackId) {
this.context = context;
this.callbackId = callbackId;
}
public void resolve(JavaValue... javaValue) {
Object[] params = new Object[javaValue.length + 2];
params[0] = context.getContextId();
params[1] = callbackId;
System.arraycopy(javaValue, 0, params, 2, javaValue.length);
context.getDriver().invokeDoricMethod(
DoricConstant.DORIC_BRIDGE_RESOLVE,
params);
}
public void reject(JavaValue... javaValue) {
Object[] params = new Object[javaValue.length + 2];
params[0] = context.getContextId();
params[1] = callbackId;
System.arraycopy(javaValue, 0, params, 2, javaValue.length);
context.getDriver().invokeDoricMethod(
DoricConstant.DORIC_BRIDGE_REJECT,
params);
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.extension.timer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import java.util.HashSet;
import java.util.Set;
/**
* @Description: Doric
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class DoricTimerExtension implements Handler.Callback {
private static final int MSG_TIMER = 0;
private final Handler mTimerHandler;
private final TimerCallback mTimerCallback;
private Set<Long> mDeletedTimerIds = new HashSet<>();
public DoricTimerExtension(Looper looper, TimerCallback timerCallback) {
mTimerHandler = new Handler(looper, this);
mTimerCallback = timerCallback;
}
public void setTimer(long timerId, long time, boolean repeat) {
TimerInfo timerInfo = new TimerInfo();
timerInfo.timerId = timerId;
timerInfo.time = time;
timerInfo.repeat = repeat;
mTimerHandler.sendMessageDelayed(Message.obtain(mTimerHandler, MSG_TIMER, timerInfo), timerInfo.time);
}
public void clearTimer(long timerId) {
mDeletedTimerIds.add(timerId);
}
@Override
public boolean handleMessage(Message msg) {
if (msg.obj instanceof TimerInfo) {
TimerInfo timerInfo = (TimerInfo) msg.obj;
if (mDeletedTimerIds.contains(timerInfo.timerId)) {
mDeletedTimerIds.remove(timerInfo.timerId);
} else {
mTimerCallback.callback(timerInfo.timerId);
if (timerInfo.repeat) {
mTimerHandler.sendMessageDelayed(Message.obtain(mTimerHandler, MSG_TIMER, timerInfo), timerInfo.time);
} else {
mDeletedTimerIds.remove(timerInfo.timerId);
}
}
}
return true;
}
public void teardown() {
mTimerHandler.removeCallbacksAndMessages(null);
}
private class TimerInfo {
long timerId;
long time;
boolean repeat;
}
public interface TimerCallback {
void callback(long timerId);
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.loader;
import pub.doric.async.AsyncResult;
import pub.doric.utils.DoricUtils;
/**
* @Description: handle "assets://asset-file-path"
* @Author: pengfei.zhou
* @CreateDate: 2019-11-23
*/
public class DoricAssetJSLoader implements IDoricJSLoader {
@Override
public boolean filter(String scheme) {
return scheme.startsWith("assets");
}
@Override
public AsyncResult<String> request(String scheme) {
return new AsyncResult<>(DoricUtils.readAssetFile(scheme.substring("assets://".length())));
}
}

Some files were not shown because too many files have changed in this diff Show More