move files from main project

This commit is contained in:
pengfei.zhou 2019-12-04 14:11:47 +08:00
parent 39f8045dd8
commit a2d25c4cb5
161 changed files with 13197 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
/.idea/
.DS_Store
/build
/captures
.externalNativeBuild

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

33
app/build.gradle Normal file
View File

@ -0,0 +1,33 @@
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 project(':devkit')
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
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,35 @@
<?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>
<activity
android:name=".DemoActivity"
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>

1
app/src/main/assets/demo Symbolic link
View File

@ -0,0 +1 @@
../../../../../demo/bundle/src

View File

@ -0,0 +1,46 @@
/*
* 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.os.Bundle;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import pub.doric.DoricContext;
import pub.doric.DoricPanel;
import pub.doric.utils.DoricUtils;
/**
* @Description: pub.doric.demo
* @Author: pengfei.zhou
* @CreateDate: 2019-11-19
*/
public class DemoActivity extends AppCompatActivity {
private DoricContext doricContext;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String source = getIntent().getStringExtra("source");
DoricPanel doricPanel = new DoricPanel(this);
addContentView(doricPanel, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
doricPanel.config(DoricUtils.readAssetFile("demo/" + source), source);
doricContext = doricPanel.getDoricContext();
}
}

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,134 @@
/*
* 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.DoricActivity;
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<>();
ret.add("Test");
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) {
if (data[position].contains("Test")) {
Intent intent = new Intent(tv.getContext(), PullableActivity.class);
tv.getContext().startActivity(intent);
return;
}
if (data[position].contains("NavigatorDemo")) {
Intent intent = new Intent(tv.getContext(), DoricActivity.class);
intent.putExtra("scheme", "assets://demo/" + data[position]);
intent.putExtra("alias", data[position]);
tv.getContext().startActivity(intent);
return;
}
Intent intent = new Intent(tv.getContext(), DemoDebugActivity.class);
intent.putExtra("source", data[position]);
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>

30
build.gradle Normal file
View File

@ -0,0 +1,30 @@
// 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.2'
// 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' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

1
devkit/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

52
devkit/build.gradle Normal file
View File

@ -0,0 +1,52 @@
apply plugin: 'com.android.library'
def projectHome = project.rootDir.getParent() + "/demo"
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'
}
debug {
buildConfigField "String", "PROJECT_HOME", "\"${projectHome}\""
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.github.pengfeizhou:jsc4a:0.1.0'
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.0'
api 'org.nanohttpd:nanohttpd:2.3.1'
api project(':doric')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

View File

21
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,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pub.doric.devkit">
<uses-permission android:name="android.permission.CAMERA" />
<application>
<activity android:name=".ui.ScanQRCodeActivity" />
<activity android:name=".ui.DemoDebugActivity" />
</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,29 @@
package pub.doric.devkit;
import pub.doric.DoricContext;
import pub.doric.DoricNativeDriver;
public class DoricContextDebuggable {
private DoricContext doricContext;
private DoricDebugDriver doricDebugDriver;
public DoricContextDebuggable(DoricContext doricContext) {
this.doricContext = doricContext;
}
public void startDebug() {
doricDebugDriver = new DoricDebugDriver(new IStatusCallback() {
@Override
public void start() {
doricContext.setDriver(doricDebugDriver);
doricContext.reInit();
}
});
}
public void stopDebug() {
doricDebugDriver.destroy();
doricContext.setDriver(DoricNativeDriver.getInstance());
doricContext.reInit();
}
}

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,181 @@
/*
* 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.content.Context;
import android.content.res.AssetManager;
import android.net.Uri;
import android.util.Log;
import android.webkit.MimeTypeMap;
import com.github.pengfeizhou.jscore.JSONBuilder;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fi.iki.elonen.NanoHTTPD;
import pub.doric.DoricContext;
import pub.doric.DoricContextManager;
/**
* @Description: com.github.penfeizhou.doricdemo
* @Author: pengfei.zhou
* @CreateDate: 2019-08-03
*/
public class LocalServer extends NanoHTTPD {
private final Context context;
private Map<String, APICommand> commandMap = new HashMap<>();
public LocalServer(Context context, int port) {
super(port);
this.context = context;
commandMap.put("allContexts", new APICommand() {
@Override
public String name() {
return "allContexts";
}
@Override
public Object exec(IHTTPSession session) {
Collection<DoricContext> ret = DoricContextManager.aliveContexts();
JSONArray jsonArray = new JSONArray();
for (DoricContext doricContext : ret) {
JSONBuilder jsonBuilder = new JSONBuilder();
jsonBuilder.put("source", doricContext.getSource());
jsonBuilder.put("id", doricContext.getContextId());
jsonArray.put(jsonBuilder.toJSONObject());
}
return jsonArray;
}
});
commandMap.put("context", new APICommand() {
@Override
public String name() {
return "context";
}
@Override
public Object exec(IHTTPSession session) {
String id = session.getParms().get("id");
DoricContext doricContext = DoricContextManager.getContext(id);
if (doricContext != null) {
return new JSONBuilder()
.put("id", doricContext.getContextId())
.put("source", doricContext.getSource())
.put("script", doricContext.getScript())
.toJSONObject();
}
return "{}";
}
});
commandMap.put("reload", new APICommand() {
@Override
public String name() {
return "reload";
}
@Override
public Object exec(IHTTPSession session) {
Map<String, String> files = new HashMap<>();
try {
session.parseBody(files);
} catch (Exception e) {
e.printStackTrace();
}
String id = session.getParms().get("id");
DoricContext doricContext = DoricContextManager.getContext(id);
if (doricContext != null) {
try {
JSONObject jsonObject = new JSONObject(files.get("postData"));
doricContext.reload(jsonObject.optString("script"));
} catch (Exception e) {
e.printStackTrace();
}
return "success";
}
return "fail";
}
});
}
private static String getIpAddressString() {
try {
for (Enumeration<NetworkInterface> enNetI = NetworkInterface
.getNetworkInterfaces(); enNetI.hasMoreElements(); ) {
NetworkInterface netI = enNetI.nextElement();
for (Enumeration<InetAddress> enumIpAddr = netI
.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return "0.0.0.0";
}
@Override
public void start() throws IOException {
super.start();
Log.d("Debugger", String.format("Open http://%s:8910/index.html to start debug", getIpAddressString()));
}
@Override
public Response serve(IHTTPSession session) {
Uri uri = Uri.parse(session.getUri());
List<String> segments = uri.getPathSegments();
if (segments.size() > 1 && "api".equals(segments.get(0))) {
String cmd = segments.get(1);
APICommand apiCommand = commandMap.get(cmd);
if (apiCommand != null) {
Object ret = apiCommand.exec(session);
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, "application/json", ret.toString());
}
}
String fileName = session.getUri().substring(1);
AssetManager assetManager = context.getAssets();
try {
InputStream inputStream = assetManager.open("debugger/" + fileName);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileName.substring(fileName.lastIndexOf(".") + 1));
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, mimeType, inputStream, inputStream.available());
} catch (IOException e) {
e.printStackTrace();
}
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, "text/html", "HelloWorld");
}
public interface APICommand {
String name();
Object exec(IHTTPSession session);
}
}

