Mirror from github

This commit is contained in:
weixing.jwx 2018-03-13 11:00:50 +08:00
commit cb400c9372
1116 changed files with 274113 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.DS_Store
website

64
GCanvas.podspec Normal file
View File

@ -0,0 +1,64 @@
Pod::Spec.new do |s|
s.name = "GCanvas"
s.version = "0.1.1"
s.summary = "GCanvas Source."
s.description = <<-DESC
A cross-platform fast Canvas render engine.
DESC
s.homepage = 'https://github.com/alibaba/GCanvas'
s.license = {
:type => 'Copyright',
:text => <<-LICENSE
Alibaba-INC copyright
LICENSE
}
s.author = { 'jwxbond' => 'jwxbond@gmail.com' }
s.platform = :ios
s.ios.deployment_target = '7.0'
# s.source = { :path => '.' }
s.source = { :git => "https://github.com/alibaba/GCanvas.git", :tag => s.version }
s.source_files = 'GCanvas/ios/Classes/**/*.{h,m,mm}',
'GCanvas/core/src/GCanvas.{hpp,cpp}',
'GCanvas/core/src/GCanvasManager.{h,cpp}',
'GCanvas/core/src/gcanvas/shaders/*.glsl',
'GCanvas/core/src/gcanvas/GCanvas2dContext.{h,cpp}',
'GCanvas/core/src/gcanvas/GConvert.{h,cpp}',
'GCanvas/core/src/gcanvas/GFillStyle.h',
'GCanvas/core/src/gcanvas/GPath.{h,cpp}',
'GCanvas/core/src/gcanvas/GPoint.{h,cpp}',
'GCanvas/core/src/gcanvas/GShader.{h,cpp}',
'GCanvas/core/src/gcanvas/GShaderManager.{h,cpp}',
'GCanvas/core/src/gcanvas/GTextDefine.h',
'GCanvas/core/src/gcanvas/GTexture.{h,cpp}',
'GCanvas/core/src/gcanvas/GTransform.h',
'GCanvas/core/src/gcanvas/GTriangulate.{h,cpp}',
'GCanvas/core/src/gcanvas/GWebglContext.{h,cpp}',
'GCanvas/core/src/memory/*.h',
'GCanvas/core/src/png/**/*.*',
'GCanvas/core/src/support/DynArray.h',
'GCanvas/core/src/support/Encode.{h,cpp}',
'GCanvas/core/src/support/Lesser.h',
'GCanvas/core/src/support/Log.{h,cpp}',
'GCanvas/core/src/support/Util.{h,cpp}'
s.public_header_files = 'GCanvas/ios/Classes/**/*.h', 'GCanvas/core/src/gcanvas/GTextDefine.h'
s.user_target_xcconfig = { 'FRAMEWORK_SEARCH_PATHS' => "'$(PODS_ROOT)/GCanvas'" }
s.requires_arc = true
s.frameworks = 'Foundation','UIKit','GLKit'
s.library = 'stdc++'
s.pod_target_xcconfig = { 'OTHER_CFLAGS' => '-DIOS' }
end

10
GCanvas/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
local.properties
gradle/
.idea/
.gradle/
*.iml
Pods
Podfile.lock
*.xcworkspace
build/
.externalNativeBuild/

91
GCanvas/README.MD Normal file
View File

