Merge branch 'feature/slider' into 'master'

Feature/slider



See merge request !23
This commit is contained in:
pengfeizhou 2019-11-21 20:17:24 +08:00
commit 32af5a2f77
14 changed files with 408 additions and 79 deletions

View File

@ -17,6 +17,7 @@ package pub.doric;
import android.text.TextUtils; import android.text.TextUtils;
import pub.doric.plugin.NetworkPlugin;
import pub.doric.plugin.ShaderPlugin; import pub.doric.plugin.ShaderPlugin;
import pub.doric.shader.HLayoutNode; import pub.doric.shader.HLayoutNode;
import pub.doric.shader.ImageNode; import pub.doric.shader.ImageNode;
@ -65,6 +66,7 @@ public class DoricRegistry {
public DoricRegistry() { public DoricRegistry() {
this.registerNativePlugin(ShaderPlugin.class); this.registerNativePlugin(ShaderPlugin.class);
this.registerNativePlugin(ModalPlugin.class); this.registerNativePlugin(ModalPlugin.class);
this.registerNativePlugin(NetworkPlugin.class);
this.registerViewNode(RootNode.class); this.registerViewNode(RootNode.class);
this.registerViewNode(TextNode.class); this.registerViewNode(TextNode.class);
this.registerViewNode(ImageNode.class); this.registerViewNode(ImageNode.class);

View File

@ -17,7 +17,6 @@ package pub.doric.plugin;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.text.TextUtils;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;

View File

@ -0,0 +1,116 @@
/*
* 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(name = "request")
public void request(JSDecoder decoder, final DoricPromise promise) {
try {
JSObject requestVal = decoder.decode().asObject();
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()));
}
}
}

View File

@ -8,4 +8,5 @@ export default [
'src/EffectsDemo', 'src/EffectsDemo',
'src/ImageDemo', 'src/ImageDemo',
'src/ModalDemo', 'src/ModalDemo',
'src/NetworkDemo',
] ]

View File

@ -10,7 +10,7 @@ class ImageDemo extends Panel {
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST), layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30, textSize: 30,
textColor: Color.WHITE, textColor: Color.WHITE,
bgColor: colors[1], bgColor: colors[5],
textAlignment: gravity().center(), textAlignment: gravity().center(),
height: 50, height: 50,
}), }),
@ -29,18 +29,6 @@ class ImageDemo extends Panel {
loadCallback: (ret) => { loadCallback: (ret) => {
} }
}), }),
label('WebP'),
image({
imageUrl: "https://misc.aotu.io/ONE-SUNDAY/world_cup_2014_42.webp",
loadCallback: (ret) => {
}
}),
label('Lossy WebP'),
image({
imageUrl: "https://misc.aotu.io/ONE-SUNDAY/world_cup_2014_42_lossy.webp",
loadCallback: (ret) => {
}
}),
label('ScaleToFill'), label('ScaleToFill'),
image({ image({
imageUrl, imageUrl,

34
demo/src/NetworkDemo.ts Normal file
View File

@ -0,0 +1,34 @@
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, Text, scroller, layoutConfig, image, IView, IVLayout, ScaleType, modal, IText, network } from "doric";
import { title, label, colors } from "./utils";
@Entry
class NetworkDemo extends Panel {
build(rootView: Group): void {
scroller(vlayout([
title("Network Demo"),
label('Click me').apply({
width: 200,
height: 50,
bgColor: colors[0],
textSize: 30,
textColor: Color.WHITE,
layoutConfig: layoutConfig().exactly(),
onClick: () => {
network(context).get('https://m.baidu.com').then(
e => {
modal(context).alert(JSON.stringify(e))
}
).catch(e => {
modal(context).toast('Catched:' + JSON.stringify(e))
})
}
} as IText),
]).apply({
layoutConfig: layoutConfig().atmost().h(LayoutSpec.WRAP_CONTENT),
gravity: gravity().center(),
space: 10,
} as IVLayout)).apply({
layoutConfig: layoutConfig().atmost(),
}).in(rootView)
}
}

View File

@ -1,4 +1,4 @@
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, slider, slideItem, image, layoutConfig } from "doric"; import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout, Gravity, hlayout, slider, slideItem, image, layoutConfig, ScaleType } from "doric";
import { colors } from "./utils"; import { colors } from "./utils";
const imageUrls = [ const imageUrls = [
@ -33,7 +33,8 @@ class SliderPanel extends Panel {
renderPage: (idx) => { renderPage: (idx) => {
return slideItem(image({ return slideItem(image({
imageUrl: imageUrls[idx % imageUrls.length], imageUrl: imageUrls[idx % imageUrls.length],
layoutConfig: layoutConfig().w(LayoutSpec.WRAP_CONTENT).h(LayoutSpec.WRAP_CONTENT).a(gravity().center()), scaleType: ScaleType.ScaleAspectFit,
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST).h(LayoutSpec.AT_MOST).a(gravity().center()),
})).also(it => { })).also(it => {
let start = idx let start = idx
it.onClick = () => { it.onClick = () => {

View File

@ -1,4 +1,4 @@
import { Color, text, Stack, Text } from "doric"; import { Color, text, Stack, Text, layoutConfig, LayoutSpec, gravity } from "doric";
export const colors = [ export const colors = [
"#70a1ff", "#70a1ff",
@ -34,3 +34,15 @@ export function boxStr(str: string, idx = 0) {
it.bgColor = colors[idx || 0] it.bgColor = colors[idx || 0]
}) })
} }
export function title(str: string) {
return text({
text: "Network Demo",
layoutConfig: layoutConfig().w(LayoutSpec.AT_MOST),
textSize: 30,
textColor: Color.WHITE,
bgColor: colors[1],
textAlignment: gravity().center(),
height: 50,
})
}

View File

@ -22,6 +22,7 @@
#import "DoricRegistry.h" #import "DoricRegistry.h"
#import "DoricModalPlugin.h" #import "DoricModalPlugin.h"
#import "DoricNetworkPlugin.h"
#import "DoricShaderPlugin.h" #import "DoricShaderPlugin.h"
#import "DoricStackNode.h" #import "DoricStackNode.h"
#import "DoricVLayoutNode.h" #import "DoricVLayoutNode.h"
@ -56,6 +57,7 @@ - (instancetype)init {
- (void)innerRegister { - (void)innerRegister {
[self registerNativePlugin:DoricModalPlugin.class withName:@"modal"]; [self registerNativePlugin:DoricModalPlugin.class withName:@"modal"];
[self registerNativePlugin:DoricNetworkPlugin.class withName:@"network"];
[self registerNativePlugin:DoricShaderPlugin.class withName:@"shader"]; [self registerNativePlugin:DoricShaderPlugin.class withName:@"shader"];
[self registerViewNode:DoricStackNode.class withName:@"Stack"]; [self registerViewNode:DoricStackNode.class withName:@"Stack"];

View File

@ -0,0 +1,24 @@
/*
* 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.
*/
//
// Created by pengfei.zhou on 2019/11/21.
//
#import <Foundation/Foundation.h>
#import "DoricNativePlugin.h"
@interface DoricNetworkPlugin : DoricNativePlugin
@end

View File

@ -0,0 +1,59 @@
/*
* 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.
*/
//
// Created by pengfei.zhou on 2019/11/21.
//
#import "DoricNetworkPlugin.h"
@implementation DoricNetworkPlugin
- (void)request:(NSDictionary *)dic withPromise:(DoricPromise *)promise {
NSString *url = dic[@"url"];
NSString *method = dic[@"method"];
NSDictionary <NSString *, NSString *> *headers = dic[@"headers"];
NSNumber *timeout = dic[@"timeout"];
NSString *data = dic[@"data"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
request.HTTPMethod = method.uppercaseString;
if (timeout) {
request.timeoutInterval = [timeout floatValue] / 1000;
}
if (headers) {
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
[request setValue:obj forHTTPHeaderField:key];
}];
}
if (data) {
[request setHTTPBody:[data dataUsingEncoding:NSUTF8StringEncoding]];
}
[[[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSString *dataStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *resDic = @{
@"status": @(((NSHTTPURLResponse *) response).statusCode),
@"headers": ((NSHTTPURLResponse *) response).allHeaderFields,
@"data": dataStr,
};
[promise resolve:resDic];
} else {
[promise reject:error.description];
}
}] resume];
}
@end

View File

@ -28,4 +28,4 @@ export * from './src/util/gravity'
export * from './src/util/candies' export * from './src/util/candies'
export * from './src/vm/mvvm' export * from './src/vm/mvvm'
export * from './src/runtime/global' export * from './src/runtime/global'
export * from './src/util/modal' export * from './src/util/nativeModules'

View File

@ -1,61 +0,0 @@
/*
* 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.
*/
import { BridgeContext } from "../runtime/global";
import { Gravity } from "./gravity";
export function modal(context: BridgeContext) {
return {
toast: (msg: string, gravity: Gravity = Gravity.Bottom) => {
context.modal.toast({
msg,
gravity: gravity.toModel(),
})
},
alert: (arg: string | {
title: string,
msg: string,
okLabel?: string,
}) => {
if (typeof arg === 'string') {
return context.modal.alert({ msg: arg })
} else {
return context.modal.alert(arg)
}
},
confirm: (arg: string | {
title: string,
msg: string,
okLabel?: string,
cancelLabel?: string,
}) => {
if (typeof arg === 'string') {
return context.modal.confirm({ msg: arg })
} else {
return context.modal.confirm(arg)
}
},
prompt: (arg: {
title?: string,
msg?: string,
okLabel?: string,
cancelLabel?: string,
text?: string,
defaultText?: string,
}) => {
return context.modal.prompt(arg)
},
}
}

View File

@ -0,0 +1,152 @@
/*
* 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.
*/
import { BridgeContext } from "../runtime/global";
import { Gravity } from "./gravity";
export function modal(context: BridgeContext) {
return {
toast: (msg: string, gravity: Gravity = Gravity.Bottom) => {
context.modal.toast({
msg,
gravity: gravity.toModel(),
})
},
alert: (arg: string | {
title: string,
msg: string,
okLabel?: string,
}) => {
if (typeof arg === 'string') {
return context.modal.alert({ msg: arg })
} else {
return context.modal.alert(arg)
}
},
confirm: (arg: string | {
title: string,
msg: string,
okLabel?: string,
cancelLabel?: string,
}) => {
if (typeof arg === 'string') {
return context.modal.confirm({ msg: arg })
} else {
return context.modal.confirm(arg)
}
},
prompt: (arg: {
title?: string,
msg?: string,
okLabel?: string,
cancelLabel?: string,
text?: string,
defaultText?: string,
}) => {
return context.modal.prompt(arg) as Promise<string>
},
}
}
export interface IRequest {
// `url` is the server URL that will be used for the request
url?: string,
// `method` is the request method to be used when making the request
method?: "get" | "post" | "put" | "delete",
// `headers` are custom headers to be sent
headers?: { [index: string]: string }
// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
params?: { [index: string]: string }
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
data?: object | string
// `timeout` specifies the number of milliseconds before the request times out.
// If the request takes longer than `timeout`, the request will be aborted.
timeout?: number, // default is `0` (no timeout)
}
export interface IResponse {
// `data` is the response that was provided by the server
data: any,
// `status` is the HTTP status code from the server response
status: number,
// `headers` the headers that the server responded with
// All header names are lower cased
headers?: { [index: string]: string },
}
function transformRequest(request: IRequest) {
let url = request.url || ""
if (request.params !== undefined) {
const queryStrings = []
for (let key in request.params) {
queryStrings.push(`${key}=${encodeURIComponent(request.params[key])}`)
}
request.url = `${request.url}${url.indexOf('?') >= 0 ? '&' : '?'}${queryStrings.join('&')}`
}
if (typeof request.data === 'object') {
request.data = JSON.stringify(request.data)
}
return request
}
export function network(context: BridgeContext) {
return {
request: (config: IRequest) => {
return context.network.request(transformRequest(config)) as Promise<IResponse>
},
get: (url: string, config?: IRequest) => {
let finalConfig = config
if (finalConfig === undefined) {
finalConfig = {}
}
finalConfig.url = url
finalConfig.method = "get"
return context.network.request(transformRequest(finalConfig)) as Promise<IResponse>
},
post: (url: string, data?: object | string, config?: IRequest) => {
let finalConfig = config
if (finalConfig === undefined) {
finalConfig = {}
}
finalConfig.url = url
finalConfig.method = "post"
if (data !== undefined) {
finalConfig.data = data
}
return context.network.request(transformRequest(finalConfig)) as Promise<IResponse>
},
put: (url: string, data?: object | string, config?: IRequest) => {
let finalConfig = config
if (finalConfig === undefined) {
finalConfig = {}
}
finalConfig.url = url
finalConfig.method = "put"
if (data !== undefined) {
finalConfig.data = data
}
return context.network.request(transformRequest(finalConfig)) as Promise<IResponse>
},
delete: (url: string, data?: object | string, config?: IRequest) => {
let finalConfig = config
if (finalConfig === undefined) {
finalConfig = {}
}
finalConfig.url = url
finalConfig.method = "delete"
return context.network.request(transformRequest(finalConfig)) as Promise<IResponse>
},
}
}