View File

@ -0,0 +1,119 @@
/*
* 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": {
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,4 @@
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,4 @@
package pub.doric.devkit.event;
public class QuitDebugEvent {
}

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,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.QuitDebugEvent;
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 QuitDebugEvent());
}
}
@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,84 @@
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 pub.doric.DoricContext;
import pub.doric.DoricContextManager;
import pub.doric.devkit.BuildConfig;
import pub.doric.devkit.DoricDev;
import pub.doric.devkit.IDevKit;
import pub.doric.devkit.R;
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) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("contextId", doricContext.getContextId());
jsonObject.addProperty("projectHome", BuildConfig.PROJECT_HOME);
jsonObject.addProperty("source", doricContext.getSource().replace(".js", ".ts"));
DoricDev.sendDevCommand(IDevKit.Command.DEBUG, jsonObject);
dismissAllowingStateLoss();
}
});
container.addView(cell);
}
}
}

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.devkit.ui;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import pub.doric.DoricContext;
import pub.doric.DoricContextManager;
import pub.doric.DoricPanel;
import pub.doric.devkit.DoricContextDebuggable;
import pub.doric.devkit.event.EnterDebugEvent;
import pub.doric.devkit.event.QuitDebugEvent;
import pub.doric.devkit.event.ReloadEvent;
import pub.doric.devkit.util.SensorManagerHelper;
import pub.doric.utils.DoricUtils;
/**
* @Description: pub.doric.demo
* @Author: pengfei.zhou
* @CreateDate: 2019-11-19
*/
public class DemoDebugActivity extends AppCompatActivity {
private DoricContext doricContext;
private SensorManagerHelper sensorHelper;
private DoricContextDebuggable doricContextDebuggable;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String source = getIntent().getStringExtra("source");
DoricPanel doricPanel = new DoricPanel(this);
addContentView(doricPanel, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
doricPanel.config(DoricUtils.readAssetFile("demo/" + source), source);
doricContext = doricPanel.getDoricContext();
doricContextDebuggable = new DoricContextDebuggable(doricContext);
sensorHelper = new SensorManagerHelper(this);
sensorHelper.setOnShakeListener(new SensorManagerHelper.OnShakeListener() {
@Override
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
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
sensorHelper.stop();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEnterDebugEvent(EnterDebugEvent enterDebugEvent) {
doricContextDebuggable.startDebug();
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onReloadEvent(ReloadEvent reloadEvent) {
for (DoricContext context : DoricContextManager.aliveContexts()) {
if (reloadEvent.source.contains(context.getSource())) {
context.reload(reloadEvent.script);
}
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onQuitDebugEvent(QuitDebugEvent quitDebugEvent) {
doricContextDebuggable.stopDebug();
}
@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/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

59
doric/build.gradle Normal file
View File

@ -0,0 +1,59 @@
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()
buildDemo.exec()
//buildDebugger.exec()
}
task buildJSBundle(type: Exec) {
workingDir project.rootDir.getParent() + "/js-framework"
commandLine 'npm', 'run', 'build'
}
task buildDemo(type: Exec) {
workingDir project.rootDir.getParent() + "/demo"
commandLine 'npm', 'run', 'build'
}
task buildDebugger(type: Exec) {
workingDir project.rootDir.getParent() + "/debugger"
commandLine 'npm', 'run', 'build'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
api 'com.github.pengfeizhou:jsc4a:0.1.0'
implementation 'com.squareup.okhttp3:okhttp:4.2.2'
implementation 'com.github.penfeizhou.android.animation:glide-plugin:1.3.1'
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'
}

31
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,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pub.doric">
<uses-permission android:name="android.permission.CAMERA" />
<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-framework/bundle

View File

@ -0,0 +1 @@
../../../../../debugger/dist

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,53 @@
/*
* 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 {
private DoricFragment doricFragment;
@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.newInstance(scheme, alias);
getSupportFragmentManager().beginTransaction()
.add(R.id.container, doricFragment)
.commit();
}
}
@Override
public void onBackPressed() {
if (doricFragment.canPop()) {
doricFragment.pop();
} else {
super.onBackPressed();
}
}
}

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,209 @@
/*
* 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.Animator;
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 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) {
this.mContext = context;
this.mContextId = contextId;
this.source = source;
}
public String getSource() {
return source;
}
public String getScript() {
return script;
}
public static DoricContext create(Context context, String script, String source) {
DoricContext doricContext = DoricContextManager.getInstance().createContext(context, script, source);
doricContext.script = script;
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);
callEntity(DoricConstant.DORIC_ENTITY_CREATE);
}
public void reInit() {
callEntity(DoricConstant.DORIC_ENTITY_INIT, this.initParams);
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);
}
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) {
final String contextId = String.valueOf(counter.incrementAndGet());
final DoricContext doricContext = new DoricContext(context, contextId, source);
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,84 @@
/*
* 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 pub.doric.navigator.IDoricNavigator;
/**
* @Description: pub.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-11-23
*/
public class DoricFragment extends Fragment implements IDoricNavigator {
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;
}
@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);
Bundle argument = getArguments();
if (argument != null) {
String alias = argument.getString("alias");
String scheme = argument.getString("scheme");
push(scheme, alias);
}
}
@Override
public void push(String scheme, String alias) {
getChildFragmentManager().beginTransaction()
.add(R.id.root, DoricPanelFragment.newInstance(scheme, alias))
.addToBackStack(scheme)
.commit();
}
@Override
public void pop() {
if (canPop()) {
getChildFragmentManager().popBackStack();
} else {
if (getActivity() != null) {
getActivity().finish();
}
}
}
public boolean canPop() {
return getChildFragmentManager().getBackStackEntryCount() > 1;
}
}

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) {
DoricContext doricContext = DoricContext.create(getContext(), script, alias);
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,92 @@
/*
* 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 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 {
private DoricPanel doricPanel;
private BaseDoricNavBar navBar;
public static DoricPanelFragment newInstance(String scheme, String alias) {
Bundle args = new Bundle();
args.putString("scheme", scheme);
args.putString("alias", alias);
DoricPanelFragment fragment = new DoricPanelFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
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);
navBar = view.findViewById(R.id.doric_nav_bar);
Bundle argument = getArguments();
if (argument == null) {
DoricLog.e("DoricPanelFragment argument is null");
return;
}
final String alias = argument.getString("alias");
String scheme = argument.getString("scheme");
DoricJSLoaderManager.getInstance().loadJSBundle(scheme).setCallback(new AsyncResult.Callback<String>() {
@Override
public void onResult(String result) {
doricPanel.config(result, alias);
DoricContext context = doricPanel.getDoricContext();
Fragment fragment = getParentFragment();
if (fragment instanceof IDoricNavigator) {
context.setDoricNavigator((IDoricNavigator) fragment);
}
context.setDoricNavBar(navBar);
}
@Override
public void onError(Throwable t) {
DoricLog.e("DoricPanelFragment load JS error:" + t.getLocalizedMessage());
}
@Override
public void onFinish() {
}
});
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.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.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);
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,228 @@
/*
* 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.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.JavaFunction;
import com.github.pengfeizhou.jscore.JavaValue;
import java.util.ArrayList;
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() {
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())));
}
}

View File

@ -0,0 +1,64 @@
/*
* 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 com.bumptech.glide.RequestBuilder;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import pub.doric.async.AsyncResult;
/**
* @Description: handle like "https://xxxx.js"
* @Author: pengfei.zhou
* @CreateDate: 2019-11-23
*/
public class DoricHttpJSLoader implements IDoricJSLoader {
private OkHttpClient okHttpClient = new OkHttpClient();
@Override
public boolean filter(String scheme) {
return scheme.startsWith("http");
}
@Override
public AsyncResult<String> request(String scheme) {
final AsyncResult<String> ret = new AsyncResult<>();
okHttpClient.newCall(new Request.Builder().url(scheme).build()).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
ret.setError(e);
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) {
try {
ret.setResult(response.body().string());
} catch (Exception e) {
ret.setError(e);
}
}
});
return ret;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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 java.util.Collection;
import pub.doric.DoricRegistry;
import pub.doric.async.AsyncResult;
/**
* @Description: pub.doric
* @Author: pengfei.zhou
* @CreateDate: 2019-11-23
*/
public class DoricJSLoaderManager {
private DoricJSLoaderManager() {
}
private static class Inner {
private static final DoricJSLoaderManager sInstance = new DoricJSLoaderManager();
}
public static DoricJSLoaderManager getInstance() {
return Inner.sInstance;
}
public AsyncResult<String> loadJSBundle(String scheme) {
Collection<IDoricJSLoader> jsLoaders = DoricRegistry.getJSLoaders();
for (IDoricJSLoader jsLoader : jsLoaders) {
if (jsLoader.filter(scheme)) {
return jsLoader.request(scheme);
}
}
return new AsyncResult<>("");
}
}

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