@ -0,0 +1,91 @@
# GCanvas
GCanvas is a cross-platform rendering engine for mobile devices developed by Taobao. It is written with C++ based on OpenGL ES, so it can provide high performance 2D/WebGL rendering capabilities for Javascript runtime. It also has browser-like canvas APIs, so it's very convinent and flexiable for use, espcially for web developers.
Supported operating systems are Android 4.0+ (API 14) and iOS 8.0+.
- [Getting Started](#getting-started)
- [Documentation](#documentation)
- [Examples](#examples)
- [Built With](#build-with)
- [Opening Issues](#opening-issues)
- [Contributing](#contributing)
- [License](#license)
## Features
- Cross-platform, support popular iOS and Android.
- High performance, accelerate graphic draw by OpenGL ES.
- Provide javascript runtime, such as [Weex](https://github.com/apache/incubator-weex) and [ReactNative](https://github.com/facebook/react-native/). convenient to use Javascript API like HTML canvas.
- Scalable Architecture, easy to implement a GCanvas bridge by yourself following the guide [Custom Native Bridge](./docs/Guide_Custom_GCanvas_Bridge.md) and [Custom Javascript Bridge](./docs/Guide_JS_Use.md) .
- Small size.
## Introduction
See the [Introduction to GCanvas](./docs/An_Introduction_to_GCanvas.md) for a detailed introduction to GCanvas.
## Getting Started
### Weex
Follow these guides to setup GCanvas in Weex
* [Weex Guide Android](./docs/Guide_Android_Setup_Weex.md)
* [Weex Guide iOS](./docs/Guide_iOS_Setup_Weex.md)
### ReactNative
Follow this guide [ReactNative Guide](./docs/Guide_Setup_ReactNative.md) to setup GCanvas in ReactNative.
### Javascript
Follow this [Javascript Guide](./docs/Guide_JS_Use.md) to setup Javascript runtime environment.
## Documentation
GCanvas has browser-like canvas APIs, so almost all of the APIs are exactly same as HTML5 canvas. At this moment, we have already supported 90% of 2D APIs and 99% of WebGL APIs. You can find out those informations in [APIs document](./docs/APIs.md).
## Examples
We take [Weex](https://github.com/apache/incubator-weex) as example, code snippet of context 2d using `GCanvas`.
```javascript
import { enable, WeexBridge, Image as GImage } from "../../../../js/src/index.js";
var gcanvas = enable(this.$refs.canvas_holder, {bridge: WeexBridge});
var ctx = gcanvas.getContext("2d");
//rect
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 100, 100);
//rect
ctx.fillStyle = 'black';
ctx.fillRect(100, 100, 100, 100);
ctx.fillRect(25, 210, 700, 5);
//circle
ctx.arc(450, 200, 100, 0, Math.PI * 2, true);
ctx.fill();
var image = new GImage();
image.src = 'https://gw.alicdn.com/tfs/TB1KwRTlh6I8KJjy0FgXXXXzVXa-225-75.png';
image.onload = function(){
ctx.drawImage(image, 100, 300);
};
```
## Built With
* [Freetype](https://www.freetype.org/) - Used for font rendering on Android
## Open Issues
If you encounter a bug with GCanvas we would like to hear about it. Search the [existing issues](https://github.com/alibaba/GCanvas/issues) and try to make sure your problem doesnt already exist before opening a new issue. Its helpful if you include the version of GCanvas and OS youre using. Please include a stack trace and reduced repro case when appropriate, too.
## Contributing
Please read [CONTRIBUTING](./docs/Contributing.md) for details on our code of conduct, and the process for submitting pull requests to us.
## Authors
GCanvas Open Source Team
## License
This project is licensed under the Apache License - see the [LICENSE](./docs/LICENSE.md) file for details

6
GCanvas/android/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.iml
.idea
.gradle
.externalNativeBuild
local.properties
build

View File

@ -0,0 +1,2 @@
/build
*.iml

View File

@ -0,0 +1,52 @@
apply plugin: 'com.android.library'
group = rootProject.extensions.groups.Adapter
version = "1.0.0"
buildscript {
repositories {
//local repository(${user.home}/.m2/repository)
jcenter()
mavenCentral()
maven {
url 'https://maven.google.com/'
name 'Google'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3'
}
}
android {
compileSdkVersion rootProject.extensions.sdks.CompileSDK
buildToolsVersion rootProject.extensions.sdks.BuildTool
defaultConfig {
minSdkVersion rootProject.extensions.sdks.MinSDK
targetSdkVersion rootProject.extensions.sdks.TargetSDK
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
project.ext.id = rootProject.extensions.artifacts.ImageAdapter
project.ext.meta.labels = ['Weex', 'ReactNative', 'GPU', 'OpenGL', 'GCanvas', "canvas", 'Adapter']
project.ext.meta.description = 'This package defines how to load images into bitmaps in GCanvas project.'
apply from: "${rootProject.projectDir}/publish.gradle"

View File

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

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.android.gcanvas.plugin.adapter" />

View File

@ -0,0 +1,42 @@
package com.taobao.gcanvas.adapters.img;
import android.content.Context;
import android.graphics.Bitmap;
/**
* An Image loader that loads url into bitmap.
* Will replace it with C++ image library.
* @author ertong
*/
public interface IGImageLoader {
/**
* Load specified url into bitmap asynchronously, and send results to specified callback.
* Will return if any parameter is invalid(url is empty / null, or context/callback is null).
* @param context context for potential use
* @param url url to load
* @param callback callback receiving result
*/
void load(final Context context, final String url, final ImageCallback callback);
/**
* Callback receiving loading result.
*/
interface ImageCallback {
/**
* Success callback with loaded bitmap
* @param bitmap
*/
void onSuccess(Bitmap bitmap);
/**
* Error callback with error message, maybe null.
* @param error
*/
void onFail(Object error);
/**
* Request was canceled.
*/
void onCancel();
}
}

View File

@ -0,0 +1,2 @@
/build
*.iml

View File

@ -0,0 +1,41 @@
apply plugin: 'com.android.library'
group = rootProject.extensions.groups.Adapter
version = "1.0.0"
android {
compileSdkVersion rootProject.extensions.sdks.CompileSDK
buildToolsVersion rootProject.extensions.sdks.BuildTool
defaultConfig {
minSdkVersion rootProject.extensions.sdks.MinSDK
targetSdkVersion rootProject.extensions.sdks.TargetSDK
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
provided "com.android.support:appcompat-v7:${rootProject.extensions.sdks.Support}"
provided "com.facebook.fresco:fresco:${rootProject.extensions.sdks.Fresco}"
compile project(':android:adapters:bridge_adapter')
testCompile "junit:junit:${rootProject.extensions.sdks.JUnit}"
}
project.ext.id = rootProject.extensions.artifacts.FrescoAdapter
project.ext.meta.labels = ['Weex', 'ReactNative', 'GPU', 'OpenGL', 'GCanvas', "canvas", 'Fresco', 'Image Loader']
project.ext.meta.description = 'This package is the Fresco implemation of GCanvas image adapter.'
apply from: "${rootProject.projectDir}/publish.gradle"

View File

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

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.android.gcanvas.adapters.img.picasso" />

View File

@ -0,0 +1,149 @@
package com.taobao.gcanvas.adapters.img.impl.fresco;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.datasource.DataSubscriber;
import com.facebook.drawee.backends.pipeline.DraweeConfig;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.common.RotationOptions;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.image.CloseableBitmap;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.taobao.gcanvas.adapters.img.IGImageLoader;
/**
* Image loader implemented with Fresco.
*
* @author ertong
*/
public class GCanvasFrescoImageLoader implements IGImageLoader {
private static final String TAG = GCanvasFrescoImageLoader.class.getSimpleName();
private ImagePipelineConfig imagePipelineConfig;
private DraweeConfig draweeConfig;
private Handler mHandler;
public GCanvasFrescoImageLoader() {
this(null, null);
}
public GCanvasFrescoImageLoader(DraweeConfig draweeConfig) {
this(null, draweeConfig);
}
public GCanvasFrescoImageLoader(ImagePipelineConfig imagePipelineConfig) {
this(imagePipelineConfig, null);
}
public GCanvasFrescoImageLoader(ImagePipelineConfig imagePipelineConfig, DraweeConfig draweeConfig) {
this.imagePipelineConfig = imagePipelineConfig;
this.draweeConfig = draweeConfig;
}
@Override
public void load(Context context, String url, final ImageCallback callback) {
if (TextUtils.isEmpty(url)) {
Log.i(TAG, "url is null or empty");
return;
}
if (!Fresco.hasBeenInitialized()) {
Log.i(TAG, "fresco not init, do initialization");
Fresco.initialize(context, imagePipelineConfig, draweeConfig);
}
ImageLoadRunnable runnable = new ImageLoadRunnable(context, url, callback);
if (Looper.myLooper() != Looper.getMainLooper()) {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
}
mHandler.post(runnable);
} else {
runnable.run();
}
}
private static class ImageLoadRunnable implements Runnable {
private Context context;
private String url;
private final ImageCallback callback;
public ImageLoadRunnable(Context context, String url, ImageCallback callback) {
this.context = context;
this.url = url;
this.callback = callback;
}
@Override
public void run() {
Uri uri = Uri.parse(url);
ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
builder.setAutoRotateEnabled(false);
builder.setRotationOptions(RotationOptions.disableRotation());
ImageRequest request = builder.build();
DataSource<CloseableReference<CloseableImage>> ds;
if (Fresco.getImagePipeline().isInBitmapMemoryCache(uri)) {
ds = Fresco.getImagePipeline().fetchImageFromBitmapCache(request, this);
} else {
ds = Fresco.getImagePipeline().fetchDecodedImage(request, this);
}
ds.subscribe(new DataSubscriber<CloseableReference<CloseableImage>>() {
@Override
public void onNewResult(DataSource<CloseableReference<CloseableImage>> dataSource) {
if (!dataSource.isFinished()) {
return;
}
CloseableReference<CloseableImage> ref = dataSource.getResult();
if (ref != null) {
try {
CloseableImage result = ref.get();
if (result instanceof CloseableBitmap) {
callback.onSuccess(((CloseableBitmap) result).getUnderlyingBitmap());
} else {
byte[] array = new byte[result.getSizeInBytes()];
callback.onSuccess(BitmapFactory.decodeByteArray(array, 0, array.length));
}
} catch (Throwable throwable) {
callback.onFail(throwable.getMessage());
} finally {
CloseableReference.closeSafely(ref);
}
}
}
@Override
public void onFailure(DataSource<CloseableReference<CloseableImage>> dataSource) {
String msg = dataSource.getFailureCause() != null ? dataSource.getFailureCause().getMessage() : null;
callback.onFail(msg);
}
@Override
public void onCancellation(DataSource<CloseableReference<CloseableImage>> dataSource) {
callback.onCancel();
}
@Override
public void onProgressUpdate(DataSource<CloseableReference<CloseableImage>> dataSource) {
}
}, CallerThreadExecutor.getInstance());
}
}
}

View File

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

View File

@ -0,0 +1,2 @@
/build
*.iml

View File

@ -0,0 +1,38 @@
apply plugin: 'com.android.library'
group = rootProject.extensions.groups.Adapter
version = "1.0.0"
android {
compileSdkVersion rootProject.extensions.sdks.CompileSDK
defaultConfig {
minSdkVersion rootProject.extensions.sdks.MinSDK
targetSdkVersion rootProject.extensions.sdks.TargetSDK
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
provided "com.squareup.picasso:picasso:${rootProject.extensions.sdks.Picasso}"
compile project(':android:adapters:bridge_adapter')
}
project.ext.id = rootProject.extensions.artifacts.PicassoAdapter
project.ext.meta.labels = ['Weex', 'ReactNative', 'GPU', 'OpenGL', 'GCanvas', "canvas", 'Picasso', 'Image Loader']
project.ext.meta.description = 'This package is the Picasso implemation of GCanvas image adapter.'
apply from: "${rootProject.projectDir}/publish.gradle"

View File

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

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.android.gcanvas.adapters.img.picasso" />

View File

@ -0,0 +1,83 @@
package com.taobao.android.gcanvas.adapters.img.impl.picasso;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import com.taobao.gcanvas.adapters.img.IGImageLoader;
import java.util.HashMap;
/**
* Image loader implemented with Picasso.
* @author ertong
*/
public class GCanvasPicassoImageLoader implements IGImageLoader {
private static final String TAG = GCanvasPicassoImageLoader.class.getSimpleName();
private Handler mMainHandler = null;
private static HashMap<String, Target> sTargetTrigger = new HashMap<>();
private ImageTarget mCurrentTarget;
@Override
public void load(final Context context, final String url, final ImageCallback callback) {
mCurrentTarget = new ImageTarget(url, callback);
sTargetTrigger.put(url, mCurrentTarget);
if (Looper.myLooper() != Looper.getMainLooper()) {
if (null == mMainHandler) {
mMainHandler = new Handler(Looper.getMainLooper());
}
mMainHandler.post(new Runnable() {
@Override
public void run() {
Picasso.with(context).load(url).into(mCurrentTarget);
}
});
} else {
Picasso.with(context).load(url).into(mCurrentTarget);
}
}
private class ImageTarget implements Target {
private String mUrl;
private ImageCallback mCallback;
private Bitmap mBitmap;
public ImageTarget(String url, ImageCallback callback) {
this.mUrl = url;
this.mCallback = callback;
}
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
mBitmap = bitmap;
mCallback.onSuccess(mBitmap);
mCallback = null;
mBitmap = null;
sTargetTrigger.remove(mUrl);
mUrl = null;
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
mCallback.onFail(null);
mCallback = null;
mBitmap = null;
sTargetTrigger.remove(mUrl);
mUrl = null;
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
}
}

View File

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

View File

@ -0,0 +1,2 @@
/build
*.iml

View File

@ -0,0 +1,56 @@
apply plugin: 'com.android.library'
group = rootProject.extensions.groups.Bridge
version = "1.0.0"
buildscript {
repositories {
//local repository(${user.home}/.m2/repository)
mavenCentral()
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
android {
compileSdkVersion rootProject.extensions.sdks.CompileSDK
buildToolsVersion rootProject.extensions.sdks.BuildTool
defaultConfig {
minSdkVersion rootProject.extensions.sdks.MinSDK
targetSdkVersion rootProject.extensions.sdks.TargetSDK
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':android:adapters:bridge_adapter')
compile project(':android:gcanvas_library')
}
project.ext.id = rootProject.extensions.artifacts.BridgeSpec
project.ext.meta.labels = ['Weex', 'ReactNative', 'GPU', 'OpenGL', 'GCanvas', "canvas", 'Customize Own Bridge', 'Bridge Specification']
project.ext.meta.description = 'This package defines native interfaces that a customize GCanvas bridge must implements.'
apply from: "${rootProject.projectDir}/publish.gradle"

View File

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

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.gcanvas.spec" />

View File

@ -0,0 +1,15 @@
package com.taobao.gcanvas.bridges.spec;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation means the specified code are only used by Android.
* @author ertong
*/
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface AndroidOnly {
}

View File

@ -0,0 +1,62 @@
package com.taobao.gcanvas.bridges.spec.bridge;
/**
* Communications between Javascript runtime and native are container dependent. Different environment has different standards.
* This interface provides array data structure for passing parameters from native to javascript.
* @author ertong
*/
public interface IJSCallbackArray {
/**
* Return size of this array.
* @return 0 if array is empty.
*/
int size();
/**
* Test element at specified index if it is null.
* @param index
* @return true if element at index is null.
*/
boolean isNull(int index);
/**
* Get specified index as boolean. Throw ClassCastException if element at specified index is not type of boolean
* @param index
* @return
*/
boolean getBoolean(int index);
/**
* Get specified index as double. Throw ClassCastException if element at specified index is not type of double
* @param index
* @return
*/
double getDouble(int index);
/**
* Get specified index as integer. Throw ClassCastException if element at specified index is not type of integer
* @param index
* @return
*/
int getInt(int index);
/**
* Get specified index as String.
* @param index
* @return
*/
String getString(int index);
/**
* Get specified index as IJSCallbackArray instance.Throw ClassCastException if element at specified index is not type of IJSCallbackArray
* @param index
* @return
*/
IJSCallbackArray getArray(int index);
IJSCallbackMap getMap(int index);
IJSCallbackType getType(int index);
void pushNull();
void pushBoolean(boolean value);
void pushDouble(double value);
void pushInt(int value);
void pushString(String value);
void pushArray(IJSCallbackArray array);
void pushMap(IJSCallbackMap map);
}

View File

@ -0,0 +1,11 @@
package com.taobao.gcanvas.bridges.spec.bridge;
/**
* Factory for creating Callback data structures.
* @author ertong
*/
public interface IJSCallbackDataFactory {
IJSCallbackArray createJSCallbackArray();
IJSCallbackMap createJSCallbackMap();
}

View File

@ -0,0 +1,43 @@
package com.taobao.gcanvas.bridges.spec.bridge;
/**
* <p>Communications between Javascript runtime and native are container dependent. Different environment has different standards.</p>
* <p>This interface provides an map that allows typed access to its members. Used to pass parameters from native
* to javascript.</p>
* @author ertong
*/
public interface IJSCallbackMap {
boolean hasKey(String name);
boolean isNull(String name);
boolean getBoolean(String name);
double getDouble(String name);
int getInt(String name);
String getString(String name);
IJSCallbackArray getArray(String name);
IJSCallbackMap getMap(String name);
IJSCallbackType getType(String name);
IJSCallbackMapKeySetIterator keySetIterator();
void putNull(String key);
void putBoolean(String key, boolean value);
void putDouble(String key, double value);
void putInt(String key, int value);
void putString(String key, String value);
void putArray(String key, IJSCallbackArray value);
void putMap(String key, IJSCallbackMap value);
/**
* Merge specified map's contents into this map.
* @param source
*/
void merge(IJSCallbackMap source);
/**
* Iterator of this map.
*/
interface IJSCallbackMapKeySetIterator {
boolean hasNextKey();
String nextKey();
}
}

View File

@ -0,0 +1,16 @@
package com.taobao.gcanvas.bridges.spec.bridge;
/**
* <p>Communications between Javascript runtime and native are container dependent. Different environment has different standards.</p>
* <p>This enum defines supported data types.</p>
* @author ertong
*/
public enum IJSCallbackType {
Null,
Boolean,
Number,
String,
Map,
Array,
}

View File

@ -0,0 +1,504 @@
package com.taobao.gcanvas.bridges.spec.module;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.text.TextUtils;
import android.util.Log;
import com.taobao.gcanvas.GCanvasJNI;
import com.taobao.gcanvas.adapters.img.IGImageLoader;
import com.taobao.gcanvas.bridges.spec.bridge.IJSCallbackDataFactory;
import com.taobao.gcanvas.bridges.spec.bridge.IJSCallbackMap;
import com.taobao.gcanvas.util.GLog;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Abstract bridge module.
* @author ertong
*/
public abstract class AbsGBridgeModule<JSCallback> implements IGBridgeModule<JSCallback> {
public static final String TAG = AbsGBridgeModule.class.getSimpleName();
protected HashMap<String, GImageLoadInfo> mImageIdCache = new HashMap<>();
protected HashMap<String, ArrayList<JSCallback>> mCallbacks = new HashMap<>();
protected IGImageLoader mImageLoader;
public void setImageLoader(IGImageLoader mImageLoader) {
this.mImageLoader = mImageLoader;
}
private abstract static class AbsImageCallback implements IGImageLoader.ImageCallback {
private final Object sync = new Object();
final AtomicBoolean finished = new AtomicBoolean(false);
@Override
public void onSuccess(Bitmap bitmap) {
doSuccessAction(bitmap);
synchronized (sync) {
sync.notifyAll();
finished.set(true);
}
}
@Override
public void onFail(Object error) {
synchronized (sync) {
sync.notifyAll();
finished.set(true);
}
}
@Override
public void onCancel() {
synchronized (sync) {
sync.notifyAll();
finished.set(true);
}
}
protected void waitTillFinish() {
synchronized (sync) {
while (!finished.get()) {
try {
sync.wait();
} catch (InterruptedException e) {
break;
}
}
}
}
protected abstract void doSuccessAction(Bitmap bitmap);
}
@Override
public void bindImageTexture(final String canvasId, final String src, final int id, final JSCallback callback) {
if (!TextUtils.isEmpty(src)) {
try {
if (src.startsWith("data:image")) {
Bitmap bmp = handleBase64Texture(src.substring(src.indexOf("base64,") + "base64,".length()));
if (bmp != null) {
GCanvasJNI.bindTexture(canvasId, bmp, 0, GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE);
} else {
GLog.d("decode base64 texture failed,bitmap is null.");
}
} else {
final IJSCallbackMap callbackMap = getDataFactory().createJSCallbackMap();
AbsImageCallback imgCb = new AbsImageCallback() {
@Override
protected void doSuccessAction(Bitmap bitmap) {
if (null != bitmap) {
GCanvasJNI.bindTexture(canvasId, bitmap, id, GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE);
} else {
GLog.d("bitmap is null in teximage2D.");
}
if (null != callback && bitmap != null) {
callbackMap.putString("url", src);
callbackMap.putInt("width", bitmap.getWidth());
callbackMap.putInt("height", bitmap.getHeight());
}
}
};
mImageLoader.load(getContext(), src, imgCb);
imgCb.waitTillFinish();
invokeCallback(callback, callbackMap);
}
} catch (Throwable e) {
GLog.e(TAG, e.getMessage(), e);
}
}
}
@Override
public void setLogLevel(int level) {
String logLevel = "debug";
// 0-debug;1-info;2-warn;3-error;
try {
switch (level) {
case 0:
logLevel = "debug";
break;
case 1:
logLevel = "info";
break;
case 2:
logLevel = "warn";
break;
case 3:
logLevel = "error";
break;
}
} catch (NumberFormatException ex) {
}
GCanvasJNI.setLogLevel(logLevel);
}
@Override
public void texSubImage2D(final String canvasid, final int target, final int level, final int xoffset, final int yoffset,
final int format, final int type, String path) {
if (!TextUtils.isEmpty(path)) {
try {
if (path.startsWith("data:image")) {
Bitmap bmp = handleBase64Texture(path.substring(path.indexOf("base64," + "base64,".length())));
if (bmp != null) {
GCanvasJNI.texSubImage2D(canvasid, bmp, 0, target, level, xoffset, yoffset, format, type);
} else {
GLog.d("[texSubImage2D] decode base64 texture failed,bitmap is null.");
}
} else {
AbsImageCallback imgCb = new AbsImageCallback() {
@Override
protected void doSuccessAction(Bitmap bitmap) {
if (null != bitmap) {
GLog.d("[texSubImage2D] start to bindtexture in 3dmodule.");
GCanvasJNI.texSubImage2D(canvasid, bitmap, 0, target, level, xoffset, yoffset, format, type);
} else {
GLog.d("[texSubImage2D] bitmap is null.");
}
}
};
mImageLoader.load(getContext(), path, imgCb);
imgCb.waitTillFinish();
}
} catch (Throwable e) {
GLog.e(TAG, e.getMessage(), e);
}
}
}
@Override
public void texImage2D(final String canvasId, final int target, final int level, final int internalformat,
final int format, final int type, final String path) {
if (!TextUtils.isEmpty(path)) {
try {
if (path.startsWith("data:image")) {
Bitmap bmp = handleBase64Texture(path.substring(path.indexOf("base64,") + "base64,".length()));
if (bmp != null) {
GCanvasJNI.bindTexture(canvasId, bmp, 0, target, level, internalformat, format, type);
} else {
GLog.d("decode base64 texture failed,bitmap is null.");
}
} else {
AbsImageCallback imgCb = new AbsImageCallback() {
@Override
protected void doSuccessAction(Bitmap bitmap) {
if (null != bitmap) {
GCanvasJNI.bindTexture(canvasId, bitmap, 0, target, level, internalformat, format, type);
} else {
GLog.d("bitmap is null in teximage2D.");
}
}
};
mImageLoader.load(getContext(), path, imgCb);
imgCb.waitTillFinish();
}
} catch (Throwable e) {
GLog.e(TAG, e.getMessage(), e);
}
}
}
@Override
public void preLoadImage(JSONArray arrayPara, final JSCallback callback) {
IJSCallbackMap result = getDataFactory().createJSCallbackMap();
if (null == arrayPara || arrayPara.length() != 2) {
result.putString("error", "invalid input param. specify an json array which length is 2, and index 0 is url to load, index 1 is image id.");
invokeCallback(callback, result);
return;
}
String url;
int imageId;
try {
url = arrayPara.getString(0);
imageId = arrayPara.getInt(1);
} catch (JSONException e) {
result.putString("error", "invalid input param. specify an json array which length is 2, and index 0 is url to load, index 1 is image id.");
GLog.d(TAG, "error parse preload image data:" + e.getMessage());
invokeCallback(callback, result);
return;
}
if (TextUtils.isEmpty(url)) {
result.putString("error", "invalid input param. specify an json array which length is 2, and index 0 is url to load, index 1 is image id.");
return;
}
try {
final IJSCallbackMap resultMap = getDataFactory().createJSCallbackMap();
if (url.startsWith("data:image")) {
Bitmap bmp = handleBase64Texture(url.substring(url.indexOf("base64,") + "base64,".length()));
if (bmp != null) {
resultMap.putInt("id", imageId);
resultMap.putString("url", url);
resultMap.putInt("width", bmp.getWidth());
resultMap.putInt("height", bmp.getHeight());
} else {
resultMap.putString("error", "process base64 failed,url=" + url);
}
invokeCallback(callback, resultMap);
return;
}
GImageLoadInfo imgInfo = mImageIdCache.get(url);
if (null == imgInfo) {
imgInfo = new GImageLoadInfo();
mImageIdCache.put(url, imgInfo);
}
if (imgInfo.status.get() == GImageLoadInfo.IDLE) {
imgInfo.status.set(GImageLoadInfo.LOADING);
imgInfo.id = imageId;
ArrayList<JSCallback> callbacks = mCallbacks.get(url);
if (null == callbacks) {
callbacks = new ArrayList<>();
mCallbacks.put(url, callbacks);
}
callbacks.add(callback);
final int imageIdFinal = imageId;
final String urlFinal = url;
mImageLoader.load(getContext(), url, new IGImageLoader.ImageCallback() {
@Override
public void onSuccess(Bitmap bitmap) {
if (null != bitmap) {
GImageLoadInfo imageInfo = mImageIdCache.get(urlFinal);
imageInfo.width = bitmap.getWidth();
imageInfo.height = bitmap.getHeight();
resultMap.putInt("id", imageIdFinal);
resultMap.putString("url", urlFinal);
resultMap.putInt("width", imageInfo.width);
resultMap.putInt("height", imageInfo.height);
imageInfo.status.set(GImageLoadInfo.LOADED);
try {
ArrayList<JSCallback> callbackList = mCallbacks.remove(urlFinal);
if (null != callbackList) {
for (JSCallback callback : callbackList) {
invokeCallback(callback, resultMap);
}
}
} catch (Throwable throwable) {
imageInfo.status.set(GImageLoadInfo.IDLE);
invokeCallback(callback, resultMap);
}
} else {
resultMap.putString("error", "bitmap load failed");
try {
ArrayList<JSCallback> callbackList = mCallbacks.remove(urlFinal);
if (null != callbackList) {
for (JSCallback callback : callbackList) {
invokeCallback(callback, resultMap);
}
}
} catch (Throwable throwable) {
invokeCallback(callback, resultMap);
}
}
}
@Override
public void onFail(Object error) {
resultMap.putString("error", "bitmap load failed");
try {
ArrayList<JSCallback> callbackList = mCallbacks.remove(urlFinal);
if (null != callbackList) {
for (JSCallback callback : callbackList) {
invokeCallback(callback, resultMap);
}
}
} catch (Throwable throwable) {
invokeCallback(callback, resultMap);
}
}
@Override
public void onCancel() {
}
});
} else if (GImageLoadInfo.LOADING == imgInfo.status.get()) {
// if same url is on loading, add it to callback lists.
ArrayList<JSCallback> callbacks = mCallbacks.get(url);
if (null == callbacks) {
callbacks = new ArrayList<>();
mCallbacks.put(url, callbacks);
}
callbacks.add(callback);
} else if (GImageLoadInfo.LOADED == imgInfo.status.get()) {
resultMap.putInt("id", imageId);
resultMap.putString("url", url);
resultMap.putInt("width", imgInfo.width);
resultMap.putInt("height", imgInfo.height);
ArrayList<JSCallback> callbackList = mCallbacks.remove(url);
if (null != callbackList) {
for (JSCallback cb : callbackList) {
invokeCallback(callback, resultMap);
}
}
invokeCallback(callback, resultMap);
}
} catch (Throwable e) {
Log.e(TAG, e.getMessage(), e);
}
}
public abstract Context getContext();
/**
* invoke javascript callback
* @param callback
* @param args
*/
protected abstract void invokeCallback(JSCallback callback, Object args);
// TODO may have performance issues. DO NOT recommend.
public Bitmap handleBase64Texture(String url) {
try {
byte[] decodedBytes = decode(url.getBytes());
Bitmap bmp = BitmapFactory.decodeByteArray(decodedBytes, 0,
decodedBytes.length);
return bmp;
} catch (Throwable e) {
Log.e(TAG, "error in processing base64Texture,error=" + e);
}
return null;
}
public static byte[] decode(byte[] in) {
return decode(in, in.length);
}
public static byte[] decode(byte[] in, int len) {
// approximate output length
int length = len / 4 * 3;
// return an empty array on empty or short input without padding
if (length == 0) {
return new byte[0];
}
// temporary array
byte[] out = new byte[length];
// number of padding characters ('=')
int pad = 0;
byte chr;
// compute the number of the padding characters
// and adjust the length of the input
for (; ; len--) {
chr = in[len - 1];
// skip the neutral characters
if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) {
continue;
}
if (chr == '=') {
pad++;
} else {
break;
}
}
// index in the output array
int outIndex = 0;
// index in the input array
int inIndex = 0;
// holds the value of the input character
int bits = 0;
// holds the value of the input quantum
int quantum = 0;
for (int i = 0; i < len; i++) {
chr = in[i];
// skip the neutral characters
if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) {
continue;
}
if ((chr >= 'A') && (chr <= 'Z')) {
// char ASCII value
// A 65 0
// Z 90 25 (ASCII - 65)
bits = chr - 65;
} else if ((chr >= 'a') && (chr <= 'z')) {
// char ASCII value
// a 97 26
// z 122 51 (ASCII - 71)
bits = chr - 71;
} else if ((chr >= '0') && (chr <= '9')) {
// char ASCII value
// 0 48 52
// 9 57 61 (ASCII + 4)
bits = chr + 4;
} else if (chr == '+') {
bits = 62;
} else if (chr == '/') {
bits = 63;
} else {
return null;
}
// append the value to the quantum
quantum = (quantum << 6) | (byte) bits;
if (inIndex % 4 == 3) {
// 4 characters were read, so make the output:
out[outIndex++] = (byte) (quantum >> 16);
out[outIndex++] = (byte) (quantum >> 8);
out[outIndex++] = (byte) quantum;
}
inIndex++;
}
if (pad > 0) {
// adjust the quantum value according to the padding
quantum = quantum << (6 * pad);
// make output
out[outIndex++] = (byte) (quantum >> 16);
if (pad == 1) {
out[outIndex++] = (byte) (quantum >> 8);
}
}
// create the resulting array
byte[] result = new byte[outIndex];
System.arraycopy(out, 0, result, 0, outIndex);
return result;
}
protected abstract IJSCallbackDataFactory getDataFactory();
}

View File

@ -0,0 +1,30 @@
package com.taobao.gcanvas.bridges.spec.module;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Class for recording image loading status and basic information.
* @author ertong
*/
public class GImageLoadInfo {
/**
* Image has not loaded yet.
*/
public static final int IDLE = -1;
/**
* Image is on loading.
*/
public static final int LOADING = 0x100;
/**
* Image has been successfully loaded.
*/
public static final int LOADED = 0x200;
public int width;
public int height;
public int id;
/**
* current load status
*/
public AtomicInteger status = new AtomicInteger(IDLE);
}

View File

@ -0,0 +1,123 @@
package com.taobao.gcanvas.bridges.spec.module;
import com.taobao.gcanvas.bridges.spec.AndroidOnly;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* <b>Bridge interface for GCanvas bridge layer.</b>
* <p>The bridge is the environment which provides Javascript execution functionality, such as ReactNative, Weex, WebView, and so on.
* We need such functionality to connect between GCanvas C++ Render Layer and Javascript runtime. That's why "bridge" comes from.
* </p>
* <p>However, on other view, GCanvas bridge is a plugin for Javascript runtime container, so we also use "plugin" to describe these bridges.</p>
* <p>This class is for bridge extension in case of situation that you need your own bridge.</p>
* @author ertong
*/
public interface IGBridgeModule<JSCallback> {
/**
* Canvas context type
*/
enum ContextType {
/**
* "canvas"
**/
_2D(0),
/**
* "webgl"
*/
_3D(1);
private int value;
ContextType(int value) {
this.value = value;
}
public int value() {
return value;
}
}
/**
* Enable this bridge module, tell native view prepare for rendering.
* This will build relationship between GCanvas.js and native view.
* @param data
* @return String "true" if enable succeed, otherwise "false"
*/
String enable(JSONObject data);
/**
* bind the specified image to the specified canvas's OpenGL environment.
* @param canvasId
* @param src
* @param id image id, a unique id that used for tracking cache.
* @param callback
*/
void bindImageTexture(String canvasId, String src, int id, JSCallback callback);
/**
* Set the context type for specified GCanvas.
* @param canvasId
* @param type
*/
void setContextType(String canvasId, ContextType type);
/**
* Set log level for C++ renderer.
* @param level 0-debug, 1-info, 2-warn, 3-error
*/
void setLogLevel(int level);
/**
* Load texture sub image for webgl
* @param canvasid
* @param target
* @param level
* @param xoffset
* @param yoffset
* @param format
* @param type
* @param path
*/
@AndroidOnly
void texSubImage2D(final String canvasid, final int target, final int level, final int xoffset, final int yoffset, final int format, final int type, String path);
/**
*
* @param canvasId
* @param target
* @param level
* @param internalformat
* @param format
* @param type
* @param path
*/
@AndroidOnly
void texImage2D(final String canvasId, final int target, final int level, final int internalformat, final int format, final int type, String path);
/**
* <p>Load specified image into bitmap cache, send result to callback. </p>
* <p>Result is some map-like data. If failed, it contains "error" key, otherwise succeed.</p>
* @param imageInfo index 0 is url, index 1 is image id
* @param success callback to handle result
*/
void preLoadImage(JSONArray imageInfo, final JSCallback success);
/**
*
* @param canvasId
* @param ratio
*/
void setDevicePixelRatio(String canvasId, double ratio);
/**
* Send render command to specified canvas.
* @param cmd
* @param canvasId
*/
void render(String cmd, String canvasId);
}

View File

@ -0,0 +1,2 @@
/build
*.iml

View File

@ -0,0 +1,18 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.gcanvas"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="23" />
<uses-feature android:glEsVersion="0x00020000" />
<!-- android:allowBackup="true" -->
<!-- android:icon="@drawable/ic_launcher" -->
<!-- android:label="@string/app_name" -->
<!-- android:theme="@style/AppTheme" -->
<application>
</application>
</manifest>

View File

@ -0,0 +1,84 @@
apply plugin: 'com.android.library'
group = rootProject.extensions.groups.Main
version = "0.0.1"
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':core')
}
buildscript {
repositories {
//local repository(${user.home}/.m2/repository)
mavenCentral()
jcenter()
maven {
url 'https://maven.google.com/'
name 'Google'
}
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
android {
compileSdkVersion rootProject.extensions.sdks.CompileSDK
buildToolsVersion rootProject.extensions.sdks.BuildTool
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
jniLibs.srcDirs = ['libs']
}
}
lintOptions {
abortOnError false
}
buildTypes {
debug {
debuggable true
}
release {
debuggable false
}
}
defaultConfig {
consumerProguardFiles 'proguard-rules.txt'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
project.ext.id = rootProject.extensions.artifacts.Core
project.ext.meta.labels = ['Weex', 'ReactNative', 'GPU', 'OpenGL', 'GCanvas', "canvas", 'WebGL', 'Rendering', 'High Performance']
project.ext.meta.description = 'This package defines GCanvas main library.'
apply from: "${rootProject.projectDir}/publish.gradle"
android.libraryVariants.all { type ->
task("prepareSo${type.name.capitalize()}") {
copy {
from("${rootProject.projectDir}/core/build/intermediates/ndkBuild/${type.name}/obj/local/armeabi/") {
include "*.so"
}
into 'libs/armeabi'
}
}
project.getTasksByName("assemble${type.name.capitalize()}", true).any { task ->
task.dependsOn("prepareSo${type.name.capitalize()}")
task.doLast {
delete 'libs/armeabi'
}
}
}

View File

@ -0,0 +1,9 @@
-keep class com.taobao.gcanvas.GCanvasJNI {
*** bindTexture(...);
*** init(...);
*** sendEvent(...);
*** setContextType(...);
*** setDevicePixelRatio(...);
*** texSubImage2D(...);
*** registerCallback(...);
}

View File

@ -0,0 +1,16 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
* <p>
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas;
public class GCanvas {
// View Mode
public enum ViewMode {
NONE_MODE, SINGLE_CANVAS_MODE, SWITCH_MODE, HYBRID_MODE, FLOAT_HYBRID_MODE, WEEX_MODE
}
public static final ViewMode DEFAULT_VIEW_MODE = ViewMode.HYBRID_MODE;
}

View File

@ -0,0 +1,178 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
* <p>
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import com.taobao.gcanvas.util.GLog;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
public class GCanvasJNI {
public static boolean GCanvaslibEnable = false;
// Native methods
static {
if(!GCanvaslibEnable){
try {
System.loadLibrary("gcanvas");
System.loadLibrary("freetype");
GCanvaslibEnable = true;
} catch (UnsatisfiedLinkError e) {
GLog.e("CANVAS", "gcanvas is not found.");
} catch (Exception e) {
GLog.e("CANVAS", "fail to load gcanvas.");
}
// try to load gcPng library
try {
System.loadLibrary("gcPng");
} catch (UnsatisfiedLinkError e) {
GLog.e("CANVAS", "png optimization is not found.");
} catch (Exception e) {
GLog.e("CANVAS", "fail to load png optimization.");
}
}
}
public static native void setBackgroundColor(String contextID, int red, int green, int blue);
public static native void setOrtho(String contextID, int width, int height);
public static native void render(String contextID, String renderCommands);
public static native void contextLost(String contextID); // Deletes native memory associated with lost GL context
public static native void release(); // Deletes native canvas
public static native void setDevicePixelRatio(String contextID, double ratio);
public static native void setClearColor(String contextID, String color);
public static native void setTyOffsetFlag(String contextID, boolean flag);
public static native void setHiQuality(String contextID, boolean isHiQuality);
public static native boolean isNeonSupport();
public static native boolean isFboSupport(String contextID);
public static native String getImageData(String contextID, int x, int y, int width, int height);
// public static native String getAllParameter(String contextID);
public static native void setContextType(String contextID, int type);
public static native void setConfig(String key, int value);
public static native void setFallbackFont(String fallbackFontFile, String systemFontLocation);
public static native void addFontFamily(String[] fontNames, String[] fontFiles);
public static native void addFallbackFontFamily(String[] fallbackFontFiles);
public static void registerWXCallNativeFunc(Context context){
/**
* We have a different javascript-call-native solution on Weex, which we call it "directly" call-native.
* Weex's customized JavascriptCore has been injected a special handle to deal GCanvas' call. All calls can directly send to JavascriptCore, avoiding JNI calls, which significantly improves performance.
* For the purpose of decoupling, We open weexjsc.so to inject our implementation at runtime.
* Thanks to Weex team.
*/
String libraryPath = null;
if (Build.VERSION.SDK_INT >= 24) {
// Android 7 has changed security policy, different .so has different namespace.
libraryPath = context.getApplicationInfo().nativeLibraryDir + "/libweexjsc.so";
GLog.i("start to load gcanvas library,path=" + libraryPath);
}
GCanvasJNI.registerCallback(libraryPath, Build.VERSION.SDK_INT);
}
public static native void setLogLevel(String logLevel);
public static native void setPreCompilePath(String path);
// public static native String exeSyncCmd(String contextID, int type, String renderCommands);
// public static native void reset(String contextID);
public static native void bindTexture(String contextId, Bitmap bitmap,int id,
int target, int level,int internalformat,int format,int type);
public static native void texSubImage2D(String contextId, Bitmap bitmap,int id,
int target, int level,int xoffset,int yoffset,int format,int type);
public static native long getWindvaneNativeFuncPtr();
public static native long destroyWVGRef();
public static native boolean sendEvent(String contextId);
public static native void registerCallback(String soPath, int version);
public static native int getNativeFps(String contextId);
public static void setFontFamilies() {
// TODO: Do we need to make it as singleton?
GFontConfigParser parser = new GFontConfigParser();
String fallbackFont = parser.getFallbackFont();
String systemFontLocation = parser.getSystemFontLocation();
GCanvasJNI.setFallbackFont(fallbackFont, systemFontLocation);
HashMap<List<String>, List<String>> fontFamilies = parser.getFontFamilies();
if (fontFamilies != null) {
GLog.d("CANVAS", "setFontFamilies() fontFamilies.size() " + fontFamilies.size());
} else {
GLog.d("CANVAS", "setFontFamilies() error! fontFamilies is empty");
}
if (fontFamilies != null) {
Iterator<List<String>> it = fontFamilies.keySet().iterator();
while (it.hasNext()) {
List<String> fontNames = it.next();
int fontNameSize = fontNames.size();
String[] fontNameArray = new String[fontNameSize];
for (int i = 0; i < fontNameSize; ++i) {
fontNameArray[i] = fontNames.get(i);
}
List<String> fontFiles = fontFamilies.get(fontNames);
int fontFileSize = fontFiles.size();
String[] fontFileArray = new String[fontFileSize];
for (int i = 0; i < fontFileSize; ++i) {
fontFileArray[i] = fontFiles.get(i);
}
GCanvasJNI.addFontFamily(fontNameArray, fontFileArray);
}
}
List<String> fallbackfontFiles = parser.getFallbackFontsList();
if (fallbackfontFiles == null) {
return;
}
int fallbackfontFileSize = fallbackfontFiles.size();
String[] FallbackFileArray = new String[fallbackfontFileSize];
for (int i = 0; i < fallbackfontFileSize; ++i) {
FallbackFileArray[i] = fallbackfontFiles.get(i);
}
GCanvasJNI.addFallbackFontFamily(FallbackFileArray);
}
}

View File

@ -0,0 +1,174 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas;
import android.app.Activity;
import org.json.JSONArray;
import org.json.JSONObject;
public class GCanvasResult<T> {
public enum ResultCode {
NO_RESULT, OK, ERROR
}
public interface Listener {
void onResult(GCanvasResult result, ResultCode code, Object message);
void onCallJS(String code);
}
protected ResultCode mResultCode = ResultCode.NO_RESULT;
protected Object mResultMessage;
protected String mCallbackId;
protected Listener mResultListener;
protected T mCustomData;
protected boolean mFinished;
protected int mChangingThreads;
protected Activity mActivity = null;
public Activity getActivity() {
return mActivity;
}
public void setActivity(Activity act) {
mActivity = act;
}
// public Activity getActivity
public GCanvasResult() {
this.mCallbackId = "";
}
public GCanvasResult(T data) {
this.mCustomData = data;
this.mCallbackId = "";
}
public GCanvasResult(String callbackId, Listener listener) {
this.mCallbackId = callbackId;
this.mResultListener = listener;
}
public ResultCode getResultCode() {
return mResultCode;
}
public Object getResultMessage() {
return mResultMessage;
}
public boolean isFinished() {
return mFinished;
}
public boolean isChangingThreads() {
return mChangingThreads > 0;
}
public String getCallbackId() {
return mCallbackId;
}
public T getCustomData() {
return mCustomData;
}
public void setResult(ResultCode result, Object message) {
mResultCode = result;
mResultMessage = message;
onResult(result, message);
}
protected void onResult(ResultCode resultCode, Object resultMessage) {
if (mResultListener != null)
mResultListener.onResult(this, resultCode, resultMessage);
}
protected void onCallJS(String code) {
if (mResultListener != null)
mResultListener.onCallJS(code);
}
/**
* Helper for success callbacks that just returns the ResultCode.OK by
* default
*
* @param message The message to add to the success result.
*/
public void success(JSONObject message) {
setResult(ResultCode.OK, message);
}
/**
* Helper for success callbacks that just returns the ResultCode.OK by
* default
*
* @param message The message to add to the success result.
*/
public void success(JSONArray message) {
setResult(ResultCode.OK, message);
}
public void success(int message) {
setResult(ResultCode.OK, message);
}
public void success(String message) {
setResult(ResultCode.OK, message);
}
public void success(boolean message) {
setResult(ResultCode.OK, message);
}
public void success(float message) {
setResult(ResultCode.OK, message);
}
public void success(Object message) {
setResult(ResultCode.OK, message);
}
/**
* Helper for success callbacks that just returns the ResultCode.OK by
* default
*/
public void success() {
setResult(ResultCode.OK, "");
}
public void error() {
setResult(ResultCode.ERROR, "");
}
public void error(JSONObject message) {
setResult(ResultCode.ERROR, message.toString());
}
public void error(String message) {
setResult(ResultCode.ERROR, message);
}
public void error(Object message) {
setResult(ResultCode.ERROR, message);
}
public void calljs(String code) {
onCallJS(code);
}
}

View File

@ -0,0 +1,37 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas;
import android.view.View;
public abstract class GCanvasWebView {
private View mView;
public GCanvasWebView(View view) {
mView = view;
}
public View getWebView() {
return mView;
}
public void setWebView(View view) {
mView = view;
}
public abstract String getUserAgentString();
public abstract void setUserAgentString(String ua);
public abstract String getOriginalUrl();
public abstract String getUrl();
}

View File

@ -0,0 +1,346 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas;
import android.os.Build;
import android.util.Log;
import com.taobao.gcanvas.util.GLog;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class GFontConfigParser {
// Android font families path
private static final String SYSTEM_FONTS_FILE = "/system/etc/system_fonts.xml";
private static final String SYSTEM_FONTS_FILE_LOLLIPOP = "/system/etc/fonts.xml";
// TODO: Fall-back fonts are not used yet. Leave the code here since we may do it in future.
private static final String FALLBACK_FONTS_FILE = "/system/etc/fallback_fonts.xml";
// Fallback font
private static final String FALLBACK_FONT = "DroidSansFallback.ttf";
// Fonts location
private static final String SYSTEM_FONT_LOCATION = "/system/fonts/";
private boolean mIsInitialized = false;
private HashMap<List<String>, List<String>> mFontFamilies = null;
private List<String> mFallbackFonts = null;
public GFontConfigParser() {
if (!mIsInitialized) {
readConfigFile();
readFallbackConfigFile();
mIsInitialized = true;
}
}
public HashMap<List<String>, List<String>> getFontFamilies() {
return mFontFamilies;
}
public List<String> getFallbackFontsList() {
return mFallbackFonts;
}
public String getFallbackFont() {
//vivo
if (new File(SYSTEM_FONT_LOCATION + "DroidSansFallbackBBK.ttf").exists()) {
return "DroidSansFallbackBBK.ttf";
} else if (new File(SYSTEM_FONT_LOCATION + "NotoSansHans-Regular.otf").exists()) {
//meizu flyme4.0
return "NotoSansHans-Regular.otf";
} else if (new File(SYSTEM_FONT_LOCATION + "NotoSansSC-Regular.otf").exists()) {
return "NotoSansSC-Regular.otf";
} else if (new File(SYSTEM_FONT_LOCATION + "NotoSansCJK-Regular.ttc").exists()) {
return "NotoSansCJK-Regular.ttc";
} else if (new File(SYSTEM_FONT_LOCATION + FALLBACK_FONT).exists()) {
return FALLBACK_FONT;
} else {
return "DroidSans.ttf";
}
}
public String getSystemFontLocation() {
return SYSTEM_FONT_LOCATION;
}
private void readFallbackConfigFile() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
db = dbf.newDocumentBuilder();
File file = new File(FALLBACK_FONTS_FILE);
if (!file.exists()) {
return;
}
Document doc = db.parse(file);
parseFallbackXML(doc);
} catch (Exception e) {
Log.e("GFontConfigParser", "readFallbackConfigFile " + e.getMessage());
}
}
private void readConfigFile() {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
db = dbf.newDocumentBuilder();
Document doc;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
doc = db.parse(new File(SYSTEM_FONTS_FILE_LOLLIPOP));
} else {
doc = db.parse(new File(SYSTEM_FONTS_FILE));
}
parseXML(doc);
} catch (Exception e) {
GLog.e("GFontConfigParser", "readConfigFile exception:" + e.getMessage());
}
}
private void parseXML(Document doc) throws Exception {
if (mFontFamilies == null) {
mFontFamilies = new HashMap<List<String>, List<String>>();
} else {
mFontFamilies.clear();
}
// family set - root node
Element root = doc.getDocumentElement();
if (!root.getTagName().equals("familyset")) {
GLog.w("GFontConfigParser", "Can't find familyset.");
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
List<String> nameList = null;
List<String> aliasNameList = null;
List<String> fileList = null;
//leon
NodeList nodeList = root.getElementsByTagName("family");
int length = nodeList.getLength();
for (int i = 0; i < length; ++i) {
Node familyNode = nodeList.item(i);
// Acquire name-set node and file-set node
NamedNodeMap n = familyNode.getAttributes();
Node s = familyNode.getAttributes().getNamedItem("name");
if (s == null) {
break;
} else {
//GLog.w("GFontConfigParser", "family name: " + s.getNodeValue());
}
NodeList fontNodes = ((Element) familyNode).getElementsByTagName("font");
if (fontNodes == null) {
GLog.w("GFontConfigParser", "nameset or fileset is invalid.");
break;
}
// Get the length of name-set, file-set.
int namesetLength = fontNodes.getLength();
nameList = new ArrayList<String>();
nameList.add(s.getNodeValue());
fileList = new ArrayList<String>();
// Get all file name
for (int j = 0; j < namesetLength; ++j) {
Node fileNode = fontNodes.item(j);
if (fileNode instanceof Element) {
Element fileElement = (Element) fileNode;
String text = fileElement.getTextContent();
fileList.add(text);
}
}
//alias
NodeList aliasList = root.getElementsByTagName("alias");
int aliasLength = aliasList.getLength();
for (int k = 0; k < aliasLength; ++k) {
Node aliasNode = aliasList.item(k);
Node nameA = aliasNode.getAttributes().getNamedItem("name");
Node toA = aliasNode.getAttributes().getNamedItem("to");
if (nameA == null || toA == null) {
break;
} else {
//GLog.w("GCanvas - GFontConfigParser", "nameA: " + nameA + " toA: " + toA);
}
aliasNameList = new ArrayList<String>();
for (String name : nameList) {
if (name.equals(toA.getNodeValue())) {
aliasNameList.add(nameA.getNodeValue());
//GLog.w("GCanvas - GFontConfigParser", "alias " + nameA.getNodeValue() + " to: " + name);
}
}
nameList.addAll(aliasNameList);
}
// Add fonts, files to HashMap.
mFontFamilies.put(nameList, fileList);
}
//leon
} else {
NodeList nodeList = root.getElementsByTagName("family");
int length = nodeList.getLength();
for (int i = 0; i < length; ++i) {
Node familyNode = nodeList.item(i);
// Acquire name-set node and file-set node
Node namesetNode = null;
Node filesetNode = null;
if (familyNode instanceof Element) {
Element familyElement = (Element) familyNode;
NodeList namesetNodes = familyElement.getElementsByTagName("nameset");
NodeList filesetNodes = familyElement.getElementsByTagName("fileset");
if (namesetNodes == null
|| filesetNodes == null
|| namesetNodes.getLength() != 1
|| filesetNodes.getLength() != 1) {
GLog.w("GFontConfigParser", "nameset or fileset node doesn't exist.");
return;
}
namesetNode = namesetNodes.item(0);
filesetNode = filesetNodes.item(0);
}
if (namesetNode == null || filesetNode == null) {
GLog.w("GFontConfigParser", "nameset or fileset is invalid.");
return;
}
// Get the node list.
NodeList nameset = namesetNode.getChildNodes();
NodeList fileset = filesetNode.getChildNodes();
// Return if no node is found
if (nameset == null || fileset == null) {
GLog.w("GFontConfigParser", "nameset or fileset is empty.");
return;
}
// Get the length of name-set, file-set.
int namesetLength = nameset.getLength();
int filesetLength = fileset.getLength();
List<String> nameList = new ArrayList<String>();
List<String> fileList = new ArrayList<String>();
// Get all file name
for (int j = 0; j < filesetLength; ++j) {
Node fileNode = fileset.item(j);
if (fileNode instanceof Element) {
Element fileElement = (Element) fileNode;
if (fileElement.getTagName().equals("file")) {
String text = fileElement.getTextContent();
fileList.add(text);
}
}
}
// Get all font names.
for (int j = 0; j < namesetLength; ++j) {
Node nameNode = nameset.item(j);
if (nameNode instanceof Element) {
Element nameElement = (Element) nameNode;
if (nameElement.getTagName().equals("name")) {
String text = nameElement.getTextContent();
nameList.add(text);
}
}
}
// Add fonts, files to HashMap.
mFontFamilies.put(nameList, fileList);
}
}
}
private void parseFallbackXML(Document doc) throws Exception {
if (mFallbackFonts == null) {
mFallbackFonts = new ArrayList<String>();
} else {
mFallbackFonts.clear();
}
// family set - root node
Element root = doc.getDocumentElement();
if (!root.getTagName().equals("familyset")) {
GLog.w("GFontConfigParser", "Can't find familyset.");
return;
}
NodeList nodeList = root.getElementsByTagName("family");
int length = nodeList.getLength();
for (int i = 0; i < length; ++i) {
Node familyNode = nodeList.item(i);
Node filesetNode = null;
if (familyNode instanceof Element) {
Element familyElement = (Element) familyNode;
NodeList filesetNodes = familyElement.getElementsByTagName("fileset");
if (filesetNodes == null || filesetNodes.getLength() != 1) {
GLog.w("GFontConfigParser", "nameset or fileset node doesn't exist.");
return;
}
filesetNode = filesetNodes.item(0);
}
if (filesetNode == null) {
GLog.w("GFontConfigParser", "nameset or fileset is invalid.");
return;
}
NodeList fileset = filesetNode.getChildNodes();
if (fileset == null) {
GLog.w("GFontConfigParser", "nameset or fileset is empty.");
return;
}
int filesetLength = fileset.getLength();
for (int j = 0; j < filesetLength; ++j) {
Node fileNode = fileset.item(j);
if (fileNode instanceof Element) {
Element fileElement = (Element) fileNode;
if (fileElement.getTagName().equals("file")) {
String text = fileElement.getTextContent();
mFallbackFonts.add(text);
}
}
}
}
}
}

View File

@ -0,0 +1,441 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas.audio;
import android.app.Activity;
import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.os.Handler;
import android.provider.Settings;
import com.taobao.gcanvas.GCanvasResult;
import com.taobao.gcanvas.GCanvasWebView;
//import com.taobao.gcanvas.GUtil;
import com.taobao.gcanvas.util.GLog;
import org.json.JSONArray;
import org.json.JSONException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
/**
* This class called by CordovaActivity to play and record audio.
* The file can be local or over a network using http.
* <p>
* Audio formats supported (tested): .mp3, .wav
* <p>
* Local audio files must reside in one of two places:
* android_asset: file name must start with /android_asset/sound.mp3
* sdcard: file name is just sound.mp3
*/
public class GAudioHandler {
public static String TAG = GAudioHandler.class.getName();
private Activity activity;
private GCanvasWebView webView;
private HashMap<String, GAudioPlayer> players; // Audio player object
private ArrayList<GAudioPlayer> pausedForPhone; // Audio players that were paused when phone call came in
private VolumeObserver volumeObserver = null;
public GAudioHandler() {
this.players = new HashMap<String, GAudioPlayer>();
this.pausedForPhone = new ArrayList<GAudioPlayer>();
}
public static String stripFileProtocol(String uriString) {
if (uriString.startsWith("file://")) {
uriString = uriString.substring(7);
}
return uriString;
}
public void initialize(Context context, GCanvasWebView webView) {
if (context instanceof Activity) {
activity = (Activity) context;
}
this.webView = webView;
}
public Activity getActivity() {
return activity;
}
public GCanvasWebView getWebView() {
return webView;
}
private HashMap<String, GAudioPlayer> getPlayers() {
return players;
}
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param resultContext The callback context used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public boolean execute(String action, JSONArray args, GCanvasResult resultContext) throws JSONException {
if (action == null) {
GLog.e("CANVAS", " ~ GAutioHandler ::: action is null");
return false;
}
String result = "";
if (action.equals("startRecordingAudio")) {
startRecordingAudio(args.getString(0), stripFileProtocol(args.getString(1)), resultContext);
} else if (action.equals("stopRecordingAudio")) {
stopRecordingAudio(args.getString(0));
} else if (action.equals("startPlayingAudio")) {
startPlayingAudio(args.getString(0), stripFileProtocol(args.getString(1)), resultContext);
} else if (action.equals("seekToAudio")) {
seekToAudio(args.getString(0), args.getInt(1));
} else if (action.equals("pausePlayingAudio")) {
pausePlayingAudio(args.getString(0));
} else if (action.equals("stopPlayingAudio")) {
stopPlayingAudio(args.getString(0));
} else if (action.equals("setVolume")) {
try {
this.setVolume(args.getString(0), Float.parseFloat(args.getString(1)));
} catch (NumberFormatException nfe) {
//no-op
}
} else if (action.equals("getCurrentPositionAudio")) {
float f = this.getCurrentPositionAudio(args.getString(0));
resultContext.success(f);
return true;
} else if (action.equals("getDurationAudio")) {
float f = this.getDurationAudio(args.getString(0), args.getString(1), resultContext);
resultContext.success(f);
return true;
} else if (action.equals("create")) {
String id = args.getString(0);
String src = stripFileProtocol(args.getString(1));
GAudioPlayer audio = new GAudioPlayer(this, id, src, resultContext);
this.players.put(id, audio);
} else if (action.equals("release")) {
boolean success = release(args.getString(0));
resultContext.success(success);
return true;
} else if (action.equals("disable")) {
onDestroy();
return true;
} else if (action.equals("setSourceAudio")) {
String id = args.getString(0);
String src = stripFileProtocol(args.getString(1));
GAudioPlayer audio = this.players.get(id);
if (audio == null || src.isEmpty()) {
GLog.e("CANVAS", "audio's id error");
return false;
}
audio.setSourceAudio(src);
} else if (action.equals("startLoadingAudio")) {
String id = args.getString(0);
startLoadingAudio(id);
} else { // Unrecognized action.
return false;
}
resultContext.success(result);
return true;
}
/**
* Release the audio player instance to save memory.
*
* @param id The id of the audio player
*/
private boolean release(String id) {
if (!players.containsKey(id)) {
return false;
}
GAudioPlayer audio = players.get(id);
players.remove(id);
audio.destroy();
return true;
}
/**
* Stop all audio players and recorders on navigate.
*/
public void onReset() {
onDestroy();
}
/**
* Stop all audio players and recorders.
*/
public void onDestroy() {
for (GAudioPlayer audio : players.values()) {
audio.destroy();
}
// Unregister volume observer
if (volumeObserver != null) {
activity.getContentResolver().unregisterContentObserver(volumeObserver);
}
this.players.clear();
}
/**
* Called when a message is sent to plugin.
*
* @param id The message id
* @param data The message data
* @return Object to stop propagation or null
*/
public Object onMessage(String id, Object data) {
// If phone message
if (id.equals("telephone")) {
// If phone ringing, then pause playing
if ("ringing".equals(data) || "offhook".equals(data)) {
// Get all audio players and pause them
for (GAudioPlayer audio : this.players.values()) {
if (audio.getState() == GAudioPlayer.STATE.MEDIA_RUNNING.ordinal()) {
this.pausedForPhone.add(audio);
audio.pausePlaying();
}
}
}
// If phone idle, then resume playing those players we paused
else if ("idle".equals(data)) {
for (GAudioPlayer audio : this.pausedForPhone) {
audio.startPlaying(null);
}
this.pausedForPhone.clear();
}
}
return null;
}
/**
* Start recording and save the specified file.
*
* @param id The id of the audio player
* @param file The name of the file
*/
public void startRecordingAudio(String id, String file, GCanvasResult resultContext) {
GAudioPlayer audio = this.players.get(id);
if (audio == null) {
audio = new GAudioPlayer(this, id, file, resultContext);
this.players.put(id, audio);
}
audio.startRecording(file);
}
/**
* Stop recording and save to the file specified when recording started.
*
* @param id The id of the audio player
*/
public void stopRecordingAudio(String id) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.stopRecording();
}
}
/**
* Start or resume playing audio file.
*
* @param id The id of the audio player
* @param file The name of the audio file.
*/
public void startPlayingAudio(String id, String file, GCanvasResult resultContext) {
GAudioPlayer audio = this.players.get(id);
if (audio == null) {
audio = new GAudioPlayer(this, id, file, resultContext);
this.players.put(id, audio);
}
audio.startPlaying(file);
// Mute the audio based on the device settings
int mode = Settings.System.getInt(activity.getContentResolver(), Settings.System.MODE_RINGER, 2);
if (mode != 2) {
audio.setVolume(0);
} else {
audio.setVolume(1);
}
}
/**
* Seek to a location.
*
* @param id The id of the audio player
* @param milliseconds int: number of milliseconds to skip 1000 = 1 second
*/
public void seekToAudio(String id, int milliseconds) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.seekToPlaying(milliseconds);
}
}
/**
* Pause playing.
*
* @param id The id of the audio player
*/
public void pausePlayingAudio(String id) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.pausePlaying();
}
}
/**
* Stop playing the audio file.
*
* @param id The id of the audio player
*/
public void stopPlayingAudio(String id) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.stopPlaying();
}
}
public void startLoadingAudio(String id) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.loadingAudio();
}
}
/**
* Get current position of playback.
*
* @param id The id of the audio player
* @return position in msec
*/
public float getCurrentPositionAudio(String id) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
return (audio.getCurrentPosition() / 1000.0f);
}
return -1;
}
/**
* Get the duration of the audio file.
*
* @param id The id of the audio player
* @param file The name of the audio file.
* @return The duration in msec.
*/
public float getDurationAudio(String id, String file, GCanvasResult resultContext) {
// Get audio file
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
return (audio.getDuration(file));
}
// If not already open, then open the file
else {
audio = new GAudioPlayer(this, id, file, resultContext);
this.players.put(id, audio);
return (audio.getDuration(file));
}
}
/**
* Get the audio device to be used for playback.
*
* @return 1=earpiece, 2=speaker
*/
@SuppressWarnings("deprecation")
public int getAudioOutputDevice() {
AudioManager audiMgr = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
return 1;
} else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) {
return 2;
} else {
return -1;
}
}
/**
* Set the audio device to be used for playback.
*
* @param output 1=earpiece, 2=speaker
*/
@SuppressWarnings("deprecation")
public void setAudioOutputDevice(int output) {
AudioManager audiMgr = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
if (output == 2) {
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
} else if (output == 1) {
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
} else {
GLog.w("AudioHandler setAudioOutputDevice Error: Unknown output device.");
}
}
/**
* Set the volume for an audio device
*
* @param id The id of the audio player
* @param volume Volume to adjust to 0.0f - 1.0f
*/
public void setVolume(String id, float volume) {
GAudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.setVolume(volume);
} else {
GLog.w("AudioHandler setVolume Error: Unknown Audio Player " + id);
}
}
private static class VolumeObserver extends ContentObserver {
private final WeakReference<GAudioHandler> mGAudioHandlerRef;
public VolumeObserver(GAudioHandler gAudioHandler, Handler handler) {
super(handler);
mGAudioHandlerRef = new WeakReference<GAudioHandler>(gAudioHandler);
}
@SuppressWarnings("deprecation")
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
GAudioHandler audioHandler = mGAudioHandlerRef.get();
if (audioHandler != null) {
int mode = Settings.System.getInt(audioHandler.getActivity().getContentResolver(), Settings.System.MODE_RINGER, 2);
if (mode != 2) { // mute or virbate
for (String i : audioHandler.getPlayers().keySet()) {
audioHandler.getPlayers().get(i).setVolume(0);
}
} else {
// Volume values are raw scalars in range 0.0 to 1.0.
for (String i : audioHandler.getPlayers().keySet()) {
audioHandler.getPlayers().get(i).setVolume(1);
}
}
}
}
}
}

