rename dirs
This commit is contained in:
18
doric-android/doric/src/main/AndroidManifest.xml
Normal file
18
doric-android/doric/src/main/AndroidManifest.xml
Normal 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>
|
1
doric-android/doric/src/main/assets/bundle
Symbolic link
1
doric-android/doric/src/main/assets/bundle
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../js-framework/bundle
|
1
doric-android/doric/src/main/assets/debugger
Symbolic link
1
doric-android/doric/src/main/assets/debugger
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../../../debugger/dist
|
35
doric-android/doric/src/main/java/pub/doric/Doric.java
Normal file
35
doric-android/doric/src/main/java/pub/doric/Doric.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 "";
|
||||
}
|
209
doric-android/doric/src/main/java/pub/doric/DoricContext.java
Normal file
209
doric-android/doric/src/main/java/pub/doric/DoricContext.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
113
doric-android/doric/src/main/java/pub/doric/DoricPanel.java
Normal file
113
doric-android/doric/src/main/java/pub/doric/DoricPanel.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
149
doric-android/doric/src/main/java/pub/doric/DoricRegistry.java
Normal file
149
doric-android/doric/src/main/java/pub/doric/DoricRegistry.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
@@ -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();
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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())));
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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<>("");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-23
|
||||
*/
|
||||
public interface IDoricJSLoader {
|
||||
boolean filter(String scheme);
|
||||
|
||||
AsyncResult<String> request(String scheme);
|
||||
}
|
@@ -0,0 +1,105 @@
|
||||
package pub.doric.navbar;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import pub.doric.R;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.navbar
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-25
|
||||
*/
|
||||
public class BaseDoricNavBar extends FrameLayout implements IDoricNavBar {
|
||||
private ViewGroup mTitleContainer;
|
||||
private ViewGroup mRightContainer;
|
||||
private ViewGroup mLeftContainer;
|
||||
private TextView mTvTitle;
|
||||
|
||||
public BaseDoricNavBar(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BaseDoricNavBar(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BaseDoricNavBar(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setup();
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
LayoutInflater.from(getContext()).inflate(R.layout.doric_navigator, this);
|
||||
mTitleContainer = findViewById(R.id.container_title);
|
||||
mLeftContainer = findViewById(R.id.container_left);
|
||||
mRightContainer = findViewById(R.id.container_right);
|
||||
mTvTitle = findViewById(R.id.tv_title);
|
||||
findViewById(R.id.tv_back).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (getContext() instanceof Activity) {
|
||||
((Activity) getContext()).onBackPressed();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden() {
|
||||
return getVisibility() != VISIBLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHidden(boolean b) {
|
||||
setVisibility(b ? GONE : VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
mTvTitle.setText(title);
|
||||
}
|
||||
|
||||
private void updateTitleMargins() {
|
||||
try {
|
||||
int width = mRightContainer.getRight() - mLeftContainer.getLeft();
|
||||
int leftWidth = mLeftContainer.getWidth();
|
||||
int rightWidth = mRightContainer.getWidth();
|
||||
int margin = Math.max(leftWidth, rightWidth);
|
||||
if (leftWidth + rightWidth > width) {
|
||||
mTitleContainer.setVisibility(GONE);
|
||||
} else {
|
||||
mTitleContainer.setVisibility(VISIBLE);
|
||||
StaticLayout staticLayout = new StaticLayout(mTvTitle.getText(),
|
||||
mTvTitle.getPaint(), Integer.MAX_VALUE, Layout.Alignment.ALIGN_NORMAL,
|
||||
1.0f, 0.0f, false);
|
||||
float textWidth = (staticLayout.getLineCount() > 0 ? staticLayout.getLineWidth(0) : 0.0f);
|
||||
if (width - 2 * margin >= textWidth) {
|
||||
mTitleContainer.setPadding(margin, 0, margin, 0);
|
||||
} else {
|
||||
mTitleContainer.setPadding(leftWidth, 0, rightWidth, 0);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
updateTitleMargins();
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.navbar;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.navbar
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-25
|
||||
*/
|
||||
public interface IDoricNavBar {
|
||||
boolean isHidden();
|
||||
|
||||
void setHidden(boolean hidden);
|
||||
|
||||
void setTitle(String title);
|
||||
|
||||
void setBackgroundColor(int color);
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.navigator;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.navigator
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-23
|
||||
*/
|
||||
public interface IDoricNavigator {
|
||||
void push(String scheme, String alias);
|
||||
|
||||
void pop();
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
package pub.doric.plugin;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.shader.RootNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
import pub.doric.utils.DoricLog;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-29
|
||||
*/
|
||||
@DoricPlugin(name = "animate")
|
||||
public class AnimatePlugin extends DoricJavaPlugin {
|
||||
public AnimatePlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void submit(DoricPromise promise) {
|
||||
promise.resolve();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void animateRender(final JSObject jsObject, final DoricPromise promise) {
|
||||
getDoricContext().getDriver().asyncCall(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
final long duration = jsObject.getProperty("duration").asNumber().toLong();
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
getDoricContext().setAnimatorSet(animatorSet);
|
||||
String viewId = jsObject.getProperty("id").asString().value();
|
||||
RootNode rootNode = getDoricContext().getRootNode();
|
||||
if (TextUtils.isEmpty(rootNode.getId())) {
|
||||
rootNode.setId(viewId);
|
||||
rootNode.blend(jsObject.getProperty("props").asObject());
|
||||
} else {
|
||||
ViewNode viewNode = getDoricContext().targetViewNode(viewId);
|
||||
if (viewNode != null) {
|
||||
viewNode.blend(jsObject.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
getDoricContext().setAnimatorSet(null);
|
||||
animatorSet.setDuration(duration);
|
||||
animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
promise.resolve();
|
||||
}
|
||||
});
|
||||
animatorSet.start();
|
||||
return null;
|
||||
}
|
||||
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
|
||||
@Override
|
||||
public void onResult(Object result) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
t.printStackTrace();
|
||||
DoricLog.e("Shader.render:error%s", t.getLocalizedMessage());
|
||||
promise.reject(new JavaValue(t.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -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.plugin;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.utils.DoricContextHolder;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
public abstract class DoricJavaPlugin extends DoricContextHolder {
|
||||
public DoricJavaPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
}
|
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.R;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
@DoricPlugin(name = "modal")
|
||||
public class ModalPlugin extends DoricJavaPlugin {
|
||||
|
||||
public ModalPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void toast(JSObject jsObject) {
|
||||
try {
|
||||
String msg = jsObject.getProperty("msg").asString().value();
|
||||
JSValue gravityVal = jsObject.getProperty("gravity");
|
||||
int gravity = Gravity.BOTTOM;
|
||||
if (gravityVal.isNumber()) {
|
||||
gravity = gravityVal.asNumber().toInt();
|
||||
}
|
||||
Toast toast = Toast.makeText(getDoricContext().getContext(),
|
||||
jsObject.getProperty("msg").asString().value(),
|
||||
Toast.LENGTH_SHORT);
|
||||
if ((gravity & Gravity.TOP) == Gravity.TOP) {
|
||||
toast.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, DoricUtils.dp2px(50));
|
||||
} else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
|
||||
toast.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, DoricUtils.dp2px(50));
|
||||
} else {
|
||||
toast.setGravity(Gravity.CENTER | Gravity.CENTER_HORIZONTAL, 0, 0);
|
||||
|
||||
}
|
||||
toast.show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void alert(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue titleVal = jsObject.getProperty("title");
|
||||
JSValue msgVal = jsObject.getProperty("msg");
|
||||
JSValue okBtn = jsObject.getProperty("okLabel");
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getDoricContext().getContext(), R.style.Theme_Doric_Modal_Alert);
|
||||
if (titleVal.isString()) {
|
||||
builder.setTitle(titleVal.asString().value());
|
||||
}
|
||||
String btnTitle = getDoricContext().getContext().getString(android.R.string.ok);
|
||||
if (okBtn.isString()) {
|
||||
btnTitle = okBtn.asString().value();
|
||||
}
|
||||
builder.setMessage(msgVal.asString().value())
|
||||
.setPositiveButton(btnTitle, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
promise.resolve();
|
||||
}
|
||||
});
|
||||
builder.setCancelable(false);
|
||||
builder.show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void confirm(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue titleVal = jsObject.getProperty("title");
|
||||
JSValue msgVal = jsObject.getProperty("msg");
|
||||
JSValue okBtn = jsObject.getProperty("okLabel");
|
||||
JSValue cancelBtn = jsObject.getProperty("cancelLabel");
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getDoricContext().getContext(), R.style.Theme_Doric_Modal_Confirm);
|
||||
if (titleVal.isString()) {
|
||||
builder.setTitle(titleVal.asString().value());
|
||||
}
|
||||
String okLabel = getDoricContext().getContext().getString(android.R.string.ok);
|
||||
if (okBtn.isString()) {
|
||||
okLabel = okBtn.asString().value();
|
||||
}
|
||||
String cancelLabel = getDoricContext().getContext().getString(android.R.string.cancel);
|
||||
if (cancelBtn.isString()) {
|
||||
cancelLabel = cancelBtn.asString().value();
|
||||
}
|
||||
builder.setMessage(msgVal.asString().value())
|
||||
.setPositiveButton(okLabel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
promise.resolve();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(cancelLabel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
promise.reject();
|
||||
}
|
||||
});
|
||||
builder.setCancelable(false);
|
||||
builder.show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void prompt(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue titleVal = jsObject.getProperty("title");
|
||||
JSValue msgVal = jsObject.getProperty("msg");
|
||||
JSValue okBtn = jsObject.getProperty("okLabel");
|
||||
JSValue cancelBtn = jsObject.getProperty("cancelLabel");
|
||||
JSValue defaultVal = jsObject.getProperty("defaultText");
|
||||
JSValue text = jsObject.getProperty("text");
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getDoricContext().getContext(), R.style.Theme_Doric_Modal_Prompt);
|
||||
if (titleVal.isString()) {
|
||||
builder.setTitle(titleVal.asString().value());
|
||||
}
|
||||
String okLabel = getDoricContext().getContext().getString(android.R.string.ok);
|
||||
if (okBtn.isString()) {
|
||||
okLabel = okBtn.asString().value();
|
||||
}
|
||||
String cancelLabel = getDoricContext().getContext().getString(android.R.string.cancel);
|
||||
if (cancelBtn.isString()) {
|
||||
cancelLabel = cancelBtn.asString().value();
|
||||
}
|
||||
|
||||
|
||||
View v = LayoutInflater.from(getDoricContext().getContext()).inflate(R.layout.doric_modal_prompt, null);
|
||||
TextView tvMsg = v.findViewById(R.id.tv_msg);
|
||||
if (msgVal.isString()) {
|
||||
tvMsg.setText(msgVal.asString().value());
|
||||
}
|
||||
final EditText editText = v.findViewById(R.id.edit_input);
|
||||
if (defaultVal.isString()) {
|
||||
editText.setHint(defaultVal.asString().value());
|
||||
}
|
||||
if (text.isString()) {
|
||||
editText.setText(text.asString().value());
|
||||
editText.setSelection(text.asString().value().length());
|
||||
}
|
||||
builder.setView(v);
|
||||
builder
|
||||
.setPositiveButton(okLabel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
promise.resolve(new JavaValue(editText.getText().toString()));
|
||||
}
|
||||
})
|
||||
.setNegativeButton(cancelLabel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
promise.reject(new JavaValue(editText.getText().toString()));
|
||||
}
|
||||
});
|
||||
builder.setCancelable(false);
|
||||
builder.show();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.github.pengfeizhou.jscore.ArchiveException;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
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.navbar.IDoricNavBar;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-25
|
||||
*/
|
||||
@DoricPlugin(name = "navbar")
|
||||
public class NavBarPlugin extends DoricJavaPlugin {
|
||||
public NavBarPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void isHidden(DoricPromise promise) {
|
||||
IDoricNavBar navBar = getDoricContext().getDoricNavBar();
|
||||
if (navBar == null) {
|
||||
promise.reject(new JavaValue("Not implement NavBar"));
|
||||
} else {
|
||||
promise.resolve(new JavaValue(navBar.isHidden()));
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void setHidden(JSDecoder jsDecoder, DoricPromise promise) {
|
||||
IDoricNavBar navBar = getDoricContext().getDoricNavBar();
|
||||
if (navBar == null) {
|
||||
promise.reject(new JavaValue("Not implement NavBar"));
|
||||
} else {
|
||||
try {
|
||||
JSObject jsObject = jsDecoder.decode().asObject();
|
||||
boolean hidden = jsObject.getProperty("hidden").asBoolean().value();
|
||||
navBar.setHidden(hidden);
|
||||
View v = getDoricContext().getRootNode().getNodeView();
|
||||
ViewGroup.LayoutParams params = v.getLayoutParams();
|
||||
if (params instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) params).topMargin =
|
||||
hidden ? 0
|
||||
: ((View) navBar).getMeasuredHeight();
|
||||
}
|
||||
promise.resolve();
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void setTitle(JSDecoder jsDecoder, DoricPromise promise) {
|
||||
IDoricNavBar navBar = getDoricContext().getDoricNavBar();
|
||||
if (navBar == null) {
|
||||
promise.reject(new JavaValue("Not implement NavBar"));
|
||||
} else {
|
||||
try {
|
||||
JSObject jsObject = jsDecoder.decode().asObject();
|
||||
String title = jsObject.getProperty("title").asString().value();
|
||||
navBar.setTitle(title);
|
||||
promise.resolve();
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void setBgColor(JSDecoder jsDecoder, DoricPromise promise) {
|
||||
IDoricNavBar navBar = getDoricContext().getDoricNavBar();
|
||||
if (navBar == null) {
|
||||
promise.reject(new JavaValue("Not implement NavBar"));
|
||||
} else {
|
||||
try {
|
||||
JSObject jsObject = jsDecoder.decode().asObject();
|
||||
int color = jsObject.getProperty("color").asNumber().toInt();
|
||||
navBar.setBackgroundColor(color);
|
||||
promise.resolve();
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import com.github.pengfeizhou.jscore.ArchiveException;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.navigator.IDoricNavigator;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-23
|
||||
*/
|
||||
@DoricPlugin(name = "navigator")
|
||||
public class NavigatorPlugin extends DoricJavaPlugin {
|
||||
public NavigatorPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void push(JSDecoder jsDecoder) {
|
||||
IDoricNavigator navigator = getDoricContext().getDoricNavigator();
|
||||
if (navigator != null) {
|
||||
try {
|
||||
JSObject jsObject = jsDecoder.decode().asObject();
|
||||
navigator.push(jsObject.getProperty("scheme").asString().value(),
|
||||
jsObject.getProperty("alias").asString().value()
|
||||
);
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod(thread = ThreadMode.UI)
|
||||
public void pop() {
|
||||
IDoricNavigator navigator = getDoricContext().getDoricNavigator();
|
||||
if (navigator != null) {
|
||||
navigator.pop();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.internal.http.HttpMethod;
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-21
|
||||
*/
|
||||
@DoricPlugin(name = "network")
|
||||
public class NetworkPlugin extends DoricJavaPlugin {
|
||||
private OkHttpClient okHttpClient = new OkHttpClient();
|
||||
|
||||
public NetworkPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void request(JSObject requestVal, final DoricPromise promise) {
|
||||
try {
|
||||
String url = requestVal.getProperty("url").asString().value();
|
||||
String method = requestVal.getProperty("method").asString().value();
|
||||
JSValue headerVal = requestVal.getProperty("headers");
|
||||
JSValue dataVal = requestVal.getProperty("data");
|
||||
JSValue timeoutVal = requestVal.getProperty("timeout");
|
||||
|
||||
Headers.Builder headersBuilder = new Headers.Builder();
|
||||
if (headerVal.isObject()) {
|
||||
JSObject headerObject = headerVal.asObject();
|
||||
Set<String> headerKeys = headerObject.propertySet();
|
||||
for (String key : headerKeys) {
|
||||
headersBuilder.add(key, headerObject.getProperty(key).asString().value());
|
||||
}
|
||||
}
|
||||
Headers headers = headersBuilder.build();
|
||||
String contentType = headers.get("Content-Type");
|
||||
MediaType mediaType = MediaType.parse(TextUtils.isEmpty(contentType) ? "application/json; charset=utf-8" : contentType);
|
||||
RequestBody requestBody = HttpMethod.permitsRequestBody(method) ? RequestBody.create(mediaType, dataVal.isString() ? dataVal.asString().value() : "") : null;
|
||||
Request.Builder requestBuilder = new Request.Builder();
|
||||
requestBuilder.url(url)
|
||||
.headers(headers)
|
||||
.method(method, requestBody);
|
||||
if (timeoutVal.isNumber() && okHttpClient.connectTimeoutMillis() != timeoutVal.asNumber().toLong()) {
|
||||
okHttpClient = okHttpClient.newBuilder().connectTimeout(timeoutVal.asNumber().toLong(), TimeUnit.MILLISECONDS).build();
|
||||
}
|
||||
okHttpClient.newCall(requestBuilder.build()).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NotNull Call call, @NotNull IOException e) {
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
|
||||
JSONBuilder header = new JSONBuilder();
|
||||
for (String key : response.headers().names()) {
|
||||
header.put(key, response.headers().get(key));
|
||||
}
|
||||
JSONObject jsonObject = new JSONBuilder()
|
||||
.put("status", response.code())
|
||||
.put("headers", header.toJSONObject())
|
||||
.put("data", response.body() == null ? "" : response.body().string())
|
||||
.toJSONObject();
|
||||
promise.resolve(new JavaValue(jsonObject));
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
package pub.doric.plugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.async.AsyncCall;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.shader.ViewNode;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-29
|
||||
*/
|
||||
@DoricPlugin(name = "popover")
|
||||
public class PopoverPlugin extends DoricJavaPlugin {
|
||||
private FrameLayout mFullScreenView;
|
||||
|
||||
public PopoverPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void show(JSDecoder decoder, final DoricPromise promise) {
|
||||
try {
|
||||
final JSObject jsObject = decoder.decode().asObject();
|
||||
getDoricContext().getDriver().asyncCall(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
if (mFullScreenView == null) {
|
||||
mFullScreenView = new FrameLayout(getDoricContext().getContext());
|
||||
ViewGroup decorView = (ViewGroup) getDoricContext().getRootNode().getNodeView().getRootView();
|
||||
decorView.addView(mFullScreenView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
mFullScreenView.setVisibility(View.VISIBLE);
|
||||
mFullScreenView.bringToFront();
|
||||
String viewId = jsObject.getProperty("id").asString().value();
|
||||
String type = jsObject.getProperty("type").asString().value();
|
||||
ViewNode node = ViewNode.create(getDoricContext(), type);
|
||||
node.setId(viewId);
|
||||
node.init(new FrameLayout.LayoutParams(0, 0));
|
||||
node.blend(jsObject.getProperty("props").asObject());
|
||||
mFullScreenView.addView(node.getNodeView());
|
||||
getDoricContext().addHeadNode(node);
|
||||
return null;
|
||||
}
|
||||
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
|
||||
@Override
|
||||
public void onResult(Object result) {
|
||||
promise.resolve();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
t.printStackTrace();
|
||||
promise.reject(new JavaValue(t.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void dismiss(final JSValue value, final DoricPromise promise) {
|
||||
try {
|
||||
getDoricContext().getDriver().asyncCall(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
if (value.isObject()) {
|
||||
String viewId = value.asObject().getProperty("id").asString().value();
|
||||
ViewNode node = getDoricContext().targetViewNode(viewId);
|
||||
dismissViewNode(node);
|
||||
} else {
|
||||
dismissPopover();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
|
||||
@Override
|
||||
public void onResult(Object result) {
|
||||
promise.resolve();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
t.printStackTrace();
|
||||
promise.reject(new JavaValue(t.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private void dismissViewNode(ViewNode node) {
|
||||
getDoricContext().removeHeadNode(node);
|
||||
mFullScreenView.removeView(node.getNodeView());
|
||||
if (getDoricContext().allHeadNodes().isEmpty()) {
|
||||
mFullScreenView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void dismissPopover() {
|
||||
for (ViewNode node : getDoricContext().allHeadNodes()) {
|
||||
dismissViewNode(node);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.shader.SuperNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
import pub.doric.utils.DoricLog;
|
||||
import pub.doric.utils.DoricMetaInfo;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
import pub.doric.shader.RootNode;
|
||||
|
||||
import com.github.pengfeizhou.jscore.ArchiveException;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-22
|
||||
*/
|
||||
@DoricPlugin(name = "shader")
|
||||
public class ShaderPlugin extends DoricJavaPlugin {
|
||||
public ShaderPlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void render(JSDecoder jsDecoder) {
|
||||
try {
|
||||
final JSObject jsObject = jsDecoder.decode().asObject();
|
||||
getDoricContext().getDriver().asyncCall(new Callable<Object>() {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
String viewId = jsObject.getProperty("id").asString().value();
|
||||
RootNode rootNode = getDoricContext().getRootNode();
|
||||
if (TextUtils.isEmpty(rootNode.getId())) {
|
||||
rootNode.setId(viewId);
|
||||
rootNode.blend(jsObject.getProperty("props").asObject());
|
||||
} else {
|
||||
ViewNode viewNode = getDoricContext().targetViewNode(viewId);
|
||||
if (viewNode != null) {
|
||||
viewNode.blend(jsObject.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, ThreadMode.UI).setCallback(new AsyncResult.Callback<Object>() {
|
||||
@Override
|
||||
public void onResult(Object result) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
t.printStackTrace();
|
||||
DoricLog.e("Shader.render:error%s", t.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
DoricLog.e("Shader.render:error%s", e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public JavaValue command(JSDecoder jsDecoder, final DoricPromise doricPromise) {
|
||||
try {
|
||||
final JSObject jsObject = jsDecoder.decode().asObject();
|
||||
final JSValue[] viewIds = jsObject.getProperty("viewIds").asArray().toArray();
|
||||
final String name = jsObject.getProperty("name").asString().value();
|
||||
final JSValue args = jsObject.getProperty("args");
|
||||
ViewNode viewNode = null;
|
||||
for (JSValue value : viewIds) {
|
||||
if (viewNode == null) {
|
||||
viewNode = getDoricContext().targetViewNode(value.asString().value());
|
||||
} else {
|
||||
if (value.isString() && viewNode instanceof SuperNode) {
|
||||
String viewId = value.asString().value();
|
||||
viewNode = ((SuperNode) viewNode).getSubNodeById(viewId);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (viewNode == null) {
|
||||
doricPromise.reject(new JavaValue("Cannot find opposite view"));
|
||||
} else {
|
||||
final ViewNode targetViewNode = viewNode;
|
||||
DoricMetaInfo<ViewNode> pluginInfo = getDoricContext().getDriver().getRegistry()
|
||||
.acquireViewNodeInfo(viewNode.getType());
|
||||
final Method method = pluginInfo.getMethod(name);
|
||||
if (method == null) {
|
||||
String errMsg = String.format(
|
||||
"Cannot find plugin method in class:%s,method:%s",
|
||||
viewNode.getClass(),
|
||||
name);
|
||||
doricPromise.reject(new JavaValue(errMsg));
|
||||
} else {
|
||||
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(targetViewNode);
|
||||
} else if (classes.length == 1) {
|
||||
ret = method.invoke(targetViewNode,
|
||||
createParam(classes[0], doricPromise, args));
|
||||
} else {
|
||||
ret = method.invoke(targetViewNode,
|
||||
createParam(classes[0], doricPromise, args),
|
||||
createParam(classes[1], doricPromise, args));
|
||||
}
|
||||
return DoricUtils.toJavaValue(ret);
|
||||
}
|
||||
};
|
||||
AsyncResult<JavaValue> asyncResult = getDoricContext().getDriver()
|
||||
.asyncCall(callable, ThreadMode.UI);
|
||||
if (!method.getReturnType().equals(Void.TYPE)) {
|
||||
asyncResult.setCallback(new AsyncResult.Callback<JavaValue>() {
|
||||
@Override
|
||||
public void onResult(JavaValue result) {
|
||||
doricPromise.resolve(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t) {
|
||||
doricPromise.resolve(new JavaValue(t.getLocalizedMessage()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new JavaValue(true);
|
||||
}
|
||||
|
||||
|
||||
private Object createParam(Class clz, DoricPromise doricPromise, JSValue jsValue) {
|
||||
if (clz == DoricPromise.class) {
|
||||
return doricPromise;
|
||||
} else {
|
||||
try {
|
||||
return DoricUtils.toJavaObject(clz, jsValue);
|
||||
} catch (Exception e) {
|
||||
return jsValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.plugin;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.plugin
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-22
|
||||
*/
|
||||
@DoricPlugin(name = "storage")
|
||||
public class StoragePlugin extends DoricJavaPlugin {
|
||||
private static final String PREF_NAME = "pref_doric";
|
||||
|
||||
public StoragePlugin(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setItem(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue zone = jsObject.getProperty("zone");
|
||||
String key = jsObject.getProperty("key").asString().value();
|
||||
String value = jsObject.getProperty("value").asString().value();
|
||||
String prefName = zone.isString() ? PREF_NAME + "_" + zone.asString() : PREF_NAME;
|
||||
getDoricContext().getContext().getSharedPreferences(
|
||||
prefName,
|
||||
Context.MODE_PRIVATE).edit().putString(key, value).apply();
|
||||
promise.resolve();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void getItem(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue zone = jsObject.getProperty("zone");
|
||||
String key = jsObject.getProperty("key").asString().value();
|
||||
String prefName = zone.isString() ? PREF_NAME + "_" + zone.asString() : PREF_NAME;
|
||||
String ret = getDoricContext().getContext().getSharedPreferences(
|
||||
prefName,
|
||||
Context.MODE_PRIVATE).getString(key, "");
|
||||
promise.resolve(new JavaValue(ret));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void remove(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue zone = jsObject.getProperty("zone");
|
||||
String key = jsObject.getProperty("key").asString().value();
|
||||
String prefName = zone.isString() ? PREF_NAME + "_" + zone.asString() : PREF_NAME;
|
||||
getDoricContext().getContext().getSharedPreferences(
|
||||
prefName,
|
||||
Context.MODE_PRIVATE).edit().remove(key).apply();
|
||||
promise.resolve();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void clear(JSObject jsObject, final DoricPromise promise) {
|
||||
try {
|
||||
JSValue zone = jsObject.getProperty("zone");
|
||||
if (zone.isString()) {
|
||||
String prefName = PREF_NAME + "_" + zone.asString();
|
||||
getDoricContext().getContext().getSharedPreferences(
|
||||
prefName,
|
||||
Context.MODE_PRIVATE).edit().clear().apply();
|
||||
promise.resolve();
|
||||
} else {
|
||||
promise.reject(new JavaValue("Zone is empty"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
promise.reject(new JavaValue(e.getLocalizedMessage()));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,103 @@
|
||||
package pub.doric.refresh;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.pullable
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-25
|
||||
*/
|
||||
public class DoricRefreshView extends FrameLayout implements PullingListener {
|
||||
private View content;
|
||||
private Animation.AnimationListener mListener;
|
||||
|
||||
private PullingListener mPullingListener;
|
||||
|
||||
public DoricRefreshView(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public DoricRefreshView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public DoricRefreshView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setContent(View v) {
|
||||
removeAllViews();
|
||||
content = v;
|
||||
ViewGroup.LayoutParams params = v.getLayoutParams();
|
||||
if (params instanceof LayoutParams) {
|
||||
((LayoutParams) params).gravity = Gravity.BOTTOM;
|
||||
} else {
|
||||
LayoutParams layoutParams = new LayoutParams(
|
||||
params == null ? ViewGroup.LayoutParams.WRAP_CONTENT : params.width,
|
||||
params == null ? ViewGroup.LayoutParams.WRAP_CONTENT : params.height);
|
||||
layoutParams.gravity = Gravity.CENTER;
|
||||
v.setLayoutParams(layoutParams);
|
||||
}
|
||||
addView(v);
|
||||
}
|
||||
|
||||
public View getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
public void setPullingListener(PullingListener listener) {
|
||||
this.mPullingListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation() {
|
||||
if (mPullingListener != null) {
|
||||
mPullingListener.startAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAnimation() {
|
||||
if (mPullingListener != null) {
|
||||
mPullingListener.stopAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPullingDistance(float distance) {
|
||||
if (mPullingListener != null) {
|
||||
mPullingListener.setPullingDistance(distance);
|
||||
}
|
||||
}
|
||||
|
||||
public void setAnimationListener(Animation.AnimationListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAnimationStart() {
|
||||
super.onAnimationStart();
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationStart(getAnimation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAnimationEnd() {
|
||||
super.onAnimationEnd();
|
||||
if (mListener != null) {
|
||||
mListener.onAnimationEnd(getAnimation());
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,920 @@
|
||||
package pub.doric.refresh;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Transformation;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.view.NestedScrollingChild;
|
||||
import androidx.core.view.NestedScrollingChildHelper;
|
||||
import androidx.core.view.NestedScrollingParent;
|
||||
import androidx.core.view.NestedScrollingParentHelper;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.widget.ListViewCompat;
|
||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable;
|
||||
|
||||
import android.view.animation.Animation.AnimationListener;
|
||||
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.pullable
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-25
|
||||
*/
|
||||
public class DoricSwipeLayout extends ViewGroup implements NestedScrollingParent,
|
||||
NestedScrollingChild {
|
||||
// Maps to ProgressBar.Large style
|
||||
public static final int LARGE = CircularProgressDrawable.LARGE;
|
||||
// Maps to ProgressBar default style
|
||||
public static final int DEFAULT = CircularProgressDrawable.DEFAULT;
|
||||
|
||||
public static final int DEFAULT_SLINGSHOT_DISTANCE = -1;
|
||||
|
||||
@VisibleForTesting
|
||||
static final int CIRCLE_DIAMETER = 40;
|
||||
@VisibleForTesting
|
||||
static final int CIRCLE_DIAMETER_LARGE = 56;
|
||||
|
||||
private static final String LOG_TAG = DoricSwipeLayout.class.getSimpleName();
|
||||
|
||||
private static final int MAX_ALPHA = 255;
|
||||
private static final int STARTING_PROGRESS_ALPHA = (int) (.3f * MAX_ALPHA);
|
||||
|
||||
private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
|
||||
private static final int INVALID_POINTER = -1;
|
||||
private static final float DRAG_RATE = .5f;
|
||||
|
||||
// Max amount of circle that can be filled by progress during swipe gesture,
|
||||
// where 1.0 is a full circle
|
||||
private static final float MAX_PROGRESS_ANGLE = .8f;
|
||||
|
||||
private static final int SCALE_DOWN_DURATION = 150;
|
||||
|
||||
private static final int ALPHA_ANIMATION_DURATION = 300;
|
||||
|
||||
private static final int ANIMATE_TO_TRIGGER_DURATION = 200;
|
||||
|
||||
private static final int ANIMATE_TO_START_DURATION = 200;
|
||||
|
||||
// Default offset in dips from the top of the view to where the progress spinner should stop
|
||||
private static final int DEFAULT_CIRCLE_TARGET = 64;
|
||||
|
||||
private View mTarget; // the target of the gesture
|
||||
OnRefreshListener mListener;
|
||||
boolean mRefreshing = false;
|
||||
private int mTouchSlop;
|
||||
private float mTotalDragDistance = -1;
|
||||
|
||||
// If nested scrolling is enabled, the total amount that needed to be
|
||||
// consumed by this as the nested scrolling parent is used in place of the
|
||||
// overscroll determined by MOVE events in the onTouch handler
|
||||
private float mTotalUnconsumed;
|
||||
private final NestedScrollingParentHelper mNestedScrollingParentHelper;
|
||||
private final NestedScrollingChildHelper mNestedScrollingChildHelper;
|
||||
private final int[] mParentScrollConsumed = new int[2];
|
||||
private final int[] mParentOffsetInWindow = new int[2];
|
||||
private boolean mNestedScrollInProgress;
|
||||
|
||||
private int mMediumAnimationDuration;
|
||||
int mCurrentTargetOffsetTop;
|
||||
|
||||
private float mInitialMotionY;
|
||||
private float mInitialDownY;
|
||||
private boolean mIsBeingDragged;
|
||||
private int mActivePointerId = INVALID_POINTER;
|
||||
|
||||
// Target is returning to its start offset because it was cancelled or a
|
||||
// refresh was triggered.
|
||||
private boolean mReturningToStart;
|
||||
private final DecelerateInterpolator mDecelerateInterpolator;
|
||||
private static final int[] LAYOUT_ATTRS = new int[]{
|
||||
android.R.attr.enabled
|
||||
};
|
||||
|
||||
private int mCircleViewIndex = -1;
|
||||
|
||||
protected int mFrom;
|
||||
|
||||
float mStartingScale;
|
||||
|
||||
protected int mOriginalOffsetTop;
|
||||
|
||||
int mSpinnerOffsetEnd;
|
||||
|
||||
int mCustomSlingshotDistance;
|
||||
|
||||
|
||||
boolean mNotify;
|
||||
|
||||
// Whether the client has set a custom starting position;
|
||||
boolean mUsingCustomStart;
|
||||
|
||||
private OnChildScrollUpCallback mChildScrollUpCallback;
|
||||
|
||||
private DoricRefreshView mRefreshView;
|
||||
|
||||
private AnimationListener mRefreshListener = new AnimationListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
if (mRefreshing) {
|
||||
mRefreshView.startAnimation();
|
||||
if (mNotify) {
|
||||
if (mListener != null) {
|
||||
mListener.onRefresh();
|
||||
}
|
||||
}
|
||||
mCurrentTargetOffsetTop = mRefreshView.getTop();
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
};
|
||||
private int mPullDownHeight = 0;
|
||||
private ValueAnimator headerViewAnimator;
|
||||
|
||||
private void onRefreshAnimationEnd() {
|
||||
if (mRefreshing) {
|
||||
mRefreshView.startAnimation();
|
||||
if (mNotify) {
|
||||
if (mListener != null) {
|
||||
mListener.onRefresh();
|
||||
}
|
||||
}
|
||||
mCurrentTargetOffsetTop = mRefreshView.getTop();
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
mRefreshing = false;
|
||||
if (headerViewAnimator != null && headerViewAnimator.isRunning()) {
|
||||
headerViewAnimator.cancel();
|
||||
}
|
||||
headerViewAnimator = ValueAnimator.ofInt(mRefreshView.getBottom(), 0);
|
||||
headerViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
mCurrentTargetOffsetTop = (int) animation.getAnimatedValue()
|
||||
- mRefreshView.getMeasuredHeight();
|
||||
if (mRefreshView.getMeasuredHeight() > 0) {
|
||||
mRefreshView.setPullingDistance(DoricUtils.px2dp(mRefreshView.getBottom()));
|
||||
}
|
||||
mRefreshView.requestLayout();
|
||||
}
|
||||
});
|
||||
headerViewAnimator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mRefreshView.stopAnimation();
|
||||
mRefreshView.setVisibility(View.GONE);
|
||||
// Return the circle to its start position
|
||||
|
||||
setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop);
|
||||
mCurrentTargetOffsetTop = mRefreshView.getTop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
});
|
||||
headerViewAnimator.setDuration(SCALE_DOWN_DURATION);
|
||||
headerViewAnimator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (!enabled) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple constructor to use when creating a SwipeRefreshLayout from code.
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public DoricSwipeLayout(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor that is called when inflating SwipeRefreshLayout from XML.
|
||||
*
|
||||
* @param context
|
||||
* @param attrs
|
||||
*/
|
||||
public DoricSwipeLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
mMediumAnimationDuration = getResources().getInteger(
|
||||
android.R.integer.config_mediumAnimTime);
|
||||
|
||||
setWillNotDraw(false);
|
||||
mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
|
||||
|
||||
final DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||
|
||||
createProgressView();
|
||||
setChildrenDrawingOrderEnabled(true);
|
||||
// the absolute offset has to take into account that the circle starts at an offset
|
||||
mSpinnerOffsetEnd = (int) (DEFAULT_CIRCLE_TARGET * metrics.density);
|
||||
mTotalDragDistance = mSpinnerOffsetEnd;
|
||||
mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
|
||||
|
||||
mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
|
||||
setNestedScrollingEnabled(true);
|
||||
|
||||
moveToStart(1.0f);
|
||||
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
|
||||
setEnabled(a.getBoolean(0, true));
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setPullDownHeight(int height) {
|
||||
mPullDownHeight = height;
|
||||
mOriginalOffsetTop = mCurrentTargetOffsetTop = -height;
|
||||
mSpinnerOffsetEnd = height;
|
||||
mTotalDragDistance = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getChildDrawingOrder(int childCount, int i) {
|
||||
if (mCircleViewIndex < 0) {
|
||||
return i;
|
||||
} else if (i == childCount - 1) {
|
||||
// Draw the selected child last
|
||||
return mCircleViewIndex;
|
||||
} else if (i >= mCircleViewIndex) {
|
||||
// Move the children after the selected child earlier one
|
||||
return i + 1;
|
||||
} else {
|
||||
// Keep the children before the selected child the same
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
private void createProgressView() {
|
||||
mRefreshView = new DoricRefreshView(getContext());
|
||||
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
addView(mRefreshView, layoutParams);
|
||||
}
|
||||
|
||||
public DoricRefreshView getRefreshView() {
|
||||
return mRefreshView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener to be notified when a refresh is triggered via the swipe
|
||||
* gesture.
|
||||
*/
|
||||
public void setOnRefreshListener(@Nullable OnRefreshListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the widget that refresh state has changed. Do not call this when
|
||||
* refresh is triggered by a swipe gesture.
|
||||
*
|
||||
* @param refreshing Whether or not the view should show refresh progress.
|
||||
*/
|
||||
public void setRefreshing(boolean refreshing) {
|
||||
if (refreshing && mRefreshing != refreshing) {
|
||||
// scale and show
|
||||
mRefreshing = refreshing;
|
||||
int endTarget = 0;
|
||||
if (!mUsingCustomStart) {
|
||||
endTarget = mSpinnerOffsetEnd + mOriginalOffsetTop;
|
||||
} else {
|
||||
endTarget = mSpinnerOffsetEnd;
|
||||
}
|
||||
setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop);
|
||||
mNotify = false;
|
||||
mRefreshView.setVisibility(View.VISIBLE);
|
||||
onRefreshAnimationEnd();
|
||||
} else {
|
||||
setRefreshing(refreshing, false /* notify */);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setRefreshing(boolean refreshing, final boolean notify) {
|
||||
if (mRefreshing != refreshing) {
|
||||
mNotify = notify;
|
||||
ensureTarget();
|
||||
mRefreshing = refreshing;
|
||||
if (mRefreshing) {
|
||||
animateOffsetToCorrectPosition(mCurrentTargetOffsetTop, mRefreshListener);
|
||||
} else {
|
||||
onRefreshAnimationEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the SwipeRefreshWidget is actively showing refresh
|
||||
* progress.
|
||||
*/
|
||||
public boolean isRefreshing() {
|
||||
return mRefreshing;
|
||||
}
|
||||
|
||||
private void ensureTarget() {
|
||||
// Don't bother getting the parent height if the parent hasn't been laid
|
||||
// out yet.
|
||||
if (mTarget == null) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
if (!child.equals(mRefreshView)) {
|
||||
mTarget = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
final int width = getMeasuredWidth();
|
||||
final int height = getMeasuredHeight();
|
||||
if (getChildCount() == 0) {
|
||||
return;
|
||||
}
|
||||
if (mTarget == null) {
|
||||
ensureTarget();
|
||||
}
|
||||
if (mTarget == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int circleWidth = mRefreshView.getMeasuredWidth();
|
||||
int circleHeight = mRefreshView.getMeasuredHeight();
|
||||
|
||||
mRefreshView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop,
|
||||
(width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight);
|
||||
|
||||
final View child = mTarget;
|
||||
final int childLeft = getPaddingLeft();
|
||||
final int childTop = getPaddingTop() + mRefreshView.getBottom();
|
||||
final int childWidth = width - getPaddingLeft() - getPaddingRight();
|
||||
final int childHeight = height - getPaddingTop() - getPaddingBottom();
|
||||
child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (mTarget == null) {
|
||||
ensureTarget();
|
||||
}
|
||||
if (mTarget == null) {
|
||||
return;
|
||||
}
|
||||
mTarget.measure(MeasureSpec.makeMeasureSpec(
|
||||
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
|
||||
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
|
||||
getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY));
|
||||
mRefreshView.measure(
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
|
||||
MeasureSpec.EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(
|
||||
(getMeasuredHeight() - getPaddingTop() - getPaddingBottom()) / 3,
|
||||
MeasureSpec.UNSPECIFIED));
|
||||
if (mPullDownHeight != mRefreshView.getMeasuredHeight()) {
|
||||
setPullDownHeight(mRefreshView.getMeasuredHeight());
|
||||
}
|
||||
mCircleViewIndex = -1;
|
||||
// Get the index of the circleview.
|
||||
for (int index = 0; index < getChildCount(); index++) {
|
||||
if (getChildAt(index) == mRefreshView) {
|
||||
mCircleViewIndex = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether it is possible for the child view of this layout to
|
||||
* scroll up. Override this if the child view is a custom view.
|
||||
*/
|
||||
public boolean canChildScrollUp() {
|
||||
if (mChildScrollUpCallback != null) {
|
||||
return mChildScrollUpCallback.canChildScrollUp(this, mTarget);
|
||||
}
|
||||
if (mTarget instanceof ListView) {
|
||||
return ListViewCompat.canScrollList((ListView) mTarget, -1);
|
||||
}
|
||||
return mTarget.canScrollVertically(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a callback to override {@link androidx.swiperefreshlayout.widget.SwipeRefreshLayout#canChildScrollUp()} method. Non-null
|
||||
* callback will return the value provided by the callback and ignore all internal logic.
|
||||
*
|
||||
* @param callback Callback that should be called when canChildScrollUp() is called.
|
||||
*/
|
||||
public void setOnChildScrollUpCallback(@Nullable OnChildScrollUpCallback callback) {
|
||||
mChildScrollUpCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
ensureTarget();
|
||||
|
||||
final int action = ev.getActionMasked();
|
||||
int pointerIndex;
|
||||
|
||||
if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
|
||||
mReturningToStart = false;
|
||||
}
|
||||
|
||||
if (!isEnabled() || mReturningToStart || canChildScrollUp()
|
||||
|| mRefreshing || mNestedScrollInProgress) {
|
||||
// Fail fast if we're not in a state where a swipe is possible
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
setTargetOffsetTopAndBottom(mOriginalOffsetTop - mRefreshView.getTop());
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mIsBeingDragged = false;
|
||||
|
||||
pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (pointerIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
mInitialDownY = ev.getY(pointerIndex);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (mActivePointerId == INVALID_POINTER) {
|
||||
Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (pointerIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
final float y = ev.getY(pointerIndex);
|
||||
startDragging(y);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
onSecondaryPointerUp(ev);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsBeingDragged = false;
|
||||
mActivePointerId = INVALID_POINTER;
|
||||
break;
|
||||
}
|
||||
|
||||
return mIsBeingDragged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDisallowInterceptTouchEvent(boolean b) {
|
||||
// if this is a List < L or another view that doesn't support nested
|
||||
// scrolling, ignore this request so that the vertical scroll event
|
||||
// isn't stolen
|
||||
if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView)
|
||||
|| (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) {
|
||||
// Nope.
|
||||
} else {
|
||||
super.requestDisallowInterceptTouchEvent(b);
|
||||
}
|
||||
}
|
||||
|
||||
// NestedScrollingParent
|
||||
|
||||
@Override
|
||||
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
|
||||
return isEnabled() && !mReturningToStart && !mRefreshing
|
||||
&& (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScrollAccepted(View child, View target, int axes) {
|
||||
// Reset the counter of how much leftover scroll needs to be consumed.
|
||||
mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes);
|
||||
// Dispatch up to the nested parent
|
||||
startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);
|
||||
mTotalUnconsumed = 0;
|
||||
mNestedScrollInProgress = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
|
||||
// If we are in the middle of consuming, a scroll, then we want to move the spinner back up
|
||||
// before allowing the list to scroll
|
||||
if (dy > 0 && mTotalUnconsumed > 0) {
|
||||
if (dy > mTotalUnconsumed) {
|
||||
consumed[1] = dy - (int) mTotalUnconsumed;
|
||||
mTotalUnconsumed = 0;
|
||||
} else {
|
||||
if (dy > 3) {
|
||||
mTotalUnconsumed -= dy;
|
||||
consumed[1] = dy;
|
||||
}
|
||||
}
|
||||
moveSpinner(mTotalUnconsumed);
|
||||
}
|
||||
|
||||
// If a client layout is using a custom start position for the circle
|
||||
// view, they mean to hide it again before scrolling the child view
|
||||
// If we get back to mTotalUnconsumed == 0 and there is more to go, hide
|
||||
// the circle so it isn't exposed if its blocking content is moved
|
||||
if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0
|
||||
&& Math.abs(dy - consumed[1]) > 0) {
|
||||
mRefreshView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Now let our nested parent consume the leftovers
|
||||
final int[] parentConsumed = mParentScrollConsumed;
|
||||
if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) {
|
||||
consumed[0] += parentConsumed[0];
|
||||
consumed[1] += parentConsumed[1];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNestedScrollAxes() {
|
||||
return mNestedScrollingParentHelper.getNestedScrollAxes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopNestedScroll(View target) {
|
||||
mNestedScrollingParentHelper.onStopNestedScroll(target);
|
||||
mNestedScrollInProgress = false;
|
||||
// Finish the spinner for nested scrolling if we ever consumed any
|
||||
// unconsumed nested scroll
|
||||
if (mTotalUnconsumed > 0) {
|
||||
finishSpinner(mTotalUnconsumed);
|
||||
mTotalUnconsumed = 0;
|
||||
}
|
||||
// Dispatch up our nested parent
|
||||
stopNestedScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed,
|
||||
final int dxUnconsumed, final int dyUnconsumed) {
|
||||
// Dispatch up to the nested parent first
|
||||
dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
|
||||
mParentOffsetInWindow);
|
||||
|
||||
// This is a bit of a hack. Nested scrolling works from the bottom up, and as we are
|
||||
// sometimes between two nested scrolling views, we need a way to be able to know when any
|
||||
// nested scrolling parent has stopped handling events. We do that by using the
|
||||
// 'offset in window 'functionality to see if we have been moved from the event.
|
||||
// This is a decent indication of whether we should take over the event stream or not.
|
||||
final int dy = dyUnconsumed + mParentOffsetInWindow[1];
|
||||
if (dy < 0 && !canChildScrollUp()) {
|
||||
mTotalUnconsumed += Math.abs(dy);
|
||||
moveSpinner(mTotalUnconsumed);
|
||||
}
|
||||
}
|
||||
|
||||
// NestedScrollingChild
|
||||
|
||||
@Override
|
||||
public void setNestedScrollingEnabled(boolean enabled) {
|
||||
mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNestedScrollingEnabled() {
|
||||
return mNestedScrollingChildHelper.isNestedScrollingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startNestedScroll(int axes) {
|
||||
return mNestedScrollingChildHelper.startNestedScroll(axes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopNestedScroll() {
|
||||
mNestedScrollingChildHelper.stopNestedScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNestedScrollingParent() {
|
||||
return mNestedScrollingChildHelper.hasNestedScrollingParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
|
||||
int dyUnconsumed, int[] offsetInWindow) {
|
||||
return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
|
||||
dxUnconsumed, dyUnconsumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
|
||||
return mNestedScrollingChildHelper.dispatchNestedPreScroll(
|
||||
dx, dy, consumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedPreFling(View target, float velocityX,
|
||||
float velocityY) {
|
||||
return dispatchNestedPreFling(velocityX, velocityY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onNestedFling(View target, float velocityX, float velocityY,
|
||||
boolean consumed) {
|
||||
return dispatchNestedFling(velocityX, velocityY, consumed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
|
||||
return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
|
||||
return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
|
||||
}
|
||||
|
||||
private boolean isAnimationRunning(Animation animation) {
|
||||
return animation != null && animation.hasStarted() && !animation.hasEnded();
|
||||
}
|
||||
|
||||
private void moveSpinner(float overscrollTop) {
|
||||
float originalDragPercent = overscrollTop / mTotalDragDistance;
|
||||
|
||||
float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
|
||||
float extraOS = Math.abs(overscrollTop) - mTotalDragDistance;
|
||||
float slingshotDist = mCustomSlingshotDistance > 0
|
||||
? mCustomSlingshotDistance
|
||||
: (mUsingCustomStart
|
||||
? mSpinnerOffsetEnd - mOriginalOffsetTop
|
||||
: mSpinnerOffsetEnd);
|
||||
float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2)
|
||||
/ slingshotDist);
|
||||
float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(
|
||||
(tensionSlingshotPercent / 4), 2)) * 2f;
|
||||
float extraMove = (slingshotDist) * tensionPercent * 2;
|
||||
|
||||
int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove);
|
||||
// where 1.0f is a full circle
|
||||
if (mRefreshView.getVisibility() != View.VISIBLE) {
|
||||
mRefreshView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
mRefreshView.setScaleX(1f);
|
||||
mRefreshView.setScaleY(1f);
|
||||
|
||||
setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop);
|
||||
}
|
||||
|
||||
private void finishSpinner(float overscrollTop) {
|
||||
if (overscrollTop > mTotalDragDistance) {
|
||||
setRefreshing(true, true /* notify */);
|
||||
} else {
|
||||
// cancel refresh
|
||||
mRefreshing = false;
|
||||
animateOffsetToStartPosition(mCurrentTargetOffsetTop, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
final int action = ev.getActionMasked();
|
||||
int pointerIndex = -1;
|
||||
|
||||
if (mReturningToStart && action == MotionEvent.ACTION_DOWN) {
|
||||
mReturningToStart = false;
|
||||
}
|
||||
|
||||
if (!isEnabled() || mReturningToStart || canChildScrollUp()
|
||||
|| mRefreshing || mNestedScrollInProgress) {
|
||||
// Fail fast if we're not in a state where a swipe is possible
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mIsBeingDragged = false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (pointerIndex < 0) {
|
||||
Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final float y = ev.getY(pointerIndex);
|
||||
startDragging(y);
|
||||
|
||||
if (mIsBeingDragged) {
|
||||
final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
|
||||
if (overscrollTop > 0) {
|
||||
moveSpinner(overscrollTop);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
pointerIndex = ev.getActionIndex();
|
||||
if (pointerIndex < 0) {
|
||||
Log.e(LOG_TAG,
|
||||
"Got ACTION_POINTER_DOWN event but have an invalid action index.");
|
||||
return false;
|
||||
}
|
||||
mActivePointerId = ev.getPointerId(pointerIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
onSecondaryPointerUp(ev);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP: {
|
||||
pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (pointerIndex < 0) {
|
||||
Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mIsBeingDragged) {
|
||||
final float y = ev.getY(pointerIndex);
|
||||
final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;
|
||||
mIsBeingDragged = false;
|
||||
finishSpinner(overscrollTop);
|
||||
}
|
||||
mActivePointerId = INVALID_POINTER;
|
||||
return false;
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startDragging(float y) {
|
||||
final float yDiff = y - mInitialDownY;
|
||||
if (yDiff > mTouchSlop && !mIsBeingDragged) {
|
||||
mInitialMotionY = mInitialDownY + mTouchSlop;
|
||||
mIsBeingDragged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void animateOffsetToCorrectPosition(int from, AnimationListener listener) {
|
||||
mFrom = from;
|
||||
mAnimateToCorrectPosition.reset();
|
||||
mAnimateToCorrectPosition.setDuration(ANIMATE_TO_TRIGGER_DURATION);
|
||||
mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator);
|
||||
if (listener != null) {
|
||||
mRefreshView.setAnimationListener(listener);
|
||||
}
|
||||
mRefreshView.clearAnimation();
|
||||
mRefreshView.startAnimation(mAnimateToCorrectPosition);
|
||||
}
|
||||
|
||||
private void animateOffsetToStartPosition(int from, AnimationListener listener) {
|
||||
mFrom = from;
|
||||
mAnimateToStartPosition.reset();
|
||||
mAnimateToStartPosition.setDuration(ANIMATE_TO_START_DURATION);
|
||||
mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator);
|
||||
if (listener != null) {
|
||||
mRefreshView.setAnimationListener(listener);
|
||||
}
|
||||
mRefreshView.clearAnimation();
|
||||
mRefreshView.startAnimation(mAnimateToStartPosition);
|
||||
}
|
||||
|
||||
private final Animation mAnimateToCorrectPosition = new Animation() {
|
||||
@Override
|
||||
public void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
int targetTop = 0;
|
||||
int endTarget = 0;
|
||||
if (!mUsingCustomStart) {
|
||||
endTarget = mSpinnerOffsetEnd - Math.abs(mOriginalOffsetTop);
|
||||
} else {
|
||||
endTarget = mSpinnerOffsetEnd;
|
||||
}
|
||||
targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime));
|
||||
int offset = targetTop - mRefreshView.getTop();
|
||||
setTargetOffsetTopAndBottom(offset);
|
||||
}
|
||||
};
|
||||
|
||||
void moveToStart(float interpolatedTime) {
|
||||
int targetTop = 0;
|
||||
targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime));
|
||||
int offset = targetTop - mRefreshView.getTop();
|
||||
setTargetOffsetTopAndBottom(offset);
|
||||
}
|
||||
|
||||
private final Animation mAnimateToStartPosition = new Animation() {
|
||||
@Override
|
||||
public void applyTransformation(float interpolatedTime, Transformation t) {
|
||||
moveToStart(interpolatedTime);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void setTargetOffsetTopAndBottom(int offset) {
|
||||
mRefreshView.bringToFront();
|
||||
ViewCompat.offsetTopAndBottom(mRefreshView, offset);
|
||||
mCurrentTargetOffsetTop = mRefreshView.getTop();
|
||||
if (mRefreshView.getMeasuredHeight() > 0) {
|
||||
mRefreshView.setPullingDistance(DoricUtils.px2dp(mRefreshView.getBottom()));
|
||||
}
|
||||
}
|
||||
|
||||
private void onSecondaryPointerUp(MotionEvent ev) {
|
||||
final int pointerIndex = ev.getActionIndex();
|
||||
final int pointerId = ev.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
// This was our active pointer going up. Choose a new
|
||||
// active pointer and adjust accordingly.
|
||||
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
|
||||
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes that wish to be notified when the swipe gesture correctly
|
||||
* triggers a refresh should implement this interface.
|
||||
*/
|
||||
public interface OnRefreshListener {
|
||||
/**
|
||||
* Called when a swipe gesture triggers a refresh.
|
||||
*/
|
||||
void onRefresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes that wish to override {@link androidx.swiperefreshlayout.widget.SwipeRefreshLayout#canChildScrollUp()} method
|
||||
* behavior should implement this interface.
|
||||
*/
|
||||
public interface OnChildScrollUpCallback {
|
||||
/**
|
||||
* Callback that will be called when {@link androidx.swiperefreshlayout.widget.SwipeRefreshLayout#canChildScrollUp()} method
|
||||
* is called to allow the implementer to override its behavior.
|
||||
*
|
||||
* @param parent SwipeRefreshLayout that this callback is overriding.
|
||||
* @param child The child view of SwipeRefreshLayout.
|
||||
* @return Whether it is possible for the child view of parent layout to scroll up.
|
||||
*/
|
||||
boolean canChildScrollUp(@NonNull DoricSwipeLayout parent, @Nullable View child);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package pub.doric.refresh;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.pullable
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-25
|
||||
*/
|
||||
public interface PullingListener {
|
||||
|
||||
void startAnimation();
|
||||
|
||||
void stopAnimation();
|
||||
|
||||
/**
|
||||
* Set the amount of rotation to apply to the progress spinner.
|
||||
*
|
||||
* @param rotation Rotation is from [0..2]
|
||||
*/
|
||||
void setPullingDistance(float rotation);
|
||||
}
|
@@ -0,0 +1,196 @@
|
||||
package pub.doric.refresh;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
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.shader.SuperNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.pullable
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-26
|
||||
*/
|
||||
@DoricPlugin(name = "Refreshable")
|
||||
public class RefreshableNode extends SuperNode<DoricSwipeLayout> implements PullingListener {
|
||||
|
||||
private String mContentViewId;
|
||||
private ViewNode mContentNode;
|
||||
|
||||
private String mHeaderViewId;
|
||||
private ViewNode mHeaderNode;
|
||||
|
||||
public RefreshableNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected DoricSwipeLayout build() {
|
||||
DoricSwipeLayout doricSwipeLayout = new DoricSwipeLayout(getContext());
|
||||
doricSwipeLayout.getRefreshView().setPullingListener(this);
|
||||
return doricSwipeLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(DoricSwipeLayout view, String name, JSValue prop) {
|
||||
if ("content".equals(name)) {
|
||||
mContentViewId = prop.asString().value();
|
||||
} else if ("header".equals(name)) {
|
||||
mHeaderViewId = prop.asString().value();
|
||||
} else if ("onRefresh".equals(name)) {
|
||||
final String funcId = prop.asString().value();
|
||||
mView.setOnRefreshListener(new DoricSwipeLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
callJSResponse(funcId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
blendContentNode();
|
||||
blendHeadNode();
|
||||
}
|
||||
|
||||
|
||||
private void blendContentNode() {
|
||||
JSObject contentModel = getSubModel(mContentViewId);
|
||||
if (contentModel == null) {
|
||||
return;
|
||||
}
|
||||
String viewId = contentModel.getProperty("id").asString().value();
|
||||
String type = contentModel.getProperty("type").asString().value();
|
||||
JSObject props = contentModel.getProperty("props").asObject();
|
||||
if (mContentNode != null) {
|
||||
if (mContentNode.getId().equals(viewId)) {
|
||||
//skip
|
||||
} else {
|
||||
if (mReusable && type.equals(mContentNode.getType())) {
|
||||
mContentNode.setId(viewId);
|
||||
mContentNode.blend(props);
|
||||
} else {
|
||||
mView.removeAllViews();
|
||||
mContentNode = ViewNode.create(getDoricContext(), type);
|
||||
mContentNode.setId(viewId);
|
||||
mContentNode.init(this);
|
||||
mContentNode.blend(props);
|
||||
mView.addView(mContentNode.getNodeView());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mContentNode = ViewNode.create(getDoricContext(), type);
|
||||
mContentNode.setId(viewId);
|
||||
mContentNode.init(this);
|
||||
mContentNode.blend(props);
|
||||
mView.addView(mContentNode.getNodeView());
|
||||
}
|
||||
}
|
||||
|
||||
private void blendHeadNode() {
|
||||
JSObject headerModel = getSubModel(mHeaderViewId);
|
||||
if (headerModel == null) {
|
||||
return;
|
||||
}
|
||||
String viewId = headerModel.getProperty("id").asString().value();
|
||||
String type = headerModel.getProperty("type").asString().value();
|
||||
JSObject props = headerModel.getProperty("props").asObject();
|
||||
if (mHeaderNode != null) {
|
||||
if (mHeaderNode.getId().equals(viewId)) {
|
||||
//skip
|
||||
} else {
|
||||
if (mReusable && type.equals(mHeaderNode.getType())) {
|
||||
mHeaderNode.setId(viewId);
|
||||
mHeaderNode.blend(props);
|
||||
} else {
|
||||
mHeaderNode = ViewNode.create(getDoricContext(), type);
|
||||
mHeaderNode.setId(viewId);
|
||||
mHeaderNode.init(this);
|
||||
mHeaderNode.blend(props);
|
||||
mView.getRefreshView().setContent(mHeaderNode.getNodeView());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mHeaderNode = ViewNode.create(getDoricContext(), type);
|
||||
mHeaderNode.setId(viewId);
|
||||
mHeaderNode.init(this);
|
||||
mHeaderNode.blend(props);
|
||||
mView.getRefreshView().setContent(mHeaderNode.getNodeView());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewNode getSubNodeById(String id) {
|
||||
if (id.equals(mContentViewId)) {
|
||||
return mContentNode;
|
||||
}
|
||||
if (id.equals(mHeaderViewId)) {
|
||||
return mHeaderNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubNode(JSObject subProperties) {
|
||||
String viewId = subProperties.getProperty("id").asString().value();
|
||||
ViewNode node = getSubNodeById(viewId);
|
||||
if (node != null) {
|
||||
node.blend(subProperties.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setRefreshable(JSValue jsValue, DoricPromise doricPromise) {
|
||||
boolean refreshable = jsValue.asBoolean().value();
|
||||
this.mView.setEnabled(refreshable);
|
||||
doricPromise.resolve();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setRefreshing(JSValue jsValue, DoricPromise doricPromise) {
|
||||
boolean refreshing = jsValue.asBoolean().value();
|
||||
this.mView.setRefreshing(refreshing);
|
||||
doricPromise.resolve();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void isRefreshable(DoricPromise doricPromise) {
|
||||
doricPromise.resolve(new JavaValue(this.mView.isEnabled()));
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void isRefreshing(DoricPromise doricPromise) {
|
||||
doricPromise.resolve(new JavaValue(this.mView.isRefreshing()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation() {
|
||||
if (mHeaderNode != null) {
|
||||
mHeaderNode.callJSResponse("startAnimation");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopAnimation() {
|
||||
if (mHeaderNode != null) {
|
||||
mHeaderNode.callJSResponse("stopAnimation");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPullingDistance(float rotation) {
|
||||
if (mHeaderNode != null) {
|
||||
mHeaderNode.callJSResponse("setPullingDistance", rotation);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-31
|
||||
*/
|
||||
public class DoricLayer extends FrameLayout {
|
||||
private Path mCornerPath = new Path();
|
||||
private Paint mShadowPaint;
|
||||
private Paint mBorderPaint;
|
||||
private RectF mRect = new RectF();
|
||||
private float[] mCornerRadii;
|
||||
|
||||
public DoricLayer(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public DoricLayer(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public DoricLayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
public DoricLayer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
mRect.left = 0;
|
||||
mRect.right = getWidth();
|
||||
mRect.top = 0;
|
||||
mRect.bottom = getHeight();
|
||||
canvas.save();
|
||||
if (mCornerRadii != null) {
|
||||
mCornerPath.reset();
|
||||
mCornerPath.addRoundRect(mRect, mCornerRadii, Path.Direction.CW);
|
||||
canvas.clipPath(mCornerPath);
|
||||
}
|
||||
|
||||
super.dispatchDraw(canvas);
|
||||
canvas.restore();
|
||||
// draw border
|
||||
if (mBorderPaint != null) {
|
||||
((ViewGroup) getParent()).setClipChildren(false);
|
||||
if (mCornerRadii != null) {
|
||||
canvas.drawRoundRect(mRect, mCornerRadii[0], mCornerRadii[1], mBorderPaint);
|
||||
} else {
|
||||
canvas.drawRect(mRect, mBorderPaint);
|
||||
}
|
||||
}
|
||||
if (mShadowPaint != null) {
|
||||
((ViewGroup) getParent()).setClipChildren(false);
|
||||
canvas.save();
|
||||
if (mCornerRadii != null) {
|
||||
canvas.clipPath(mCornerPath, Region.Op.DIFFERENCE);
|
||||
canvas.drawRoundRect(mRect, mCornerRadii[0], mCornerRadii[1], mShadowPaint);
|
||||
} else {
|
||||
canvas.clipRect(mRect, Region.Op.DIFFERENCE);
|
||||
canvas.drawRect(mRect, mShadowPaint);
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
public void setShadow(int sdColor, int sdOpacity, int sdRadius, int offsetX, int offsetY) {
|
||||
if (mShadowPaint == null) {
|
||||
mShadowPaint = new Paint();
|
||||
mShadowPaint.setAntiAlias(true);
|
||||
mShadowPaint.setStyle(Paint.Style.FILL);
|
||||
}
|
||||
mShadowPaint.setColor(sdColor);
|
||||
mShadowPaint.setAlpha(sdOpacity);
|
||||
mShadowPaint.setShadowLayer(sdRadius, offsetX, offsetY, sdColor);
|
||||
}
|
||||
|
||||
public void setBorder(int borderWidth, int borderColor) {
|
||||
if (borderWidth == 0) {
|
||||
mBorderPaint = null;
|
||||
}
|
||||
if (mBorderPaint == null) {
|
||||
mBorderPaint = new Paint();
|
||||
mBorderPaint.setAntiAlias(true);
|
||||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
}
|
||||
mBorderPaint.setStrokeWidth(borderWidth);
|
||||
mBorderPaint.setColor(borderColor);
|
||||
}
|
||||
|
||||
public void setCornerRadius(int corner) {
|
||||
setCornerRadius(corner, corner, corner, corner);
|
||||
}
|
||||
|
||||
public void setCornerRadius(int leftTop, int rightTop, int rightBottom, int leftBottom) {
|
||||
mCornerRadii = new float[]{
|
||||
leftTop, leftTop,
|
||||
rightTop, rightTop,
|
||||
rightBottom, rightBottom,
|
||||
leftBottom, leftBottom,
|
||||
};
|
||||
}
|
||||
|
||||
public float getCornerRadius() {
|
||||
if (mCornerRadii != null) {
|
||||
return mCornerRadii[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
public abstract class GroupNode<F extends ViewGroup> extends SuperNode<F> {
|
||||
private ArrayList<ViewNode> mChildNodes = new ArrayList<>();
|
||||
private ArrayList<String> mChildViewIds = new ArrayList<>();
|
||||
|
||||
public GroupNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(F view, String name, JSValue prop) {
|
||||
if ("children".equals(name)) {
|
||||
JSArray ids = prop.asArray();
|
||||
mChildViewIds.clear();
|
||||
for (int i = 0; i < ids.size(); i++) {
|
||||
mChildViewIds.add(ids.get(i).asString().value());
|
||||
}
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
configChildNode();
|
||||
}
|
||||
|
||||
private void configChildNode() {
|
||||
for (int idx = 0; idx < mChildViewIds.size(); idx++) {
|
||||
String id = mChildViewIds.get(idx);
|
||||
JSObject model = getSubModel(id);
|
||||
String type = model.getProperty("type").asString().value();
|
||||
if (idx < mChildNodes.size()) {
|
||||
ViewNode oldNode = mChildNodes.get(idx);
|
||||
if (id.equals(oldNode.getId())) {
|
||||
//The same,skip
|
||||
} else {
|
||||
if (mReusable) {
|
||||
if (oldNode.getType().equals(type)) {
|
||||
//Same type,can be reused
|
||||
oldNode.setId(id);
|
||||
oldNode.blend(model.getProperty("props").asObject());
|
||||
} else {
|
||||
//Replace this view
|
||||
mChildNodes.remove(idx);
|
||||
mView.removeView(oldNode.getNodeView());
|
||||
ViewNode newNode = ViewNode.create(getDoricContext(), type);
|
||||
newNode.setId(id);
|
||||
newNode.init(this);
|
||||
newNode.blend(model.getProperty("props").asObject());
|
||||
mChildNodes.add(idx, newNode);
|
||||
mView.addView(newNode.getNodeView(), idx, newNode.getLayoutParams());
|
||||
}
|
||||
} else {
|
||||
//Find in remain nodes
|
||||
int position = -1;
|
||||
for (int start = idx + 1; start < mChildNodes.size(); start++) {
|
||||
ViewNode node = mChildNodes.get(start);
|
||||
if (id.equals(node.getId())) {
|
||||
//Found
|
||||
position = start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (position >= 0) {
|
||||
//Found swap idx,position
|
||||
ViewNode reused = mChildNodes.remove(position);
|
||||
ViewNode abandoned = mChildNodes.remove(idx);
|
||||
mChildNodes.set(idx, reused);
|
||||
mChildNodes.set(position, abandoned);
|
||||
//View swap index
|
||||
mView.removeView(reused.getNodeView());
|
||||
mView.addView(reused.getNodeView(), idx);
|
||||
mView.removeView(abandoned.getNodeView());
|
||||
mView.addView(abandoned.getNodeView(), position);
|
||||
} else {
|
||||
//Not found,insert
|
||||
ViewNode newNode = ViewNode.create(getDoricContext(), type);
|
||||
newNode.setId(id);
|
||||
newNode.init(this);
|
||||
newNode.blend(model.getProperty("props").asObject());
|
||||
|
||||
mChildNodes.add(idx, newNode);
|
||||
mView.addView(newNode.getNodeView(), idx, newNode.getLayoutParams());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Insert
|
||||
ViewNode newNode = ViewNode.create(getDoricContext(), type);
|
||||
newNode.setId(id);
|
||||
newNode.init(this);
|
||||
newNode.blend(model.getProperty("props").asObject());
|
||||
mChildNodes.add(newNode);
|
||||
mView.addView(newNode.getNodeView(), idx, newNode.getLayoutParams());
|
||||
}
|
||||
}
|
||||
int size = mChildNodes.size();
|
||||
for (int idx = mChildViewIds.size(); idx < size; idx++) {
|
||||
ViewNode viewNode = mChildNodes.remove(mChildViewIds.size());
|
||||
mView.removeView(viewNode.getNodeView());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubNode(JSObject subProp) {
|
||||
String subNodeId = subProp.getProperty("id").asString().value();
|
||||
for (ViewNode node : mChildNodes) {
|
||||
if (subNodeId.equals(node.getId())) {
|
||||
node.blend(subProp.getProperty("props").asObject());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewNode getSubNodeById(String id) {
|
||||
for (ViewNode node : mChildNodes) {
|
||||
if (id.equals(node.getId())) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-23
|
||||
*/
|
||||
@DoricPlugin(name = "HLayout")
|
||||
public class HLayoutNode extends LinearNode {
|
||||
public HLayoutNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LinearLayout build() {
|
||||
LinearLayout linearLayout = super.build();
|
||||
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
return linearLayout;
|
||||
}
|
||||
}
|
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
@DoricPlugin(name = "Image")
|
||||
public class ImageNode extends ViewNode<ImageView> {
|
||||
private String loadCallbackId = "";
|
||||
|
||||
public ImageNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageView build() {
|
||||
return new ImageView(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(ImageView view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "imageUrl":
|
||||
Glide.with(getContext()).load(prop.asString().value())
|
||||
.listener(new RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
||||
if (!TextUtils.isEmpty(loadCallbackId)) {
|
||||
callJSResponse(loadCallbackId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
|
||||
if (!TextUtils.isEmpty(loadCallbackId)) {
|
||||
callJSResponse(loadCallbackId, new JSONBuilder()
|
||||
.put("width", DoricUtils.px2dp(resource.getIntrinsicWidth()))
|
||||
.put("height", DoricUtils.px2dp(resource.getIntrinsicHeight()))
|
||||
.toJSONObject());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.into(view);
|
||||
break;
|
||||
case "scaleType":
|
||||
int scaleType = prop.asNumber().toInt();
|
||||
switch (scaleType) {
|
||||
case 1:
|
||||
view.setScaleType(ImageView.ScaleType.FIT_CENTER);
|
||||
break;
|
||||
case 2:
|
||||
view.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
break;
|
||||
default:
|
||||
view.setScaleType(ImageView.ScaleType.FIT_XY);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "loadCallback":
|
||||
this.loadCallbackId = prop.asString().value();
|
||||
break;
|
||||
case "imageBase64":
|
||||
Pattern r = Pattern.compile("data:image/(\\S+?);base64,(\\S+)");
|
||||
Matcher m = r.matcher(prop.asString().value());
|
||||
if (m.find()) {
|
||||
String imageType = m.group(1);
|
||||
String base64 = m.group(2);
|
||||
if (!TextUtils.isEmpty(imageType) && !TextUtils.isEmpty(base64)) {
|
||||
try {
|
||||
byte[] data = Base64.decode(base64, Base64.DEFAULT);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
|
||||
view.setImageBitmap(bitmap);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-23
|
||||
*/
|
||||
public class LinearNode extends GroupNode<LinearLayout> {
|
||||
public LinearNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject layoutConfig) {
|
||||
super.blendSubLayoutConfig(viewNode, layoutConfig);
|
||||
JSValue jsValue = layoutConfig.getProperty("alignment");
|
||||
if (jsValue.isNumber()) {
|
||||
((LinearLayout.LayoutParams) viewNode.getLayoutParams()).gravity = jsValue.asNumber().toInt();
|
||||
}
|
||||
JSValue weight = layoutConfig.getProperty("weight");
|
||||
if (weight.isNumber()) {
|
||||
((LinearLayout.LayoutParams) viewNode.getLayoutParams()).weight = weight.asNumber().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
return new LinearLayout.LayoutParams(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LinearLayout build() {
|
||||
return new LinearLayout(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(LinearLayout view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "space":
|
||||
ShapeDrawable shapeDrawable;
|
||||
if (view.getDividerDrawable() == null) {
|
||||
shapeDrawable = new ShapeDrawable();
|
||||
shapeDrawable.setAlpha(0);
|
||||
view.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
|
||||
} else {
|
||||
shapeDrawable = (ShapeDrawable) view.getDividerDrawable();
|
||||
view.setDividerDrawable(null);
|
||||
}
|
||||
if (view.getOrientation() == LinearLayout.VERTICAL) {
|
||||
shapeDrawable.setIntrinsicHeight(DoricUtils.dp2px(prop.asNumber().toFloat()));
|
||||
} else {
|
||||
shapeDrawable.setIntrinsicWidth(DoricUtils.dp2px(prop.asNumber().toFloat()));
|
||||
}
|
||||
view.setDividerDrawable(shapeDrawable);
|
||||
break;
|
||||
case "gravity":
|
||||
view.setGravity(prop.asNumber().toInt());
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.shader;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
@DoricPlugin(name = "Root")
|
||||
public class RootNode extends StackNode {
|
||||
public RootNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getNodeView() {
|
||||
return mView;
|
||||
}
|
||||
|
||||
public void setRootView(FrameLayout rootView) {
|
||||
this.mView = rootView;
|
||||
mLayoutParams = rootView.getLayoutParams();
|
||||
if (mLayoutParams == null) {
|
||||
mLayoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
rootView.setLayoutParams(mLayoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewGroup.LayoutParams getLayoutParams() {
|
||||
return mView.getLayoutParams();
|
||||
}
|
||||
}
|
@@ -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.shader;
|
||||
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.widget.HVScrollView;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-18
|
||||
*/
|
||||
@DoricPlugin(name = "Scroller")
|
||||
public class ScrollerNode extends SuperNode<HVScrollView> {
|
||||
private String mChildViewId;
|
||||
private ViewNode mChildNode;
|
||||
|
||||
public ScrollerNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewNode getSubNodeById(String id) {
|
||||
return id.equals(mChildNode.getId()) ? mChildNode : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubNode(JSObject subProperties) {
|
||||
if (mChildNode != null) {
|
||||
mChildNode.blend(subProperties.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HVScrollView build() {
|
||||
return new HVScrollView(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(HVScrollView view, String name, JSValue prop) {
|
||||
if ("content".equals(name)) {
|
||||
mChildViewId = prop.asString().value();
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
JSObject contentModel = getSubModel(mChildViewId);
|
||||
if (contentModel == null) {
|
||||
return;
|
||||
}
|
||||
String viewId = contentModel.getProperty("id").asString().value();
|
||||
String type = contentModel.getProperty("type").asString().value();
|
||||
JSObject props = contentModel.getProperty("props").asObject();
|
||||
if (mChildNode != null) {
|
||||
if (mChildNode.getId().equals(viewId)) {
|
||||
//skip
|
||||
} else {
|
||||
if (mReusable && type.equals(mChildNode.getType())) {
|
||||
mChildNode.setId(viewId);
|
||||
mChildNode.blend(props);
|
||||
} else {
|
||||
mView.removeAllViews();
|
||||
mChildNode = ViewNode.create(getDoricContext(), type);
|
||||
mChildNode.setId(viewId);
|
||||
mChildNode.init(this);
|
||||
mChildNode.blend(props);
|
||||
mView.addView(mChildNode.getNodeView());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mChildNode = ViewNode.create(getDoricContext(), type);
|
||||
mChildNode.setId(viewId);
|
||||
mChildNode.init(this);
|
||||
mChildNode.blend(props);
|
||||
mView.addView(mChildNode.getNodeView());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
@DoricPlugin(name = "Stack")
|
||||
public class StackNode extends GroupNode<FrameLayout> {
|
||||
public StackNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
|
||||
super.blendSubLayoutConfig(viewNode, jsObject);
|
||||
JSValue jsValue = jsObject.getProperty("alignment");
|
||||
if (jsValue.isNumber()) {
|
||||
((FrameLayout.LayoutParams) viewNode.getLayoutParams()).gravity = jsValue.asNumber().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FrameLayout build() {
|
||||
return new FrameLayout(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
return new FrameLayout.LayoutParams(0, 0);
|
||||
}
|
||||
}
|
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-13
|
||||
*/
|
||||
public abstract class SuperNode<V extends View> extends ViewNode<V> {
|
||||
private Map<String, JSObject> subNodes = new HashMap<>();
|
||||
protected boolean mReusable = false;
|
||||
|
||||
public SuperNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
public abstract ViewNode getSubNodeById(String id);
|
||||
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
return new ViewGroup.LayoutParams(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(V view, String name, JSValue prop) {
|
||||
if (name.equals("subviews")) {
|
||||
if (prop.isArray()) {
|
||||
JSArray subviews = prop.asArray();
|
||||
for (int i = 0; i < subviews.size(); i++) {
|
||||
JSObject subNode = subviews.get(i).asObject();
|
||||
mixinSubNode(subNode);
|
||||
blendSubNode(subNode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
private void mixinSubNode(JSObject subNode) {
|
||||
String id = subNode.getProperty("id").asString().value();
|
||||
JSObject targetNode = subNodes.get(id);
|
||||
if (targetNode == null) {
|
||||
subNodes.put(id, subNode);
|
||||
} else {
|
||||
mixin(subNode, targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
public JSObject getSubModel(String id) {
|
||||
return subNodes.get(id);
|
||||
}
|
||||
|
||||
public void setSubModel(String id, JSObject model) {
|
||||
subNodes.put(id, model);
|
||||
}
|
||||
|
||||
public void clearSubModel() {
|
||||
subNodes.clear();
|
||||
}
|
||||
|
||||
protected abstract void blendSubNode(JSObject subProperties);
|
||||
|
||||
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
|
||||
JSValue margin = jsObject.getProperty("margin");
|
||||
JSValue widthSpec = jsObject.getProperty("widthSpec");
|
||||
JSValue heightSpec = jsObject.getProperty("heightSpec");
|
||||
ViewGroup.LayoutParams layoutParams = viewNode.getLayoutParams();
|
||||
if (widthSpec.isNumber()) {
|
||||
switch (widthSpec.asNumber().toInt()) {
|
||||
case 1:
|
||||
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
break;
|
||||
case 2:
|
||||
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (heightSpec.isNumber()) {
|
||||
switch (heightSpec.asNumber().toInt()) {
|
||||
case 1:
|
||||
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
break;
|
||||
case 2:
|
||||
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (margin.isObject() && layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
JSValue topVal = margin.asObject().getProperty("top");
|
||||
if (topVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).topMargin = DoricUtils.dp2px(topVal.asNumber().toFloat());
|
||||
}
|
||||
JSValue leftVal = margin.asObject().getProperty("left");
|
||||
if (leftVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).leftMargin = DoricUtils.dp2px(leftVal.asNumber().toFloat());
|
||||
}
|
||||
JSValue rightVal = margin.asObject().getProperty("right");
|
||||
if (rightVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).rightMargin = DoricUtils.dp2px(rightVal.asNumber().toFloat());
|
||||
}
|
||||
JSValue bottomVal = margin.asObject().getProperty("bottom");
|
||||
if (bottomVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = DoricUtils.dp2px(bottomVal.asNumber().toFloat());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mixin(JSObject src, JSObject target) {
|
||||
JSObject srcProps = src.getProperty("props").asObject();
|
||||
JSObject targetProps = target.getProperty("props").asObject();
|
||||
for (String key : srcProps.propertySet()) {
|
||||
JSValue jsValue = srcProps.getProperty(key);
|
||||
if ("subviews".equals(key) && jsValue.isArray()) {
|
||||
continue;
|
||||
}
|
||||
targetProps.asObject().setProperty(key, jsValue);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean viewIdIsEqual(JSObject src, JSObject target) {
|
||||
String srcId = src.asObject().getProperty("id").asString().value();
|
||||
String targetId = target.asObject().getProperty("id").asString().value();
|
||||
return srcId.equals(targetId);
|
||||
}
|
||||
|
||||
protected void recursiveMixin(JSObject src, JSObject target) {
|
||||
JSObject srcProps = src.getProperty("props").asObject();
|
||||
JSObject targetProps = target.getProperty("props").asObject();
|
||||
JSValue oriSubviews = targetProps.getProperty("subviews");
|
||||
for (String key : srcProps.propertySet()) {
|
||||
JSValue jsValue = srcProps.getProperty(key);
|
||||
if ("subviews".equals(key) && jsValue.isArray()) {
|
||||
JSValue[] subviews = jsValue.asArray().toArray();
|
||||
for (JSValue subview : subviews) {
|
||||
if (oriSubviews.isArray()) {
|
||||
for (JSValue targetSubview : oriSubviews.asArray().toArray()) {
|
||||
if (viewIdIsEqual(subview.asObject(), targetSubview.asObject())) {
|
||||
recursiveMixin(subview.asObject(), targetSubview.asObject());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
targetProps.asObject().setProperty(key, jsValue);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.widget.TextView;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
/**
|
||||
* @Description: widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
@DoricPlugin(name = "Text")
|
||||
public class TextNode extends ViewNode<TextView> {
|
||||
public TextNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextView build() {
|
||||
TextView tv = new TextView(getContext());
|
||||
tv.setGravity(Gravity.CENTER);
|
||||
return tv;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(TextView view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "text":
|
||||
view.setText(prop.asString().toString());
|
||||
break;
|
||||
case "textSize":
|
||||
view.setTextSize(TypedValue.COMPLEX_UNIT_DIP, prop.asNumber().toFloat());
|
||||
break;
|
||||
case "textColor":
|
||||
view.setTextColor(prop.asNumber().toInt());
|
||||
break;
|
||||
case "textAlignment":
|
||||
view.setGravity(prop.asNumber().toInt() | Gravity.CENTER_VERTICAL);
|
||||
break;
|
||||
case "numberOfLines":
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-23
|
||||
*/
|
||||
@DoricPlugin(name = "VLayout")
|
||||
public class VLayoutNode extends LinearNode {
|
||||
public VLayoutNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LinearLayout build() {
|
||||
LinearLayout linearLayout = super.build();
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
return linearLayout;
|
||||
}
|
||||
|
||||
}
|
829
doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java
Normal file
829
doric-android/doric/src/main/java/pub/doric/shader/ViewNode.java
Normal file
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.DoricRegistry;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPromise;
|
||||
import pub.doric.utils.DoricContextHolder;
|
||||
import pub.doric.utils.DoricConstant;
|
||||
import pub.doric.utils.DoricLog;
|
||||
import pub.doric.utils.DoricMetaInfo;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* @Description: Render
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
public abstract class ViewNode<T extends View> extends DoricContextHolder {
|
||||
protected T mView;
|
||||
SuperNode mSuperNode;
|
||||
String mId;
|
||||
protected ViewGroup.LayoutParams mLayoutParams;
|
||||
private String mType;
|
||||
|
||||
public ViewNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
}
|
||||
|
||||
private DoricLayer doricLayer;
|
||||
|
||||
public void init(SuperNode superNode) {
|
||||
if (this instanceof SuperNode) {
|
||||
((SuperNode<T>) this).mReusable = superNode.mReusable;
|
||||
}
|
||||
this.mSuperNode = superNode;
|
||||
this.mLayoutParams = superNode.generateDefaultLayoutParams();
|
||||
this.mView = build();
|
||||
this.mView.setLayoutParams(mLayoutParams);
|
||||
}
|
||||
|
||||
public void init(ViewGroup.LayoutParams layoutParams) {
|
||||
this.mLayoutParams = layoutParams;
|
||||
this.mView = build();
|
||||
this.mView.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.mId = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public View getNodeView() {
|
||||
if (doricLayer != null) {
|
||||
return doricLayer;
|
||||
} else {
|
||||
return mView;
|
||||
}
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return getDoricContext().getContext();
|
||||
}
|
||||
|
||||
protected abstract T build();
|
||||
|
||||
public void blend(JSObject jsObject) {
|
||||
if (jsObject != null) {
|
||||
for (String prop : jsObject.propertySet()) {
|
||||
blend(mView, prop, jsObject.getProperty(prop));
|
||||
}
|
||||
}
|
||||
if (doricLayer != null) {
|
||||
ViewGroup.LayoutParams params = mView.getLayoutParams();
|
||||
if (params != null) {
|
||||
params.width = mLayoutParams.width;
|
||||
params.height = mLayoutParams.height;
|
||||
} else {
|
||||
params = mLayoutParams;
|
||||
}
|
||||
if (mLayoutParams instanceof LinearLayout.LayoutParams && ((LinearLayout.LayoutParams) mLayoutParams).weight > 0) {
|
||||
if (mSuperNode instanceof VLayoutNode) {
|
||||
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
} else if (mSuperNode instanceof HLayoutNode) {
|
||||
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
}
|
||||
}
|
||||
|
||||
mView.setLayoutParams(params);
|
||||
}
|
||||
}
|
||||
|
||||
protected void blend(T view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "width":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getWidth(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setWidth(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "height":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getHeight(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setHeight(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "x":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getX(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setX(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "y":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getY(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setY(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "backgroundColor":
|
||||
if (isAnimating()) {
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(
|
||||
this,
|
||||
name,
|
||||
getBackgroundColor(),
|
||||
prop.asNumber().toInt());
|
||||
animator.setEvaluator(new ArgbEvaluator());
|
||||
addAnimator(animator);
|
||||
} else {
|
||||
setBackgroundColor(prop.asNumber().toInt());
|
||||
}
|
||||
break;
|
||||
case "onClick":
|
||||
final String functionId = prop.asString().value();
|
||||
view.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
callJSResponse(functionId);
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "layoutConfig":
|
||||
setLayoutConfig(prop.asObject());
|
||||
break;
|
||||
case "border":
|
||||
if (prop.isObject()) {
|
||||
requireDoricLayer().setBorder(DoricUtils.dp2px(prop.asObject().getProperty("width").asNumber().toFloat()),
|
||||
prop.asObject().getProperty("color").asNumber().toInt());
|
||||
}
|
||||
break;
|
||||
case "alpha":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getAlpha(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setAlpha(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "corners":
|
||||
if (prop.isNumber()) {
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getCorners(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setCorners(prop.asNumber().toFloat());
|
||||
}
|
||||
} else if (prop.isObject()) {
|
||||
JSValue lt = prop.asObject().getProperty("leftTop");
|
||||
JSValue rt = prop.asObject().getProperty("rightTop");
|
||||
JSValue rb = prop.asObject().getProperty("rightBottom");
|
||||
JSValue lb = prop.asObject().getProperty("leftBottom");
|
||||
requireDoricLayer().setCornerRadius(
|
||||
DoricUtils.dp2px(lt.isNumber() ? lt.asNumber().toFloat() : 0),
|
||||
DoricUtils.dp2px(rt.isNumber() ? rt.asNumber().toFloat() : 0),
|
||||
DoricUtils.dp2px(rb.isNumber() ? rb.asNumber().toFloat() : 0),
|
||||
DoricUtils.dp2px(lb.isNumber() ? lb.asNumber().toFloat() : 0)
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "shadow":
|
||||
if (prop.isObject()) {
|
||||
requireDoricLayer().setShadow(
|
||||
prop.asObject().getProperty("color").asNumber().toInt(),
|
||||
(int) (prop.asObject().getProperty("opacity").asNumber().toFloat() * 255),
|
||||
DoricUtils.dp2px(prop.asObject().getProperty("radius").asNumber().toFloat()),
|
||||
DoricUtils.dp2px(prop.asObject().getProperty("offsetX").asNumber().toFloat()),
|
||||
DoricUtils.dp2px(prop.asObject().getProperty("offsetY").asNumber().toFloat())
|
||||
);
|
||||
}
|
||||
break;
|
||||
case "translationX":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getTranslationX(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setTranslationX(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "translationY":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getTranslationY(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setTranslationY(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "scaleX":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getScaleX(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setScaleX(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "scaleY":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getScaleY(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setScaleY(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "pivotX":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getPivotX(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setPivotX(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "pivotY":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getPivotY(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setPivotY(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
case "rotation":
|
||||
if (isAnimating()) {
|
||||
addAnimator(ObjectAnimator.ofFloat(
|
||||
this,
|
||||
name,
|
||||
getRotation(),
|
||||
prop.asNumber().toFloat()));
|
||||
} else {
|
||||
setRotation(prop.asNumber().toFloat());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private DoricLayer requireDoricLayer() {
|
||||
if (doricLayer == null) {
|
||||
doricLayer = new DoricLayer(getContext());
|
||||
doricLayer.setLayoutParams(mLayoutParams);
|
||||
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(mLayoutParams.width, mLayoutParams.height);
|
||||
if (mView.getParent() instanceof ViewGroup) {
|
||||
//Already added in
|
||||
ViewGroup superview = (ViewGroup) mView.getParent();
|
||||
int index = superview.indexOfChild(mView);
|
||||
superview.removeView(mView);
|
||||
doricLayer.addView(mView, params);
|
||||
superview.addView(doricLayer, index);
|
||||
} else {
|
||||
doricLayer.addView(mView, params);
|
||||
}
|
||||
}
|
||||
return doricLayer;
|
||||
}
|
||||
|
||||
String[] getIdList() {
|
||||
LinkedList<String> ids = new LinkedList<>();
|
||||
ViewNode viewNode = this;
|
||||
do {
|
||||
ids.push(viewNode.mId);
|
||||
viewNode = viewNode.mSuperNode;
|
||||
} while (viewNode != null);
|
||||
|
||||
return ids.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public AsyncResult<JSDecoder> callJSResponse(String funcId, Object... args) {
|
||||
final Object[] nArgs = new Object[args.length + 2];
|
||||
nArgs[0] = getIdList();
|
||||
nArgs[1] = funcId;
|
||||
if (args.length > 0) {
|
||||
System.arraycopy(args, 0, nArgs, 2, args.length);
|
||||
}
|
||||
return getDoricContext().callEntity(DoricConstant.DORIC_ENTITY_RESPONSE, nArgs);
|
||||
}
|
||||
|
||||
public static ViewNode create(DoricContext doricContext, String type) {
|
||||
DoricRegistry registry = doricContext.getDriver().getRegistry();
|
||||
DoricMetaInfo<ViewNode> clz = registry.acquireViewNodeInfo(type);
|
||||
ViewNode ret = clz.createInstance(doricContext);
|
||||
ret.mType = type;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ViewGroup.LayoutParams getLayoutParams() {
|
||||
return mLayoutParams;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
protected void setLayoutConfig(JSObject layoutConfig) {
|
||||
if (mSuperNode != null) {
|
||||
mSuperNode.blendSubLayoutConfig(this, layoutConfig);
|
||||
} else {
|
||||
blendLayoutConfig(layoutConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private void blendLayoutConfig(JSObject jsObject) {
|
||||
JSValue margin = jsObject.getProperty("margin");
|
||||
JSValue widthSpec = jsObject.getProperty("widthSpec");
|
||||
JSValue heightSpec = jsObject.getProperty("heightSpec");
|
||||
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
if (widthSpec.isNumber()) {
|
||||
switch (widthSpec.asNumber().toInt()) {
|
||||
case 1:
|
||||
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
break;
|
||||
case 2:
|
||||
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (heightSpec.isNumber()) {
|
||||
switch (heightSpec.asNumber().toInt()) {
|
||||
case 1:
|
||||
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
break;
|
||||
case 2:
|
||||
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (margin.isObject() && layoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
JSValue topVal = margin.asObject().getProperty("top");
|
||||
if (topVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).topMargin = DoricUtils.dp2px(topVal.asNumber().toFloat());
|
||||
}
|
||||
JSValue leftVal = margin.asObject().getProperty("left");
|
||||
if (leftVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).leftMargin = DoricUtils.dp2px(leftVal.asNumber().toFloat());
|
||||
}
|
||||
JSValue rightVal = margin.asObject().getProperty("right");
|
||||
if (rightVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).rightMargin = DoricUtils.dp2px(rightVal.asNumber().toFloat());
|
||||
}
|
||||
JSValue bottomVal = margin.asObject().getProperty("bottom");
|
||||
if (bottomVal.isNumber()) {
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = DoricUtils.dp2px(bottomVal.asNumber().toFloat());
|
||||
}
|
||||
}
|
||||
JSValue jsValue = jsObject.getProperty("alignment");
|
||||
if (jsValue.isNumber() && layoutParams instanceof FrameLayout.LayoutParams) {
|
||||
((FrameLayout.LayoutParams) layoutParams).gravity = jsValue.asNumber().toInt();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAnimating() {
|
||||
return getDoricContext().getAnimatorSet() != null;
|
||||
}
|
||||
|
||||
protected void addAnimator(Animator animator) {
|
||||
if (getDoricContext().getAnimatorSet() == null) {
|
||||
return;
|
||||
}
|
||||
getDoricContext().getAnimatorSet().play(animator);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getWidth() {
|
||||
if (mLayoutParams.width >= 0) {
|
||||
return DoricUtils.px2dp(mLayoutParams.width);
|
||||
} else {
|
||||
return mView.getMeasuredWidth();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getHeight() {
|
||||
if (mLayoutParams.width >= 0) {
|
||||
return DoricUtils.px2dp(mLayoutParams.height);
|
||||
} else {
|
||||
return mView.getMeasuredHeight();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setWidth(float width) {
|
||||
if (mLayoutParams.width >= 0) {
|
||||
mLayoutParams.width = DoricUtils.dp2px(width);
|
||||
if (mView.getLayoutParams() != mLayoutParams) {
|
||||
mView.getLayoutParams().width = mLayoutParams.width;
|
||||
}
|
||||
getNodeView().requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setHeight(float height) {
|
||||
if (mLayoutParams.height >= 0) {
|
||||
mLayoutParams.height = DoricUtils.dp2px(height);
|
||||
if (mView.getLayoutParams() != mLayoutParams) {
|
||||
mView.getLayoutParams().height = mLayoutParams.height;
|
||||
}
|
||||
getNodeView().requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setX(float x) {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin = DoricUtils.dp2px(x);
|
||||
getNodeView().requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
protected void setY(float y) {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin = DoricUtils.dp2px(y);
|
||||
getNodeView().requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getX() {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).leftMargin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getY() {
|
||||
if (mLayoutParams instanceof ViewGroup.MarginLayoutParams) {
|
||||
return DoricUtils.px2dp(((ViewGroup.MarginLayoutParams) mLayoutParams).topMargin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public int getBackgroundColor() {
|
||||
if (mView.getBackground() instanceof ColorDrawable) {
|
||||
return ((ColorDrawable) mView.getBackground()).getColor();
|
||||
}
|
||||
return Color.TRANSPARENT;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setBackgroundColor(int color) {
|
||||
mView.setBackgroundColor(color);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setAlpha(float alpha) {
|
||||
getNodeView().setAlpha(alpha);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getAlpha() {
|
||||
return getNodeView().getAlpha();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setCorners(float corner) {
|
||||
requireDoricLayer().setCornerRadius(DoricUtils.dp2px(corner));
|
||||
getNodeView().invalidate();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getCorners() {
|
||||
return DoricUtils.px2dp((int) requireDoricLayer().getCornerRadius());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setTranslationX(float v) {
|
||||
getNodeView().setTranslationX(DoricUtils.dp2px(v));
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getTranslationX() {
|
||||
return DoricUtils.px2dp((int) getNodeView().getTranslationX());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setTranslationY(float v) {
|
||||
getNodeView().setTranslationY(DoricUtils.dp2px(v));
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getTranslationY() {
|
||||
return DoricUtils.px2dp((int) getNodeView().getTranslationY());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setScaleX(float v) {
|
||||
getNodeView().setScaleX(v);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getScaleX() {
|
||||
return getNodeView().getScaleX();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setScaleY(float v) {
|
||||
getNodeView().setScaleY(v);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getScaleY() {
|
||||
return getNodeView().getScaleY();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setRotation(float rotation) {
|
||||
getNodeView().setRotation(rotation * 180);
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getRotation() {
|
||||
return getNodeView().getRotation() / 180;
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setPivotX(float v) {
|
||||
getNodeView().setPivotX(v * getNodeView().getWidth());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getPivotX() {
|
||||
return getNodeView().getPivotX() / getNodeView().getWidth();
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public void setPivotY(float v) {
|
||||
getNodeView().setPivotY(v * getNodeView().getHeight());
|
||||
}
|
||||
|
||||
@DoricMethod
|
||||
public float getPivotY() {
|
||||
return getNodeView().getPivotY() / getNodeView().getHeight();
|
||||
}
|
||||
|
||||
private String[] animatedKeys = {
|
||||
"translationX",
|
||||
"translationY",
|
||||
"scaleX",
|
||||
"scaleY",
|
||||
"rotation",
|
||||
};
|
||||
|
||||
@DoricMethod
|
||||
public void doAnimation(JSValue value, final DoricPromise promise) {
|
||||
Animator animator = parseAnimator(value);
|
||||
if (animator != null) {
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
JSONBuilder jsonBuilder = new JSONBuilder();
|
||||
for (String key : animatedKeys) {
|
||||
jsonBuilder.put(key, getAnimatedValue(key));
|
||||
}
|
||||
promise.resolve(new JavaValue(jsonBuilder.toJSONObject()));
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
}
|
||||
}
|
||||
|
||||
private Animator parseAnimator(JSValue value) {
|
||||
if (!value.isObject()) {
|
||||
DoricLog.e("parseAnimator error");
|
||||
return null;
|
||||
}
|
||||
JSValue animations = value.asObject().getProperty("animations");
|
||||
if (animations.isArray()) {
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
|
||||
for (int i = 0; i < animations.asArray().size(); i++) {
|
||||
animatorSet.play(parseAnimator(animations.asArray().get(i)));
|
||||
}
|
||||
|
||||
JSValue delayJS = value.asObject().getProperty("delay");
|
||||
if (delayJS.isNumber()) {
|
||||
animatorSet.setStartDelay(delayJS.asNumber().toLong());
|
||||
}
|
||||
return animatorSet;
|
||||
} else if (value.isObject()) {
|
||||
JSArray changeables = value.asObject().getProperty("changeables").asArray();
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
|
||||
JSValue repeatCount = value.asObject().getProperty("repeatCount");
|
||||
|
||||
JSValue repeatMode = value.asObject().getProperty("repeatMode");
|
||||
JSValue fillMode = value.asObject().getProperty("fillMode");
|
||||
JSValue timingFunction = value.asObject().getProperty("timingFunction");
|
||||
for (int j = 0; j < changeables.size(); j++) {
|
||||
ObjectAnimator animator = parseChangeable(changeables.get(j).asObject(), fillMode);
|
||||
if (repeatCount.isNumber()) {
|
||||
animator.setRepeatCount(repeatCount.asNumber().toInt());
|
||||
}
|
||||
if (repeatMode.isNumber()) {
|
||||
animator.setRepeatMode(repeatMode.asNumber().toInt());
|
||||
}
|
||||
if (timingFunction.isNumber()) {
|
||||
animator.setInterpolator(getTimingInterpolator(timingFunction.asNumber().toInt()));
|
||||
}
|
||||
animatorSet.play(animator);
|
||||
}
|
||||
long duration = value.asObject().getProperty("duration").asNumber().toLong();
|
||||
animatorSet.setDuration(duration);
|
||||
JSValue delayJS = value.asObject().getProperty("delay");
|
||||
if (delayJS.isNumber()) {
|
||||
animatorSet.setStartDelay(delayJS.asNumber().toLong());
|
||||
}
|
||||
return animatorSet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Interpolator getTimingInterpolator(int timingFunction) {
|
||||
switch (timingFunction) {
|
||||
case 1:
|
||||
return new LinearInterpolator();
|
||||
case 2:
|
||||
return new AccelerateInterpolator();
|
||||
case 3:
|
||||
return new DecelerateInterpolator();
|
||||
case 4:
|
||||
return new FastOutSlowInInterpolator();
|
||||
default:
|
||||
return new AccelerateDecelerateInterpolator();
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectAnimator parseChangeable(JSObject jsObject, JSValue fillMode) {
|
||||
String key = jsObject.getProperty("key").asString().value();
|
||||
float startVal = jsObject.getProperty("fromValue").asNumber().toFloat();
|
||||
float endVal = jsObject.getProperty("toValue").asNumber().toFloat();
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(this,
|
||||
key,
|
||||
startVal,
|
||||
endVal
|
||||
);
|
||||
setFillMode(animator, key, startVal, endVal, fillMode);
|
||||
return animator;
|
||||
}
|
||||
|
||||
private void setFillMode(ObjectAnimator animator,
|
||||
final String key,
|
||||
float startVal,
|
||||
float endVal,
|
||||
JSValue jsValue) {
|
||||
int fillMode = 0;
|
||||
if (jsValue.isNumber()) {
|
||||
fillMode = jsValue.asNumber().toInt();
|
||||
}
|
||||
if ((fillMode & 2) == 2) {
|
||||
setAnimatedValue(key, startVal);
|
||||
}
|
||||
final int finalFillMode = fillMode;
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
private float originVal;
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
super.onAnimationStart(animation);
|
||||
originVal = getAnimatedValue(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
if ((finalFillMode & 1) != 1) {
|
||||
setAnimatedValue(key, originVal);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setAnimatedValue(String key, float value) {
|
||||
switch (key) {
|
||||
case "translationX":
|
||||
setTranslationX(value);
|
||||
break;
|
||||
case "translationY":
|
||||
setTranslationY(value);
|
||||
break;
|
||||
case "scaleX":
|
||||
setScaleX(value);
|
||||
break;
|
||||
case "scaleY":
|
||||
setScaleY(value);
|
||||
break;
|
||||
case "rotation":
|
||||
setRotation(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private float getAnimatedValue(String key) {
|
||||
switch (key) {
|
||||
case "translationX":
|
||||
return getTranslationX();
|
||||
case "translationY":
|
||||
return getTranslationY();
|
||||
case "scaleX":
|
||||
return getScaleX();
|
||||
case "scaleY":
|
||||
return getScaleY();
|
||||
case "rotation":
|
||||
return getRotation();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader.flowlayout;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSNull;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.shader.ViewNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
|
||||
|
||||
private final FlowLayoutNode flowLayoutNode;
|
||||
String renderItemFuncId;
|
||||
int itemCount = 0;
|
||||
int batchCount = 15;
|
||||
SparseArray<String> itemValues = new SparseArray<>();
|
||||
|
||||
FlowAdapter(FlowLayoutNode flowLayoutNode) {
|
||||
this.flowLayoutNode = flowLayoutNode;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
FlowLayoutItemNode node = (FlowLayoutItemNode) ViewNode.create(flowLayoutNode.getDoricContext(), "FlowLayoutItem");
|
||||
node.init(flowLayoutNode);
|
||||
return new DoricViewHolder(node, node.getNodeView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
|
||||
JSValue jsValue = getItemModel(position);
|
||||
if (jsValue.isObject()) {
|
||||
JSObject jsObject = jsValue.asObject();
|
||||
holder.flowLayoutItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||
holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
JSValue value = getItemModel(position);
|
||||
if (value.isObject()) {
|
||||
if (value.asObject().getProperty("identifier").isString()) {
|
||||
return value.asObject().getProperty("identifier").asString().value().hashCode();
|
||||
}
|
||||
}
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
private JSValue getItemModel(final int position) {
|
||||
String id = itemValues.get(position);
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
AsyncResult<JSDecoder> asyncResult = flowLayoutNode.callJSResponse(
|
||||
"renderBunchedItems",
|
||||
position,
|
||||
batchCount);
|
||||
try {
|
||||
JSDecoder jsDecoder = asyncResult.synchronous().get();
|
||||
JSValue result = jsDecoder.decode();
|
||||
if (result.isArray()) {
|
||||
JSArray jsArray = result.asArray();
|
||||
for (int i = 0; i < jsArray.size(); i++) {
|
||||
JSObject itemModel = jsArray.get(i).asObject();
|
||||
String itemId = itemModel.getProperty("id").asString().value();
|
||||
itemValues.put(i + position, itemId);
|
||||
flowLayoutNode.setSubModel(itemId, itemModel);
|
||||
}
|
||||
return flowLayoutNode.getSubModel(itemValues.get(position));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new JSNull();
|
||||
} else {
|
||||
JSObject childModel = flowLayoutNode.getSubModel(id);
|
||||
if (childModel == null) {
|
||||
return new JSNull();
|
||||
} else {
|
||||
return childModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blendSubNode(JSObject subProperties) {
|
||||
for (int i = 0; i < itemValues.size(); i++) {
|
||||
if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) {
|
||||
notifyItemChanged(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DoricViewHolder extends RecyclerView.ViewHolder {
|
||||
FlowLayoutItemNode flowLayoutItemNode;
|
||||
|
||||
DoricViewHolder(FlowLayoutItemNode node, @NonNull View itemView) {
|
||||
super(itemView);
|
||||
flowLayoutItemNode = node;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.shader.flowlayout;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.shader.StackNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
@DoricPlugin(name = "FlowLayoutItem")
|
||||
public class FlowLayoutItemNode extends StackNode {
|
||||
public String identifier = "";
|
||||
|
||||
public FlowLayoutItemNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
this.mReusable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||
if ("identifier".equals(name)) {
|
||||
this.identifier = prop.asString().value();
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
getNodeView().getLayoutParams().width = getLayoutParams().width;
|
||||
getNodeView().getLayoutParams().height = getLayoutParams().height;
|
||||
}
|
||||
}
|
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader.flowlayout;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.shader.SuperNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.shader.flowlayout
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-28
|
||||
*/
|
||||
@DoricPlugin(name = "FlowLayout")
|
||||
public class FlowLayoutNode extends SuperNode<RecyclerView> {
|
||||
private final FlowAdapter flowAdapter;
|
||||
private final StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(
|
||||
2,
|
||||
StaggeredGridLayoutManager.VERTICAL);
|
||||
private int columnSpace = 0;
|
||||
private int rowSpace = 0;
|
||||
private final RecyclerView.ItemDecoration spacingItemDecoration = new RecyclerView.ItemDecoration() {
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||
outRect.set(columnSpace / 2, rowSpace / 2, columnSpace / 2, rowSpace / 2);
|
||||
}
|
||||
};
|
||||
|
||||
public FlowLayoutNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
this.flowAdapter = new FlowAdapter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewNode getSubNodeById(String id) {
|
||||
RecyclerView.LayoutManager manager = mView.getLayoutManager();
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < manager.getChildCount(); i++) {
|
||||
View view = manager.getChildAt(i);
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
FlowAdapter.DoricViewHolder viewHolder = (FlowAdapter.DoricViewHolder) mView.getChildViewHolder(view);
|
||||
if (id.equals(viewHolder.flowLayoutItemNode.getId())) {
|
||||
return viewHolder.flowLayoutItemNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(RecyclerView view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "columnSpace":
|
||||
columnSpace = DoricUtils.dp2px(prop.asNumber().toFloat());
|
||||
mView.setPadding(-columnSpace / 2, mView.getPaddingTop(), -columnSpace / 2, mView.getPaddingBottom());
|
||||
break;
|
||||
case "rowSpace":
|
||||
rowSpace = DoricUtils.dp2px(prop.asNumber().toFloat());
|
||||
mView.setPadding(mView.getPaddingLeft(), -rowSpace / 2, mView.getPaddingRight(), -rowSpace / 2);
|
||||
break;
|
||||
case "columnCount":
|
||||
staggeredGridLayoutManager.setSpanCount(prop.asNumber().toInt());
|
||||
break;
|
||||
case "itemCount":
|
||||
this.flowAdapter.itemCount = prop.asNumber().toInt();
|
||||
break;
|
||||
case "renderItem":
|
||||
this.flowAdapter.renderItemFuncId = prop.asString().value();
|
||||
// If reset renderItem,should reset native cache.
|
||||
this.flowAdapter.itemValues.clear();
|
||||
clearSubModel();
|
||||
break;
|
||||
case "batchCount":
|
||||
this.flowAdapter.batchCount = prop.asNumber().toInt();
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
if (mView != null) {
|
||||
mView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
flowAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubNode(JSObject subProperties) {
|
||||
String viewId = subProperties.getProperty("id").asString().value();
|
||||
ViewNode node = getSubNodeById(viewId);
|
||||
if (node != null) {
|
||||
node.blend(subProperties.getProperty("props").asObject());
|
||||
} else {
|
||||
JSObject oldModel = getSubModel(viewId);
|
||||
if (oldModel != null) {
|
||||
recursiveMixin(subProperties, oldModel);
|
||||
}
|
||||
flowAdapter.blendSubNode(subProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView build() {
|
||||
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||
recyclerView.setLayoutManager(staggeredGridLayoutManager);
|
||||
recyclerView.setAdapter(flowAdapter);
|
||||
recyclerView.addItemDecoration(spacingItemDecoration);
|
||||
return recyclerView;
|
||||
}
|
||||
}
|
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader.list;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSNull;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.shader.ViewNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
|
||||
|
||||
private final ListNode listNode;
|
||||
String renderItemFuncId;
|
||||
int itemCount = 0;
|
||||
int batchCount = 15;
|
||||
SparseArray<String> itemValues = new SparseArray<>();
|
||||
|
||||
ListAdapter(ListNode listNode) {
|
||||
this.listNode = listNode;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ListItemNode node = (ListItemNode) ViewNode.create(listNode.getDoricContext(), "ListItem");
|
||||
node.init(listNode);
|
||||
return new DoricViewHolder(node, node.getNodeView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
|
||||
JSValue jsValue = getItemModel(position);
|
||||
if (jsValue.isObject()) {
|
||||
JSObject jsObject = jsValue.asObject();
|
||||
holder.listItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||
holder.listItemNode.blend(jsObject.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
JSValue value = getItemModel(position);
|
||||
if (value.isObject()) {
|
||||
if (value.asObject().getProperty("identifier").isString()) {
|
||||
return value.asObject().getProperty("identifier").asString().value().hashCode();
|
||||
}
|
||||
}
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
private JSValue getItemModel(final int position) {
|
||||
String id = itemValues.get(position);
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
AsyncResult<JSDecoder> asyncResult = listNode.callJSResponse(
|
||||
"renderBunchedItems",
|
||||
position,
|
||||
batchCount);
|
||||
try {
|
||||
JSDecoder jsDecoder = asyncResult.synchronous().get();
|
||||
JSValue result = jsDecoder.decode();
|
||||
if (result.isArray()) {
|
||||
JSArray jsArray = result.asArray();
|
||||
for (int i = 0; i < jsArray.size(); i++) {
|
||||
JSObject itemModel = jsArray.get(i).asObject();
|
||||
String itemId = itemModel.getProperty("id").asString().value();
|
||||
itemValues.put(i + position, itemId);
|
||||
listNode.setSubModel(itemId, itemModel);
|
||||
}
|
||||
return listNode.getSubModel(itemValues.get(position));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new JSNull();
|
||||
} else {
|
||||
JSObject childModel = listNode.getSubModel(id);
|
||||
if (childModel == null) {
|
||||
return new JSNull();
|
||||
} else {
|
||||
return childModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blendSubNode(JSObject subProperties) {
|
||||
for (int i = 0; i < itemValues.size(); i++) {
|
||||
if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) {
|
||||
notifyItemChanged(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DoricViewHolder extends RecyclerView.ViewHolder {
|
||||
ListItemNode listItemNode;
|
||||
|
||||
DoricViewHolder(ListItemNode node, @NonNull View itemView) {
|
||||
super(itemView);
|
||||
listItemNode = node;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.shader.list;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.shader.StackNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
@DoricPlugin(name = "ListItem")
|
||||
public class ListItemNode extends StackNode {
|
||||
public String identifier = "";
|
||||
|
||||
public ListItemNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
this.mReusable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||
if ("identifier".equals(name)) {
|
||||
this.identifier = prop.asString().value();
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
getNodeView().getLayoutParams().width = getLayoutParams().width;
|
||||
getNodeView().getLayoutParams().height = getLayoutParams().height;
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader.list;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.shader.SuperNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
@DoricPlugin(name = "List")
|
||||
public class ListNode extends SuperNode<RecyclerView> {
|
||||
private final ListAdapter listAdapter;
|
||||
|
||||
public ListNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
this.listAdapter = new ListAdapter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubNode(JSObject subProperties) {
|
||||
String viewId = subProperties.getProperty("id").asString().value();
|
||||
ViewNode node = getSubNodeById(viewId);
|
||||
if (node != null) {
|
||||
node.blend(subProperties.getProperty("props").asObject());
|
||||
} else {
|
||||
JSObject oldModel = getSubModel(viewId);
|
||||
if (oldModel != null) {
|
||||
recursiveMixin(subProperties, oldModel);
|
||||
}
|
||||
listAdapter.blendSubNode(subProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView build() {
|
||||
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
recyclerView.setAdapter(this.listAdapter);
|
||||
return recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
if (mView != null) {
|
||||
mView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(RecyclerView view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "itemCount":
|
||||
this.listAdapter.itemCount = prop.asNumber().toInt();
|
||||
break;
|
||||
case "renderItem":
|
||||
this.listAdapter.renderItemFuncId = prop.asString().value();
|
||||
// If reset renderItem,should reset native cache.
|
||||
this.listAdapter.itemValues.clear();
|
||||
clearSubModel();
|
||||
break;
|
||||
case "batchCount":
|
||||
this.listAdapter.batchCount = prop.asNumber().toInt();
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubLayoutConfig(ViewNode viewNode, JSObject jsObject) {
|
||||
super.blendSubLayoutConfig(viewNode, jsObject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewNode getSubNodeById(String id) {
|
||||
RecyclerView.LayoutManager manager = mView.getLayoutManager();
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < manager.getChildCount(); i++) {
|
||||
View view = manager.getChildAt(i);
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
ListAdapter.DoricViewHolder viewHolder = (ListAdapter.DoricViewHolder) mView.getChildViewHolder(view);
|
||||
if (id.equals(viewHolder.listItemNode.getId())) {
|
||||
return viewHolder.listItemNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader.slider;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSNull;
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.shader.ViewNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
class SlideAdapter extends RecyclerView.Adapter<SlideAdapter.DoricViewHolder> {
|
||||
|
||||
private final SliderNode sliderNode;
|
||||
int itemCount = 0;
|
||||
int batchCount = 3;
|
||||
SparseArray<String> itemValues = new SparseArray<>();
|
||||
|
||||
SlideAdapter(SliderNode sliderNode) {
|
||||
this.sliderNode = sliderNode;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DoricViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
SlideItemNode node = (SlideItemNode) ViewNode.create(sliderNode.getDoricContext(), "SlideItem");
|
||||
node.init(sliderNode);
|
||||
return new DoricViewHolder(node, node.getNodeView());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
|
||||
JSValue jsValue = getItemModel(position);
|
||||
if (jsValue.isObject()) {
|
||||
JSObject jsObject = jsValue.asObject();
|
||||
holder.slideItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||
holder.slideItemNode.blend(jsObject.getProperty("props").asObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
JSValue value = getItemModel(position);
|
||||
if (value.isObject()) {
|
||||
if (value.asObject().getProperty("identifier").isString()) {
|
||||
return value.asObject().getProperty("identifier").asString().value().hashCode();
|
||||
}
|
||||
}
|
||||
return super.getItemViewType(position);
|
||||
}
|
||||
|
||||
private JSValue getItemModel(final int position) {
|
||||
String id = itemValues.get(position);
|
||||
if (TextUtils.isEmpty(id)) {
|
||||
AsyncResult<JSDecoder> asyncResult = sliderNode.callJSResponse(
|
||||
"renderBunchedItems",
|
||||
position,
|
||||
batchCount);
|
||||
try {
|
||||
JSDecoder jsDecoder = asyncResult.synchronous().get();
|
||||
JSValue result = jsDecoder.decode();
|
||||
if (result.isArray()) {
|
||||
JSArray jsArray = result.asArray();
|
||||
for (int i = 0; i < jsArray.size(); i++) {
|
||||
JSObject itemModel = jsArray.get(i).asObject();
|
||||
String itemId = itemModel.getProperty("id").asString().value();
|
||||
itemValues.put(i + position, itemId);
|
||||
sliderNode.setSubModel(itemId, itemModel);
|
||||
}
|
||||
return sliderNode.getSubModel(itemValues.get(position));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new JSNull();
|
||||
} else {
|
||||
JSObject childModel = sliderNode.getSubModel(id);
|
||||
if (childModel == null) {
|
||||
return new JSNull();
|
||||
} else {
|
||||
return childModel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void blendSubNode(JSObject subProperties) {
|
||||
for (int i = 0; i < itemValues.size(); i++) {
|
||||
if (subProperties.getProperty("id").asString().value().equals(itemValues.valueAt(i))) {
|
||||
notifyItemChanged(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DoricViewHolder extends RecyclerView.ViewHolder {
|
||||
SlideItemNode slideItemNode;
|
||||
|
||||
DoricViewHolder(SlideItemNode node, @NonNull View itemView) {
|
||||
super(itemView);
|
||||
slideItemNode = node;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.shader.slider;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.shader.StackNode;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.widget
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-12
|
||||
*/
|
||||
@DoricPlugin(name = "SlideItem")
|
||||
public class SlideItemNode extends StackNode {
|
||||
public String identifier = "";
|
||||
|
||||
public SlideItemNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
this.mReusable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(FrameLayout view, String name, JSValue prop) {
|
||||
if ("identifier".equals(name)) {
|
||||
this.identifier = prop.asString().value();
|
||||
} else {
|
||||
super.blend(view, name, prop);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
getNodeView().getLayoutParams().width = getLayoutParams().width;
|
||||
getNodeView().getLayoutParams().height = getLayoutParams().height;
|
||||
}
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.shader.slider;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.PagerSnapHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSObject;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
import pub.doric.shader.SuperNode;
|
||||
import pub.doric.shader.ViewNode;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.shader
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-19
|
||||
*/
|
||||
@DoricPlugin(name = "Slider")
|
||||
public class SliderNode extends SuperNode<RecyclerView> {
|
||||
private final SlideAdapter slideAdapter;
|
||||
|
||||
public SliderNode(DoricContext doricContext) {
|
||||
super(doricContext);
|
||||
this.slideAdapter = new SlideAdapter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView build() {
|
||||
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||
|
||||
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
|
||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
PagerSnapHelper mPagerSnapHelper = new PagerSnapHelper();
|
||||
mPagerSnapHelper.attachToRecyclerView(recyclerView);
|
||||
recyclerView.setAdapter(this.slideAdapter);
|
||||
return recyclerView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewNode getSubNodeById(String id) {
|
||||
RecyclerView.LayoutManager manager = mView.getLayoutManager();
|
||||
if (manager == null) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < manager.getChildCount(); i++) {
|
||||
View view = manager.getChildAt(i);
|
||||
if (view == null) {
|
||||
continue;
|
||||
}
|
||||
SlideAdapter.DoricViewHolder viewHolder = (SlideAdapter.DoricViewHolder) mView.getChildViewHolder(view);
|
||||
if (id.equals(viewHolder.slideItemNode.getId())) {
|
||||
return viewHolder.slideItemNode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blendSubNode(JSObject subProperties) {
|
||||
String viewId = subProperties.getProperty("id").asString().value();
|
||||
ViewNode node = getSubNodeById(viewId);
|
||||
if (node != null) {
|
||||
node.blend(subProperties.getProperty("props").asObject());
|
||||
} else {
|
||||
JSObject oldModel = getSubModel(viewId);
|
||||
if (oldModel != null) {
|
||||
recursiveMixin(subProperties, oldModel);
|
||||
}
|
||||
slideAdapter.blendSubNode(subProperties);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blend(JSObject jsObject) {
|
||||
super.blend(jsObject);
|
||||
if (mView != null) {
|
||||
mView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
slideAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void blend(RecyclerView view, String name, JSValue prop) {
|
||||
switch (name) {
|
||||
case "itemCount":
|
||||
this.slideAdapter.itemCount = prop.asNumber().toInt();
|
||||
break;
|
||||
case "renderItem":
|
||||
// If reset renderItem,should reset native cache.
|
||||
this.slideAdapter.itemValues.clear();
|
||||
clearSubModel();
|
||||
break;
|
||||
case "batchCount":
|
||||
this.slideAdapter.batchCount = prop.asNumber().toInt();
|
||||
break;
|
||||
default:
|
||||
super.blend(view, name, prop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
public class DoricConstant {
|
||||
public static final String DORIC_BUNDLE_SANDBOX = "bundle/doric-sandbox.js";
|
||||
public static final String DORIC_BUNDLE_LIB = "bundle/doric-lib.js";
|
||||
public static final String DORIC_MODULE_LIB = "doric";
|
||||
|
||||
|
||||
public static final String INJECT_LOG = "nativeLog";
|
||||
public static final String INJECT_REQUIRE = "nativeRequire";
|
||||
public static final String INJECT_TIMER_SET = "nativeSetTimer";
|
||||
public static final String INJECT_TIMER_CLEAR = "nativeClearTimer";
|
||||
public static final String INJECT_BRIDGE = "nativeBridge";
|
||||
public static final String INJECT_EMPTY = "nativeEmpty";
|
||||
|
||||
public static final String TEMPLATE_CONTEXT_CREATE = "Reflect.apply(" +
|
||||
"function(doric,context,Entry,require,exports){" + "\n" +
|
||||
"%s" + "\n" +
|
||||
"},doric.jsObtainContext(\"%s\"),[" +
|
||||
"undefined," +
|
||||
"doric.jsObtainContext(\"%s\")," +
|
||||
"doric.jsObtainEntry(\"%s\")," +
|
||||
"doric.__require__" +
|
||||
",{}" +
|
||||
"])";
|
||||
|
||||
public static final String TEMPLATE_MODULE = "Reflect.apply(doric.jsRegisterModule,this,[" +
|
||||
"\"%s\"," +
|
||||
"Reflect.apply(function(__module){" +
|
||||
"(function(module,exports,require){" + "\n" +
|
||||
"%s" + "\n" +
|
||||
"})(__module,__module.exports,doric.__require__);" +
|
||||
"\nreturn __module.exports;" +
|
||||
"},this,[{exports:{}}])" +
|
||||
"])";
|
||||
public static final String TEMPLATE_CONTEXT_DESTROY = "doric.jsReleaseContext(\"%s\")";
|
||||
public static final String GLOBAL_DORIC = "doric";
|
||||
public static final String DORIC_CONTEXT_RELEASE = "jsReleaseContext";
|
||||
public static final String DORIC_CONTEXT_INVOKE = "jsCallEntityMethod";
|
||||
public static final String DORIC_TIMER_CALLBACK = "jsCallbackTimer";
|
||||
public static final String DORIC_BRIDGE_RESOLVE = "jsCallResolve";
|
||||
public static final String DORIC_BRIDGE_REJECT = "jsCallReject";
|
||||
|
||||
|
||||
public static final String DORIC_ENTITY_RESPONSE = "__response__";
|
||||
public static final String DORIC_ENTITY_INIT = "__init__";
|
||||
public static final String DORIC_ENTITY_CREATE = "__onCreate__";
|
||||
public static final String DORIC_ENTITY_DESTROY = "__onDestroy__";
|
||||
public static final String DORIC_ENTITY_SHOW = "__onShow__";
|
||||
public static final String DORIC_ENTITY_HIDDEN = "__onHidden__";
|
||||
}
|
@@ -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.utils;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.utils
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
public abstract class DoricContextHolder {
|
||||
private final DoricContext doricContext;
|
||||
|
||||
public DoricContextHolder(DoricContext doricContext) {
|
||||
this.doricContext = doricContext;
|
||||
}
|
||||
|
||||
public DoricContext getDoricContext() {
|
||||
return doricContext;
|
||||
}
|
||||
}
|
@@ -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.utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import pub.doric.Doric;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
public class DoricLog {
|
||||
private static String TAG = Doric.class.getSimpleName();
|
||||
|
||||
|
||||
public static void d(String message, Object... args) {
|
||||
Log.d(suffixTag(null), format(message, args));
|
||||
}
|
||||
|
||||
public static void w(String message, Object... args) {
|
||||
Log.w(suffixTag(null), format(message, args));
|
||||
|
||||
}
|
||||
|
||||
public static void e(String message, Object... args) {
|
||||
Log.e(suffixTag(null), format(message, args));
|
||||
}
|
||||
|
||||
public static void suffix_d(String suffix, String message, Object... args) {
|
||||
Log.d(suffixTag(suffix), format(message, args));
|
||||
}
|
||||
|
||||
public static void suffix_w(String suffix, String message, Object... args) {
|
||||
Log.w(suffixTag(suffix), format(message, args));
|
||||
}
|
||||
|
||||
public static void suffix_e(String suffix, String message, Object... args) {
|
||||
Log.e(suffixTag(suffix), format(message, args));
|
||||
}
|
||||
|
||||
private static String suffixTag(String suffix) {
|
||||
return TextUtils.isEmpty(suffix) ? TAG : TAG + suffix;
|
||||
}
|
||||
|
||||
private static String format(String message, Object... args) {
|
||||
return String.format(message, args);
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.extension.bridge.DoricMethod;
|
||||
import pub.doric.extension.bridge.DoricPlugin;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
public class DoricMetaInfo<T extends DoricContextHolder> {
|
||||
|
||||
private Constructor<? extends T> pluginConstructor;
|
||||
|
||||
private Map<String, Method> methodMap = new ConcurrentHashMap<>();
|
||||
private String name;
|
||||
|
||||
public DoricMetaInfo(Class<? extends T> pluginClass) {
|
||||
try {
|
||||
this.pluginConstructor = pluginClass.getDeclaredConstructor(DoricContext.class);
|
||||
DoricPlugin doricPlugin = pluginClass.getAnnotation(DoricPlugin.class);
|
||||
this.name = doricPlugin.name();
|
||||
Method[] methods = pluginClass.getMethods();
|
||||
for (Method method : methods) {
|
||||
DoricMethod doricMethod = method.getAnnotation(DoricMethod.class);
|
||||
if (doricMethod != null) {
|
||||
if (TextUtils.isEmpty(doricMethod.name())) {
|
||||
methodMap.put(method.getName(), method);
|
||||
} else {
|
||||
methodMap.put(doricMethod.name(), method);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
DoricLog.e("Error to create doric for " + e.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public T createInstance(DoricContext doricContext) {
|
||||
try {
|
||||
return pluginConstructor.newInstance(doricContext);
|
||||
} catch (Exception e) {
|
||||
DoricLog.e("Error to create doric plugin for " + e.getLocalizedMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Method getMethod(String name) {
|
||||
return methodMap.get(name);
|
||||
}
|
||||
}
|
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSArray;
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
import com.github.pengfeizhou.jscore.JSValue;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import pub.doric.Doric;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
public class DoricUtils {
|
||||
public static String readAssetFile(String assetFile) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
AssetManager assetManager = Doric.application().getAssets();
|
||||
inputStream = assetManager.open(assetFile);
|
||||
int length = inputStream.available();
|
||||
byte[] buffer = new byte[length];
|
||||
inputStream.read(buffer);
|
||||
return new String(buffer);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static JavaValue toJavaValue(Object arg) {
|
||||
if (arg == null) {
|
||||
return new JavaValue();
|
||||
} else if (arg instanceof JSONBuilder) {
|
||||
return new JavaValue(((JSONBuilder) arg).toJSONObject());
|
||||
} else if (arg instanceof JSONObject) {
|
||||
return new JavaValue((JSONObject) arg);
|
||||
} else if (arg instanceof String) {
|
||||
return new JavaValue((String) arg);
|
||||
} else if (arg instanceof Integer) {
|
||||
return new JavaValue((Integer) arg);
|
||||
} else if (arg instanceof Long) {
|
||||
return new JavaValue((Long) arg);
|
||||
} else if (arg instanceof Float) {
|
||||
return new JavaValue((Float) arg);
|
||||
} else if (arg instanceof Double) {
|
||||
return new JavaValue((Double) arg);
|
||||
} else if (arg instanceof Boolean) {
|
||||
return new JavaValue((Boolean) arg);
|
||||
} else if (arg instanceof JavaValue) {
|
||||
return (JavaValue) arg;
|
||||
} else if (arg instanceof Object[]) {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (Object o : (Object[]) arg) {
|
||||
jsonArray.put(o);
|
||||
}
|
||||
return new JavaValue(jsonArray);
|
||||
} else {
|
||||
return new JavaValue(String.valueOf(arg));
|
||||
}
|
||||
}
|
||||
|
||||
public static Object toJavaObject(@NonNull Class clz, JSDecoder decoder) throws Exception {
|
||||
if (clz == JSDecoder.class) {
|
||||
return decoder;
|
||||
} else {
|
||||
return toJavaObject(clz, decoder.decode());
|
||||
}
|
||||
}
|
||||
|
||||
public static Object toJavaObject(@NonNull Class clz, JSValue jsValue) throws Exception {
|
||||
if (clz == JSValue.class || JSValue.class.isAssignableFrom(clz)) {
|
||||
return jsValue;
|
||||
} else if (clz == String.class) {
|
||||
return jsValue.asString().value();
|
||||
} else if (clz == boolean.class || clz == Boolean.class) {
|
||||
return jsValue.asBoolean().value();
|
||||
} else if (clz == int.class || clz == Integer.class) {
|
||||
return jsValue.asNumber().toInt();
|
||||
} else if (clz == long.class || clz == Long.class) {
|
||||
return jsValue.asNumber().toLong();
|
||||
} else if (clz == float.class || clz == Float.class) {
|
||||
return jsValue.asNumber().toFloat();
|
||||
} else if (clz == double.class || clz == Double.class) {
|
||||
return jsValue.asNumber().toDouble();
|
||||
} else if (clz.isArray()) {
|
||||
Class elementClass = clz.getComponentType();
|
||||
Object ret;
|
||||
if (jsValue.isArray()) {
|
||||
JSArray jsArray = jsValue.asArray();
|
||||
ret = Array.newInstance(clz, jsArray.size());
|
||||
for (int i = 0; i < jsArray.size(); i++) {
|
||||
Array.set(ret, i, toJavaObject(elementClass, jsArray.get(i)));
|
||||
}
|
||||
} else if (jsValue.isNull()) {
|
||||
ret = Array.newInstance(clz, 0);
|
||||
} else {
|
||||
ret = null;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int sScreenWidthPixels;
|
||||
private static int sScreenHeightPixels;
|
||||
|
||||
public static int getScreenWidth(Context context) {
|
||||
if (context == null) {
|
||||
context = Doric.application();
|
||||
}
|
||||
if (sScreenWidthPixels > 0) {
|
||||
return sScreenWidthPixels;
|
||||
}
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
Display display = manager.getDefaultDisplay();
|
||||
if (display != null) {
|
||||
display.getMetrics(dm);
|
||||
sScreenWidthPixels = dm.widthPixels;
|
||||
sScreenHeightPixels = dm.heightPixels;
|
||||
}
|
||||
return sScreenWidthPixels;
|
||||
}
|
||||
|
||||
public static int getScreenWidth() {
|
||||
return getScreenWidth(null);
|
||||
}
|
||||
|
||||
public static int getScreenHeight(Context context) {
|
||||
if (context == null) {
|
||||
context = Doric.application();
|
||||
}
|
||||
if (sScreenHeightPixels > 0) {
|
||||
return sScreenHeightPixels;
|
||||
}
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
Display display = manager.getDefaultDisplay();
|
||||
if (display != null) {
|
||||
display.getMetrics(dm);
|
||||
sScreenWidthPixels = dm.widthPixels;
|
||||
sScreenHeightPixels = dm.heightPixels;
|
||||
}
|
||||
return sScreenHeightPixels;
|
||||
}
|
||||
|
||||
public static int getScreenHeight() {
|
||||
return getScreenHeight(null);
|
||||
}
|
||||
|
||||
public static float px2dp(int pxValue) {
|
||||
return px2dp(null, pxValue);
|
||||
}
|
||||
|
||||
public static float px2dp(Context context, int pxValue) {
|
||||
if (context == null) {
|
||||
context = Doric.application();
|
||||
}
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return pxValue / scale;
|
||||
}
|
||||
|
||||
public static int dp2px(float dpValue) {
|
||||
return dp2px(null, dpValue);
|
||||
}
|
||||
|
||||
public static int dp2px(Context context, float dipValue) {
|
||||
if (context == null) {
|
||||
context = Doric.application();
|
||||
}
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dipValue * scale + (dipValue > 0 ? 0.5f : -0.5f));
|
||||
}
|
||||
|
||||
|
||||
public static int getStatusBarHeight(Context context) {
|
||||
Class<?> c = null;
|
||||
Object obj = null;
|
||||
Field field = null;
|
||||
int x = 0, sbar = 0;
|
||||
try {
|
||||
c = Class.forName("com.android.internal.R$dimen");
|
||||
obj = c.newInstance();
|
||||
field = c.getField("status_bar_height");
|
||||
x = Integer.parseInt(field.get(obj).toString());
|
||||
sbar = context.getResources().getDimensionPixelSize(x);
|
||||
} catch (Exception E) {
|
||||
E.printStackTrace();
|
||||
}
|
||||
return sbar;
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.utils
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-20
|
||||
*/
|
||||
public enum ThreadMode {
|
||||
UI,
|
||||
JS,
|
||||
INDEPENDENT,
|
||||
}
|
2250
doric-android/doric/src/main/java/pub/doric/widget/HVScrollView.java
Normal file
2250
doric-android/doric/src/main/java/pub/doric/widget/HVScrollView.java
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
5
doric-android/doric/src/main/res/drawable/divider.xml
Normal file
5
doric-android/doric/src/main/res/drawable/divider.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#cccccc" />
|
||||
<size android:height="1dp" />
|
||||
</shape>
|
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</FrameLayout>
|
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<pub.doric.DoricPanel
|
||||
android:id="@+id/doric_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#ffffff"
|
||||
android:layout_marginTop="44dp" />
|
||||
|
||||
<pub.doric.navbar.BaseDoricNavBar
|
||||
android:id="@+id/doric_nav_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</FrameLayout>
|
@@ -0,0 +1,21 @@
|
||||
<?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="vertical"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_msg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginBottom="15dp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edit_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/Theme.Doric.Modal.Prompt.EditText" />
|
||||
</LinearLayout>
|
63
doric-android/doric/src/main/res/layout/doric_navigator.xml
Normal file
63
doric-android/doric/src/main/res/layout/doric_navigator.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/nav_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textSize="16sp">
|
||||
|
||||
</TextView>
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_left"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|left"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:drawableLeft="@drawable/doric_icon_back"
|
||||
android:minWidth="30dp"
|
||||
android:textColor="#000"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_right"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center_vertical|right"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</merge>
|
5
doric-android/doric/src/main/res/values-v21/styles.xml
Normal file
5
doric-android/doric/src/main/res/values-v21/styles.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Doric.Modal" parent="android:Theme.Material.Light.Dialog.Alert" />
|
||||
</resources>
|
3
doric-android/doric/src/main/res/values/strings.xml
Normal file
3
doric-android/doric/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">Doric</string>
|
||||
</resources>
|
15
doric-android/doric/src/main/res/values/styles.xml
Normal file
15
doric-android/doric/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Doric.Modal" parent="android:style/Theme.Holo.Light.Dialog">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="Theme.Doric.Modal.Alert" parent="Theme.Doric.Modal" />
|
||||
|
||||
<style name="Theme.Doric.Modal.Confirm" parent="Theme.Doric.Modal" />
|
||||
|
||||
<style name="Theme.Doric.Modal.Prompt" parent="Theme.Doric.Modal" />
|
||||
|
||||
<style name="Theme.Doric.Modal.Prompt.EditText" parent="Theme.AppCompat.Light" />
|
||||
</resources>
|
||||
|
Reference in New Issue
Block a user