mirror of
https://github.com/alibaba/GCanvas.git
synced 2025-12-08 17:36:42 +00:00
Mirror from github
This commit is contained in:
commit
cb400c9372
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
.DS_Store
|
||||
website
|
||||
64
GCanvas.podspec
Normal file
64
GCanvas.podspec
Normal 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
10
GCanvas/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
local.properties
|
||||
gradle/
|
||||
.idea/
|
||||
.gradle/
|
||||
*.iml
|
||||
Pods
|
||||
Podfile.lock
|
||||
*.xcworkspace
|
||||
build/
|
||||
.externalNativeBuild/
|
||||
91
GCanvas/README.MD
Normal file
91
GCanvas/README.MD
Normal 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 doesn’t already exist before opening a new issue. It’s helpful if you include the version of GCanvas and OS you’re 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
6
GCanvas/android/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.iml
|
||||
.idea
|
||||
.gradle
|
||||
.externalNativeBuild
|
||||
local.properties
|
||||
build
|
||||
2
GCanvas/android/adapters/bridge_adapter/.gitignore
vendored
Normal file
2
GCanvas/android/adapters/bridge_adapter/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
52
GCanvas/android/adapters/bridge_adapter/build.gradle
Normal file
52
GCanvas/android/adapters/bridge_adapter/build.gradle
Normal 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"
|
||||
21
GCanvas/android/adapters/bridge_adapter/proguard-rules.pro
vendored
Normal file
21
GCanvas/android/adapters/bridge_adapter/proguard-rules.pro
vendored
Normal 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
|
||||
@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.taobao.android.gcanvas.plugin.adapter" />
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
2
GCanvas/android/adapters/gcanvas_imageloader_fresco/.gitignore
vendored
Normal file
2
GCanvas/android/adapters/gcanvas_imageloader_fresco/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
@ -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"
|
||||
21
GCanvas/android/adapters/gcanvas_imageloader_fresco/proguard-rules.pro
vendored
Normal file
21
GCanvas/android/adapters/gcanvas_imageloader_fresco/proguard-rules.pro
vendored
Normal 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
|
||||
@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.taobao.android.gcanvas.adapters.img.picasso" />
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">gcanvas_imageloader_picasso</string>
|
||||
</resources>
|
||||
2
GCanvas/android/adapters/gcanvas_imageloader_picasso/.gitignore
vendored
Normal file
2
GCanvas/android/adapters/gcanvas_imageloader_picasso/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
@ -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"
|
||||
21
GCanvas/android/adapters/gcanvas_imageloader_picasso/proguard-rules.pro
vendored
Normal file
21
GCanvas/android/adapters/gcanvas_imageloader_picasso/proguard-rules.pro
vendored
Normal 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
|
||||
@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.taobao.android.gcanvas.adapters.img.picasso" />
|
||||
@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">gcanvas_imageloader_picasso</string>
|
||||
</resources>
|
||||
2
GCanvas/android/bridge_spec/.gitignore
vendored
Normal file
2
GCanvas/android/bridge_spec/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
56
GCanvas/android/bridge_spec/build.gradle
Normal file
56
GCanvas/android/bridge_spec/build.gradle
Normal 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"
|
||||
21
GCanvas/android/bridge_spec/proguard-rules.pro
vendored
Normal file
21
GCanvas/android/bridge_spec/proguard-rules.pro
vendored
Normal 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
|
||||
2
GCanvas/android/bridge_spec/src/main/AndroidManifest.xml
Normal file
2
GCanvas/android/bridge_spec/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.taobao.gcanvas.spec" />
|
||||
@ -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 {
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
2
GCanvas/android/gcanvas_library/.gitignore
vendored
Normal file
2
GCanvas/android/gcanvas_library/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
18
GCanvas/android/gcanvas_library/AndroidManifest.xml
Normal file
18
GCanvas/android/gcanvas_library/AndroidManifest.xml
Normal 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>
|
||||
84
GCanvas/android/gcanvas_library/build.gradle
Normal file
84
GCanvas/android/gcanvas_library/build.gradle
Normal 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'
|
||||
}
|
||||
}
|
||||
}
|
||||
9
GCanvas/android/gcanvas_library/proguard-rules.txt
Normal file
9
GCanvas/android/gcanvas_library/proguard-rules.txt
Normal file
@ -0,0 +1,9 @@
|
||||
-keep class com.taobao.gcanvas.GCanvasJNI {
|
||||
*** bindTexture(...);
|
||||
*** init(...);
|
||||
*** sendEvent(...);
|
||||
*** setContextType(...);
|
||||
*** setDevicePixelRatio(...);
|
||||
*** texSubImage2D(...);
|
||||
*** registerCallback(...);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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 "";
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": ["react-native"]
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
|
||||
[android]
|
||||
target = Google Inc.:Google APIs:23
|
||||
|
||||
[maven_repositories]
|
||||
central = https://repo1.maven.org/maven2
|
||||
@ -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
|
||||
1
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/.gitattributes
vendored
Normal file
1
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pbxproj -text
|
||||
53
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/.gitignore
vendored
Normal file
53
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/.gitignore
vendored
Normal 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
|
||||
@ -0,0 +1 @@
|
||||
{}
|
||||
106
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/App.js
vendored
Normal file
106
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/App.js
vendored
Normal 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
|
||||
|
||||
}
|
||||
});
|
||||
12
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/__tests__/App.js
vendored
Normal file
12
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/__tests__/App.js
vendored
Normal 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 />
|
||||
);
|
||||
});
|
||||
@ -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",
|
||||
],
|
||||
)
|
||||
@ -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'
|
||||
}
|
||||
70
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/android/app/proguard-rules.pro
vendored
Normal file
70
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/android/app/proguard-rules.pro
vendored
Normal 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.**
|
||||
@ -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>
|
||||
@ -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";
|
||||
}
|
||||
}
|
||||
@ -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 |
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">RNGCanvasSample</string>
|
||||
</resources>
|
||||
@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
164
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/android/gradlew
vendored
Executable file
164
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/android/gradlew
vendored
Executable 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 "$@"
|
||||
90
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/android/gradlew.bat
vendored
Normal file
90
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/android/gradlew.bat
vendored
Normal 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
|
||||
@ -0,0 +1,8 @@
|
||||
keystore(
|
||||
name = "debug",
|
||||
properties = "debug.keystore.properties",
|
||||
store = "debug.keystore",
|
||||
visibility = [
|
||||
"PUBLIC",
|
||||
],
|
||||
)
|
||||
@ -0,0 +1,4 @@
|
||||
key.store=debug.keystore
|
||||
key.alias=androiddebugkey
|
||||
key.store.password=android
|
||||
key.alias.password=android
|
||||
@ -0,0 +1,3 @@
|
||||
rootProject.name = 'RNGCanvasSample'
|
||||
|
||||
include ':app'
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "RNGCanvasSample",
|
||||
"displayName": "RNGCanvasSample"
|
||||
}
|
||||
4
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/index.js
vendored
Normal file
4
GCanvas/bridges/react-native-bridge/examples/RNGCanvasSample/index.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import { AppRegistry } from 'react-native';
|
||||
import App from './App';
|
||||
|
||||
AppRegistry.registerComponent('RNGCanvasSample', () => App);
|
||||
@ -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>
|
||||
@ -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>
|
||||
File diff suppressed because it is too large
Load Diff
@ -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>
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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>
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -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]));
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -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
@ -0,0 +1 @@
|
||||
[GPlatform](https://gplatformteam.github.io/gplatform-website/docs/)
|
||||
2
GCanvas/bridges/react-native-bridge/react-native-gcanvas/android/.gitignore
vendored
Normal file
2
GCanvas/bridges/react-native-bridge/react-native-gcanvas/android/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build
|
||||
*.iml
|
||||
@ -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}"
|
||||
|
||||
}
|
||||
21
GCanvas/bridges/react-native-bridge/react-native-gcanvas/android/proguard-rules.pro
vendored
Normal file
21
GCanvas/bridges/react-native-bridge/react-native-gcanvas/android/proguard-rules.pro
vendored
Normal 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
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user