View File

@ -0,0 +1,555 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas.audio;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaRecorder;
import android.os.Environment;
import com.taobao.gcanvas.GCanvasResult;
import com.taobao.gcanvas.util.GLog;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* This class implements the audio playback and recording capabilities used by Cordova.
* It is called by the AudioHandler Cordova class.
* Only one file can be played or recorded per class instance.
* <p>
* Local audio files must reside in one of two places:
* android_asset: file name must start with /android_asset/sound.mp3
* sdcard: file name is just sound.mp3
*/
public class GAudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
private static final String LOG_TAG = "GAudioPlayer";
// AudioPlayer message ids
private static int MEDIA_STATE = 1;
private static int MEDIA_DURATION = 2;
private static int MEDIA_POSITION = 3;
private static int MEDIA_READY = 4;
private static int MEDIA_ERROR = 9;
// Media error codes
private static int MEDIA_ERR_NONE_ACTIVE = 0;
private static int MEDIA_ERR_ABORTED = 1;
private static int MEDIA_ERR_NETWORK = 2;
private static int MEDIA_ERR_DECODE = 3;
private static int MEDIA_ERR_NONE_SUPPORTED = 4;
private GAudioHandler handler; // The AudioHandler object
private String id; // The id of this player (used to identify Media object in JavaScript)
private MODE mode = MODE.NONE; // Playback or Recording mode
private STATE state = STATE.MEDIA_NONE; // State of recording or playback
private String audioFile = null; // File name to play or record to
private float duration = -1; // Duration of audio
private MediaRecorder recorder = null; // Audio recording object
private String tempFile = null; // Temporary recording file name
private MediaPlayer player = null; // Audio player object
private boolean prepareOnly = true; // playback after file prepare flag
private int seekOnPrepared = 0; // seek to this location once media is prepared
private int recorderCount = 0;
private GCanvasResult resultContext;
/**
* Constructor.
*
* @param handler The audio handler object
* @param id The id of this audio player
* @param file The audio file path
* @param resultContext
*/
public GAudioPlayer(GAudioHandler handler, String id, String file, GCanvasResult resultContext) {
this.handler = handler;
this.id = id;
this.audioFile = file;
this.recorder = new MediaRecorder();
this.resultContext = resultContext;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.3gp";
} else {
this.tempFile = "/data/data/" + handler.getActivity().getPackageName() + "/cache/tmprecording.3gp";
}
}
/**
* @param file The audio file path
*/
public void setSourceAudio(String file) {
this.audioFile = file;
}
private void sendJavascript(String js) {
if (this.resultContext != null) {
this.resultContext.calljs(js);
}
}
/**
* Start recording the specified file.
*
* @param file The name of the file
*/
public void startRecording(String file) {
switch (this.mode) {
case PLAY:
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
break;
case NONE:
this.audioFile = file;
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); // AMR_NB);
this.recorder.setOutputFile(this.tempFile);
try {
this.recorder.prepare();
this.recorder.start();
this.setState(STATE.MEDIA_RUNNING);
this.recorderCount++;
return;
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
break;
case RECORD:
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
}
/**
* Save temporary recorded file to specified name
*
* @param file
*/
public void moveFile(String file) {
/* this is a hack to save the file as the specified name */
File f = new File(this.tempFile);
if (!file.startsWith("/")) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
file = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + file;
} else {
file = "/data/data/" + handler.getActivity().getPackageName() + "/cache/" + file;
}
}
if (!f.renameTo(new File(file))){
String logMsg = "renaming " + this.tempFile + " to " + file;
GLog.e(LOG_TAG, "FAILED " + logMsg);
}
}
/**
* Stop recording and save to the file specified when recording started.
*/
public void stopRecording() {
if ((this.recorder != null) && (this.recorderCount > 0)) {
try {
if (this.state == STATE.MEDIA_RUNNING) {
this.recorder.stop();
this.setState(STATE.MEDIA_STOPPED);
}
this.recorderCount--;
this.recorder.reset();
this.moveFile(this.audioFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Start or resume playing audio file.
*
* @param file The name of the audio file.
*/
public void startPlaying(String file) {
if (this.readyPlayer(file) && this.player != null) {
this.player.start();
this.setState(STATE.MEDIA_RUNNING);
this.seekOnPrepared = 0; //insures this is always reset
} else {
this.prepareOnly = false;
}
}
/**
* Seek or jump to a new time in the track.
*/
public void seekToPlaying(int milliseconds) {
if (this.readyPlayer(this.audioFile)) {
this.player.seekTo(milliseconds);
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
} else {
this.seekOnPrepared = milliseconds;
}
}
public void loadingAudio() {
if (this.readyPlayer(this.audioFile)) {
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_READY + ");");
}
}
/**
* Pause playing.
*/
public void pausePlaying() {
// If playing, then pause
if (this.state == STATE.MEDIA_RUNNING && this.player != null) {
this.player.pause();
this.setState(STATE.MEDIA_PAUSED);
} else {
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
}
}
/**
* Stop playing the audio file.
*/
public void stopPlaying() {
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
this.player.pause();
this.player.seekTo(0);
this.setState(STATE.MEDIA_STOPPED);
} else {
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
}
}
/**
* Callback to be invoked when playback of a media source has completed.
*
* @param player The MediaPlayer that reached the end of the file
*/
public void onCompletion(MediaPlayer player) {
this.setState(STATE.MEDIA_STOPPED);
}
/**
* Get current position of playback.
*
* @return position in msec or -1 if not playing
*/
public long getCurrentPosition() {
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
int curPos = this.player.getCurrentPosition();
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
return curPos;
} else {
return -1;
}
}
/**
* Determine if playback file is streaming or local.
* It is streaming if file name starts with "http://"
*
* @param file The file name
* @return T=streaming, F=local
*/
public boolean isStreaming(String file) {
if (file.contains("http://") || file.contains("https://")) {
return true;
} else {
return false;
}
}
/**
* Get the duration of the audio file.
*
* @param file The name of the audio file.
* @return The duration in msec.
* -1=can't be determined
* -2=not allowed
*/
public float getDuration(String file) {
// Can't get duration of recording
if (this.recorder != null) {
return (-2); // not allowed
}
// If audio file already loaded and started, then return duration
if (this.player != null) {
return this.duration;
}
// If no player yet, then create one
else {
this.prepareOnly = true;
this.startPlaying(file);
// This will only return value for local, since streaming
// file hasn't been read yet.
return this.duration;
}
}
/**
* Callback to be invoked when the media source is ready for playback.
*
* @param player The MediaPlayer that is ready for playback
*/
public void onPrepared(MediaPlayer player) {
// Listen for playback completion
this.player.setOnCompletionListener(this);
// seek to any location received while not prepared
this.seekToPlaying(this.seekOnPrepared);
// If start playing after prepared
if (!this.prepareOnly) {
this.player.start();
this.setState(STATE.MEDIA_RUNNING);
this.seekOnPrepared = 0; //reset only when played
} else {
this.setState(STATE.MEDIA_STARTING);
}
// Save off duration
this.duration = getDurationInSeconds();
// reset prepare only flag
this.prepareOnly = true;
// Send status notification to JavaScript
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
}
/**
* By default Android returns the length of audio in mills but we want seconds
*
* @return length of clip in seconds
*/
private float getDurationInSeconds() {
return (this.player.getDuration() / 1000.0f);
}
/**
* Callback to be invoked when there has been an error during an asynchronous operation
* (other errors will throw exceptions at method call time).
*
* @param player the MediaPlayer the error pertains to
* @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED)
* @param arg2 an extra code, specific to the error.
*/
public boolean onError(MediaPlayer player, int arg1, int arg2) {
this.player.stop();
this.player.release();
// Send error notification to JavaScript
this.sendJavascript("Media.onStatus('" + this.id + "', { \"code\":" + arg1 + "});");
return false;
}
/**
* Set the mode
*
* @param mode
*/
private void setMode(MODE mode) {
this.mode = mode;
}
/**
* Get the audio state.
*
* @return int
*/
public int getState() {
return this.state.ordinal();
}
/**
* Set the state and send it to JavaScript.
*
* @param state
*/
private void setState(STATE state) {
if (this.state != state) {
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state.ordinal() + ");");
}
this.state = state;
}
/**
* Set the volume for audio player
*
* @param volume
*/
public void setVolume(float volume) {
if (this.player != null) {
this.player.setVolume(volume, volume);
}
}
/**
* Destroy player and stop audio playing or recording.
*/
public void destroy() {
// Stop any play or record
if (this.player != null) {
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
this.player.stop();
this.setState(STATE.MEDIA_STOPPED);
}
this.player.release();
this.player = null;
}
if (this.recorder != null) {
this.stopRecording();
this.recorder.release();
this.recorder = null;
}
}
/**
* attempts to put the player in play mode
*
* @return true if in playmode, false otherwise
*/
private boolean playMode() {
switch (this.mode) {
case NONE:
this.setMode(MODE.PLAY);
break;
case PLAY:
break;
case RECORD:
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
return false; //player is not ready
}
return true;
}
/**
* attempts to initialize the media player for playback
*
* @param file the file to play
* @return false if player not ready, reports if in wrong mode or state
*/
private boolean readyPlayer(String file) {
if (playMode()) {
switch (this.state) {
case MEDIA_NONE:
if (this.player == null) {
this.player = new MediaPlayer();
}
try {
this.loadAudioFile(file);
} catch (Exception e) {
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
return false;
case MEDIA_LOADING:
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING instead
this.prepareOnly = false;
return false;
case MEDIA_STARTING:
case MEDIA_RUNNING:
case MEDIA_PAUSED:
return true;
case MEDIA_STOPPED:
//if we are readying the same file
if (this.audioFile.compareTo(file) == 0) {
//reset the audio file
player.seekTo(0);
player.pause();
return true;
} else {
//reset the player
this.player.reset();
try {
this.loadAudioFile(file);
} catch (Exception e) {
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
//if we had to prepare= the file, we won't be in the correct state for playback
return false;
}
default:
this.sendJavascript("Media.onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
}
return false;
}
/**
* load audio file
*
* @throws IOException
* @throws IllegalStateException
* @throws SecurityException
* @throws IllegalArgumentException
*/
private void loadAudioFile(String file) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
if (this.isStreaming(file)) {
this.player.setDataSource(file);
this.player.setAudioStreamType(AudioManager.STREAM_MUSIC);
//if it's a streaming file, play mode is implied
this.setMode(MODE.PLAY);
this.setState(STATE.MEDIA_STARTING);
this.player.setOnPreparedListener(this);
this.player.prepareAsync();
} else {
if (file.startsWith("/android_asset/")) {
String f = file.substring(15);
android.content.res.AssetFileDescriptor fd = this.handler.getActivity().getAssets().openFd(f);
this.player.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
} else {
File fp = new File(file);
if (fp.exists()) {
FileInputStream fileInputStream = new FileInputStream(file);
this.player.setDataSource(fileInputStream.getFD());
fileInputStream.close();
} else {
//file not exists should throw exception!
throw new IOException();
}
}
this.setState(STATE.MEDIA_STARTING);
this.player.setOnPreparedListener(this);
this.player.prepare();
// Get duration
this.duration = getDurationInSeconds();
}
}
// AudioPlayer modes
public enum MODE {
NONE, PLAY, RECORD
}
// AudioPlayer states
public enum STATE {
MEDIA_NONE,
MEDIA_STARTING,
MEDIA_RUNNING,
MEDIA_PAUSED,
MEDIA_STOPPED,
MEDIA_LOADING
}
}

View File

@ -0,0 +1,102 @@
package com.taobao.gcanvas.surface;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.TextureView;
import com.taobao.gcanvas.util.GLog;
/**
* @author ertong
* create at 2017/8/1
*/
public class GTextureView extends TextureView {
private GTextureViewCallback mCallback;
public GTextureView(Context context, String id) {
super(context);
init(id);
}
public GTextureView(Context context, String id, AttributeSet attrs) {
super(context, attrs);
init(id);
}
public GTextureView(Context context, String id, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(id);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public GTextureView(Context context, String id, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(id);
}
public void setBackgroundColor(String color) {
if (mCallback != null) {
mCallback.setBackgroundColor(color);
}
}
public void addSurfaceTextureListener(TextureView.SurfaceTextureListener listener) {
if (null == listener) {
return;
}
if (null != mCallback) {
mCallback.addSurfaceTextureListener(listener);
}
}
private void init(String id) {
// getHolder().setFormat(PixelFormat.RGBA_8888);
// mCallback = new GTextureViewCallback(id);
// getHolder().addCallback(mCallback);
// this.setZOrderOnTop(true);
mCallback = new GTextureViewCallback(this, id);
this.setSurfaceTextureListener(mCallback);
this.setOpaque(false);
this.setLayerType(LAYER_TYPE_HARDWARE, null);
}
public void resume() {
}
public void pause() {
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
// if(visibility == VISIBLE) {
super.onWindowVisibilityChanged(visibility);
// }
GLog.d("on window visibility changed.visibility=" + visibility);
}
public void requestExit() {
GLog.d("on request Exit in GSurfaceView.");
if (mCallback != null) {
GLog.d("start to request Exit.");
mCallback.onRequestExit();
}
}
public void sendEvent() {
}
public String getCanvasKey() {
if (null != mCallback) {
return mCallback.getKey();
}
return "";
}
}

View File

@ -0,0 +1,167 @@
package com.taobao.gcanvas.surface;
import android.annotation.TargetApi;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.text.TextUtils;
import android.view.Surface;
import android.view.TextureView;
import com.taobao.gcanvas.GCanvasJNI;
import com.taobao.gcanvas.util.GLog;
import java.util.ArrayList;
/**
* @author ertong
* create at 2017/8/1
*/
public class GTextureViewCallback implements TextureView.SurfaceTextureListener {
private final String mKey;
private String mBackgroundColor = "#ffffff";
private Surface mSurface;
private SurfaceTexture mSurfaceTexture;
private TextureView mTextureview;
private static boolean INITIALIZED = false;
private ArrayList<TextureView.SurfaceTextureListener> mDelegateLists;
static {
if (!INITIALIZED) {
try {
System.loadLibrary("gcanvas");
System.loadLibrary("freetype");
GCanvasJNI.setFontFamilies();
INITIALIZED = true;
} catch (Throwable throwable) {
GLog.e("GTextureViewCallback", "error when load library", throwable);
}
}
}
public GTextureViewCallback(TextureView v, String id) {
this.mKey = id;
this.mTextureview = v;
}
public void addSurfaceTextureListener(TextureView.SurfaceTextureListener listener) {
if (null == mDelegateLists) {
mDelegateLists = new ArrayList<>(1);
}
if (!mDelegateLists.contains(listener)) {
mDelegateLists.add(listener);
}
}
public void setBackgroundColor(String color) {
if (!TextUtils.isEmpty(color)) {
mBackgroundColor = color;
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
GLog.d("on surfaceTexture Available.");
if (mSurfaceTexture == null) {
mSurface = new Surface(surface);
mSurfaceTexture = surface;
} else {
mTextureview.setSurfaceTexture(mSurfaceTexture);
}
onSurfaceChanged(this.mKey, mSurface, 0, width, height, mBackgroundColor);
if (GCanvasJNI.sendEvent(mKey)) {
if (mTextureview instanceof GTextureView) {
GLog.d("start to send event in GSurfaceCallback.");
((GTextureView) mTextureview).sendEvent();
}
}
if (null != mDelegateLists) {
for (TextureView.SurfaceTextureListener listener : mDelegateLists) {
listener.onSurfaceTextureAvailable(surface, width, height);
}
}
}
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
GLog.d("on surfaceTexture changed.");
if (mSurface == null) {
mSurface = new Surface(surface);
mSurfaceTexture = surface;
}
onSurfaceChanged(this.mKey, mSurface, 0, width, height, mBackgroundColor);
if (null != mDelegateLists) {
for (TextureView.SurfaceTextureListener listener : mDelegateLists) {
listener.onSurfaceTextureSizeChanged(surface, width, height);
}
}
}
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
GLog.d("on surfaceTexture destroyed.");
if (mSurfaceTexture == null || mSurface == null) {
return true;
}
if (null != mDelegateLists) {
for (TextureView.SurfaceTextureListener listener : mDelegateLists) {
listener.onSurfaceTextureDestroyed(surface);
}
}
return true;
}
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public void onRequestExit() {
GLog.d("on RequestExit");
if (mSurface != null) {
// onSurfaceDestroyed(this.mKey, mSurface);
mSurface.release();
mSurface = null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
GLog.d("start to release surface textureview and surface in onRequestExit");
if (mSurfaceTexture != null) {
// onSurfaceTextureDestroyed(mSurfaceTexture);
mSurfaceTexture.release();
mSurfaceTexture = null;
}
}
onRenderExit(this.mKey);
if (null != mDelegateLists) {
mDelegateLists.clear();
}
}
public String getKey() {
return mKey;
}
private native void onSurfaceCreated(String key, Surface surface);
private native void onSurfaceChanged(String key, Surface surface, int format, int width, int height, String color);
private native void onSurfaceDestroyed(String key, Surface surface);
private native void onRenderExit(String key);
}

View File

@ -0,0 +1,120 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas.util;
public final class GCanvasBase64 {
public static byte[] decode(byte[] in) {
return decode(in, in.length);
}
public static byte[] decode(byte[] in, int len) {
// approximate output length
int length = len / 4 * 3;
// return an empty array on empty or short input without padding
if (length == 0) {
return new byte[0];
}
// temporary array
byte[] out = new byte[length];
// number of padding characters ('=')
int pad = 0;
byte chr;
// compute the number of the padding characters
// and adjust the length of the input
for (; ; len--) {
chr = in[len - 1];
// skip the neutral characters
if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) {
continue;
}
if (chr == '=') {
pad++;
} else {
break;
}
}
// index in the output array
int outIndex = 0;
// index in the input array
int inIndex = 0;
// holds the value of the input character
int bits = 0;
// holds the value of the input quantum
int quantum = 0;
for (int i = 0; i < len; i++) {
chr = in[i];
// skip the neutral characters
if ((chr == '\n') || (chr == '\r') || (chr == ' ') || (chr == '\t')) {
continue;
}
if ((chr >= 'A') && (chr <= 'Z')) {
// char ASCII value
// A 65 0
// Z 90 25 (ASCII - 65)
bits = chr - 65;
} else if ((chr >= 'a') && (chr <= 'z')) {
// char ASCII value
// a 97 26
// z 122 51 (ASCII - 71)
bits = chr - 71;
} else if ((chr >= '0') && (chr <= '9')) {
// char ASCII value
// 0 48 52
// 9 57 61 (ASCII + 4)
bits = chr + 4;
} else if (chr == '+') {
bits = 62;
} else if (chr == '/') {
bits = 63;
} else {
return null;
}
// append the value to the quantum
quantum = (quantum << 6) | (byte) bits;
if (inIndex % 4 == 3) {
// 4 characters were read, so make the output:
out[outIndex++] = (byte) (quantum >> 16);
out[outIndex++] = (byte) (quantum >> 8);
out[outIndex++] = (byte) quantum;
}
inIndex++;
}
if (pad > 0) {
// adjust the quantum value according to the padding
quantum = quantum << (6 * pad);
// make output
out[outIndex++] = (byte) (quantum >> 16);
if (pad == 1) {
out[outIndex++] = (byte) (quantum >> 8);
}
}
// create the resulting array
byte[] result = new byte[outIndex];
System.arraycopy(out, 0, result, 0, outIndex);
return result;
}
}

View File

@ -0,0 +1,81 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas.util;
import android.util.Log;
import com.taobao.gcanvas.GCanvas;
import com.taobao.gcanvas.GCanvas.ViewMode;
import org.json.JSONArray;
import org.json.JSONException;
public class GCanvasHelper {
/**
* Parse the js command args to a JsonArray Object
*
* @param command
* @param params
* @return
*/
public static JSONArray argsToJsonArray(String command, String params) {
JSONArray args = null;
try {
if (params.equals("{}")) {
args = new JSONArray();
}
else if (params.startsWith("\"") && params.endsWith("\"")) {
args = new JSONArray();
args.put(params.substring(1, params.length() - 1));
}
// this is hard code to optimize the performance .
// parsing json costs much time.
// TODO: need to find a graceful solution later.
else if (command.equals("render") && params.startsWith("[\"")) {
args = new JSONArray();
args.put(params.substring(2, params.length() - 2));
}
else {
args = new JSONArray(params);
}
} catch (JSONException e) {
Log.w("GCANVAS", "fail to parse params:" + params);
args = new JSONArray();
}
return args;
}
/**
* Try to parse a string content to GCanvas.ViewMode
*
* @param mode
* @return
*/
public static ViewMode parseViewModeString(String mode) {
if (mode == null) {
return GCanvas.DEFAULT_VIEW_MODE;
} else if (mode.equals("hybrid")) {
return ViewMode.HYBRID_MODE;
} else if (mode.equals("default")) {
return GCanvas.DEFAULT_VIEW_MODE;
} else if (mode.equals("canvas")) {
return ViewMode.SINGLE_CANVAS_MODE;
} else if (mode.equals("switch")) {
return ViewMode.SWITCH_MODE;
} else if (mode.equals("float")) {
return ViewMode.FLOAT_HYBRID_MODE;
} else {
GLog.w("CANVAS", "cannot parse the view mode, mode:" + mode);
return GCanvas.DEFAULT_VIEW_MODE;
}
}
}

View File

@ -0,0 +1,148 @@
/**
* Created by G-Canvas Open Source Team.
* Copyright (c) 2017, Alibaba, Inc. All rights reserved.
*
* This source code is licensed under the Apache Licence 2.0.
* For the full copyright and license information, please view
* the LICENSE file in the root directory of this source tree.
*/
package com.taobao.gcanvas.util;
import android.util.Log;
import com.taobao.gcanvas.GCanvasJNI;
public class GLog {
public static final String TAG = "CANVAS";
public static final int LEVEL_FORCE = 0;
public static final int LEVEL_VERBOSE = 2;
public static final int LEVEL_DEBUG = 3;
public static final int LEVEL_INFO = 4;
public static final int LEVEL_WARN = 5;
public static final int LEVEL_ERROR = 6;
public static final int LEVEL_ASSERT = 7;
public static final int LEVEL_NULL = 9;
public static int logLevel = LEVEL_INFO;
public static void setLevel(String level) {
if (level == null) {
return;
}
if (level.equals("force")) {
logLevel = LEVEL_FORCE;
}else if (level.equals("debug")) {
logLevel = LEVEL_DEBUG;
}else if (level.equals("info")) {
logLevel = LEVEL_INFO;
}else if (level.equals("warn")) {
logLevel = LEVEL_WARN;
}else if (level.equals("error")) {
logLevel = LEVEL_ERROR;
}else if (level.equals("fatal")) {
logLevel = LEVEL_ASSERT;
}
GCanvasJNI.setLogLevel(level);
}
public static int getLevel() {
return logLevel;
}
public static void force(String Tag, String message) {
Log.e(Tag, message);
}
public static void i(String message) {
GLog.i(TAG, message);
}
public static void i(String Tag, String message) {
if (logLevel == LEVEL_FORCE) {
GLog.force(Tag, message);
} else if (logLevel <= LEVEL_INFO) {
Log.i(Tag, message);
}
}
public static void i(String Tag, String message, Throwable e) {
if (logLevel <= LEVEL_INFO) {
Log.i(Tag, message, e);
}
}
public static void d(String message) {
GLog.d(TAG, message);
}
public static void d(String Tag, String message) {
if (logLevel == LEVEL_FORCE) {
GLog.force(Tag, message);
} else if (logLevel <= LEVEL_DEBUG) {
Log.i(Tag, message);
}
}
public static void d(String Tag, String message, Throwable e) {
if (logLevel <= LEVEL_DEBUG) {
Log.i(Tag, message, e);
}
}
public static void e(String message) {
GLog.e(TAG, message);
}
public static void e(String Tag, String message) {
if (logLevel <= LEVEL_ERROR) {
Log.e(Tag, message);
}
}
public static void e(String Tag, String message, Throwable e) {
if (logLevel <= LEVEL_ERROR) {
Log.e(Tag, message, e);
}
}
public static void w(String message) {
GLog.w(TAG, message);
}
public static void w(String Tag, String message) {
if (logLevel == LEVEL_FORCE) {
GLog.force(Tag, message);
} else if (logLevel <= LEVEL_WARN) {
Log.w(Tag, message);
}
}
public static void w(String Tag, String message, Throwable e) {
if (logLevel <= LEVEL_WARN) {
Log.w(Tag, message, e);
}
}
public static void v(String message) {
GLog.v(TAG, message);
}
public static void v(String Tag, String message) {
if (logLevel == LEVEL_FORCE) {
GLog.force(Tag, message);
} else if (logLevel <= LEVEL_VERBOSE) {
Log.v(Tag, message);
}
}
public static void v(String Tag, String message, Throwable e) {
if (logLevel <= LEVEL_VERBOSE) {
Log.v(Tag, message, e);
}
}
}

View File

@ -0,0 +1,3 @@
{
"presets": ["react-native"]
}

View File

@ -0,0 +1,6 @@
[android]
target = Google Inc.:Google APIs:23
[maven_repositories]
central = https://repo1.maven.org/maven2

View File

@ -0,0 +1,54 @@
[ignore]
; We fork some components by platform
.*/*[.]android.js
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
; Ignore duplicate module providers
; For RN Apps installed via npm, "Libraries" folder is inside
; "node_modules/react-native" but in the source repo it is in the root
.*/Libraries/react-native/React.js
; Ignore polyfills
.*/Libraries/polyfills/.*
; Ignore metro
.*/node_modules/metro/.*
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow/
node_modules/react-native/flow-github/
[options]
emoji=true
module.system=haste
munge_underscores=true
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.native.js
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[version]
^0.63.0

View File

@ -0,0 +1 @@
*.pbxproj -text

View File

@ -0,0 +1,53 @@
# OSX
#
.DS_Store
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace
# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
# node.js
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
\.buckd/
*.keystore
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/
*/fastlane/report.xml
*/fastlane/Preview.html
*/fastlane/screenshots

View File

@ -0,0 +1,106 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
import {
requireNativeComponent,
findNodeHandle,
NativeModules
} from 'react-native';
import {
GCanvasView,
} from 'react-native-gcanvas';
import { enable, ReactNativeBridge, Image as GImage } from "gcanvas.js/src/index.js";
ReactNativeBridge.GCanvasModule = NativeModules.GCanvasModule;
ReactNativeBridge.Platform = Platform;
export default class App extends Component<{}> {
onPressHandle = () => {
console.log(">>>>>>>>onPressHandle...start")
var ref = this.refs.canvas_holder;
var canvas_tag = findNodeHandle(ref);
// var canvas_tag = "2";
var el = { ref:""+canvas_tag, style:{width:414, height:376}};
ref = enable(el, {bridge: ReactNativeBridge});
var ctx = ref.getContext('2d');
//rect
ctx.fillStyle = 'green';
ctx.fillRect(0, 0, 100, 100);
//rect
ctx.fillStyle = 'black';
ctx.fillRect(100, 100, 100, 100);
ctx.fillRect(25, 205, 414-50, 5);
//circle
ctx.arc(200, 315, 100, 0, Math.PI * 2, true);
ctx.fill();
var image = new GImage();
image.onload = function(){
ctx.drawImage(image, 150, 0);
ctx.drawImage(image, 150, 450);
}
image.src = '//gw.alicdn.com/tfs/TB1KwRTlh6I8KJjy0FgXXXXzVXa-225-75.png';
console.log(">>>>>>>>onPressHandle...end")
};
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Click to draw gcanvas
</Text>
<TouchableHighlight onPress={this.onPressHandle}>
<GCanvasView ref='canvas_holder' style={styles.gcanvas}>
</GCanvasView>
</TouchableHighlight>
</View>
);
}
}
const styles = StyleSheet.create({
gcanvas: {
top: 20,
width: 414,
height :700,
backgroundColor: '#FF000030'
},
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
top:20,
height :40
}
});

View File

@ -0,0 +1,12 @@
import 'react-native';
import React from 'react';
import App from '../App';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<App />
);
});

View File

@ -0,0 +1,65 @@
# To learn about Buck see [Docs](https://buckbuild.com/).
# To run your application with Buck:
# - install Buck
# - `npm start` - to start the packager
# - `cd android`
# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
# - `buck install -r android/app` - compile, install and run application
#
lib_deps = []
for jarfile in glob(['libs/*.jar']):
name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')]
lib_deps.append(':' + name)
prebuilt_jar(
name = name,
binary_jar = jarfile,
)
for aarfile in glob(['libs/*.aar']):
name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')]
lib_deps.append(':' + name)
android_prebuilt_aar(
name = name,
aar = aarfile,
)
android_library(
name = "all-libs",
exported_deps = lib_deps,
)
android_library(
name = "app-code",
srcs = glob([
"src/main/java/**/*.java",
]),
deps = [
":all-libs",
":build_config",
":res",
],
)
android_build_config(
name = "build_config",
package = "com.rngcanvassample",
)
android_resource(
name = "res",
package = "com.rngcanvassample",
res = "src/main/res",
)
android_binary(
name = "app",
keystore = "//android/keystores:debug",
manifest = "src/main/AndroidManifest.xml",
package_type = "debug",
deps = [
":app-code",
],
)

View File

@ -0,0 +1,151 @@
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js"
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.rngcanvassample"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a":1, "x86":2]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
compile project(':bridges:react-native-bridge:react-native-gcanvas:android')
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:+" // From node_modules
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}

View File

@ -0,0 +1,70 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate
# React Native
# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.proguard.annotations.DoNotStrip class *
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
@com.facebook.proguard.annotations.DoNotStrip *;
@com.facebook.common.internal.DoNotStrip *;
}
-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * {
void set*(***);
*** get*();
}
-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; }
-keep class * extends com.facebook.react.bridge.NativeModule { *; }
-keepclassmembers,includedescriptorclasses class * { native <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.UIProp <fields>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp <methods>; }
-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup <methods>; }
-dontwarn com.facebook.react.**
# TextLayoutBuilder uses a non-public Android constructor within StaticLayout.
# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details.
-dontwarn android.text.StaticLayout
# okhttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**

View File

@ -0,0 +1,32 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rngcanvassample"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@ -0,0 +1,15 @@
package com.rngcanvassample;
import com.facebook.react.ReactActivity;
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "RNGCanvasSample";
}
}

View File

@ -0,0 +1,45 @@
package com.rngcanvassample;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

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

View File

@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -0,0 +1,24 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}

View File

@ -0,0 +1,20 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true

View File

@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View File

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,8 @@
keystore(
name = "debug",
properties = "debug.keystore.properties",
store = "debug.keystore",
visibility = [
"PUBLIC",
],
)

View File

@ -0,0 +1,4 @@
key.store=debug.keystore
key.alias=androiddebugkey
key.store.password=android
key.alias.password=android

View File

@ -0,0 +1,3 @@
rootProject.name = 'RNGCanvasSample'
include ':app'

View File

@ -0,0 +1,4 @@
{
"name": "RNGCanvasSample",
"displayName": "RNGCanvasSample"
}

View File

@ -0,0 +1,4 @@
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('RNGCanvasSample', () => App);

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSAppTransportSecurity</key>
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D2A28121D9B038B00D4039D"
BuildableName = "libReact.a"
BlueprintName = "React-tvOS"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "RNGCanvasSample-tvOS.app"
BlueprintName = "RNGCanvasSample-tvOS"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "RNGCanvasSample-tvOSTests.xctest"
BlueprintName = "RNGCanvasSample-tvOSTests"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "RNGCanvasSample-tvOSTests.xctest"
BlueprintName = "RNGCanvasSample-tvOSTests"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "RNGCanvasSample-tvOS.app"
BlueprintName = "RNGCanvasSample-tvOS"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "RNGCanvasSample-tvOS.app"
BlueprintName = "RNGCanvasSample-tvOS"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "RNGCanvasSample-tvOS.app"
BlueprintName = "RNGCanvasSample-tvOS"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0620"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a"
BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "RNGCanvasSample.app"
BlueprintName = "RNGCanvasSample"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "RNGCanvasSampleTests.xctest"
BlueprintName = "RNGCanvasSampleTests"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "RNGCanvasSampleTests.xctest"
BlueprintName = "RNGCanvasSampleTests"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "RNGCanvasSample.app"
BlueprintName = "RNGCanvasSample"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "RNGCanvasSample.app"
BlueprintName = "RNGCanvasSample"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "RNGCanvasSample.app"
BlueprintName = "RNGCanvasSample"
ReferencedContainer = "container:RNGCanvasSample.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@end

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"RNGCanvasSample"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
@end

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
<rect key="frame" x="20" y="439" width="441" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="RNGCanvasSample" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="548" y="455"/>
</view>
</objects>
</document>

View File

@ -0,0 +1,38 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>RNGCanvasSample</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSAppTransportSecurity</key>
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/ -->
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -0,0 +1,70 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>
#import <React/RCTLog.h>
#import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
@interface RNGCanvasSampleTests : XCTestCase
@end
@implementation RNGCanvasSampleTests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{
if (test(view)) {
return YES;
}
for (UIView *subview in [view subviews]) {
if ([self findSubviewInView:subview matching:test]) {
return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
BOOL foundElement = NO;
__block NSString *redboxError = nil;
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
});
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) {
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
return YES;
}
return NO;
}];
}
RCTSetLogFunction(RCTDefaultLogFunction);
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
@end

View File

@ -0,0 +1,24 @@
{
"name": "RNGCanvasSample",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
},
"dependencies": {
"gcanvas.js": "^0.0.5",
"react": "16.2.0",
"react-native": "0.53.0",
"react-native-gcanvas": "^0.0.1"
},
"devDependencies": {
"babel-jest": "22.1.0",
"babel-preset-react-native": "4.0.0",
"jest": "22.1.4",
"react-test-renderer": "16.2.0"
},
"jest": {
"preset": "react-native"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
[GPlatform](https://gplatformteam.github.io/gplatform-website/docs/)

View File

@ -0,0 +1,2 @@
/build
*.iml

View File

@ -0,0 +1,38 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.extensions.sdks.CompileSDK
buildToolsVersion rootProject.extensions.sdks.BuildTool
defaultConfig {
minSdkVersion rootProject.extensions.sdks.MinSDK
targetSdkVersion rootProject.extensions.sdks.TargetSDK
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile project(':android:gcanvas_library')
compile project(':android:bridge_spec')
compile project(':android:adapters:gcanvas_imageloader_fresco')
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile "junit:junit:${rootProject.extensions.sdks.JUnit}"
provided "com.facebook.react:react-native:+"
provided "com.android.support:support-v4:${rootProject.extensions.sdks.Support}"
provided "com.android.support:support-annotations:${rootProject.extensions.sdks.Support}"
compile "com.android.support:support-dynamic-animation:${rootProject.extensions.sdks.Support}"
}

View File

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

View File

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.taobao.gcanvas.plugin.rn" />

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