blob: 1c4d062f21fa90812526d12fe824a91007121f95 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.taobao.weex.ui.component;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.adapter.IWXImgLoaderAdapter;
import com.taobao.weex.adapter.URIAdapter;
import com.taobao.weex.annotation.Component;
import com.taobao.weex.annotation.JSMethod;
import com.taobao.weex.bridge.JSCallback;
import com.taobao.weex.common.Constants;
import com.taobao.weex.common.WXImageSharpen;
import com.taobao.weex.common.WXImageStrategy;
import com.taobao.weex.common.WXRuntimeException;
import com.taobao.weex.dom.ImmutableDomObject;
import com.taobao.weex.dom.WXDomObject;
import com.taobao.weex.ui.ComponentCreator;
import com.taobao.weex.ui.view.WXImageView;
import com.taobao.weex.ui.view.border.BorderDrawable;
import com.taobao.weex.utils.ImageDrawable;
import com.taobao.weex.utils.ImgURIUtil;
import com.taobao.weex.utils.SingleFunctionParser;
import com.taobao.weex.utils.WXViewToImageUtil;
import com.taobao.weex.utils.WXDomUtils;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.WXUtils;
import com.taobao.weex.utils.WXViewUtils;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Image component
*/
@Component(lazyload = false)
public class WXImage extends WXComponent<ImageView> {
public static final String SUCCEED = "success";
public static final String ERRORDESC = "errorDesc";
private static final int WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 0x2;
private String mSrc;
private int mBlurRadius;
private static SingleFunctionParser.FlatMapper<Integer> BLUR_RADIUS_MAPPER = new SingleFunctionParser.FlatMapper<Integer>() {
@Override
public Integer map(String raw) {
return WXUtils.getInteger(raw,0);
}
};
public static class Ceator implements ComponentCreator {
public WXComponent createInstance(WXSDKInstance instance, WXDomObject node, WXVContainer parent) throws IllegalAccessException, InvocationTargetException, InstantiationException {
return new WXImage(instance,node,parent);
}
}
@Deprecated
public WXImage(WXSDKInstance instance, WXDomObject dom, WXVContainer parent, String instanceId, boolean isLazy) {
this(instance,dom,parent);
}
public WXImage(WXSDKInstance instance, WXDomObject node,
WXVContainer parent) {
super(instance, node, parent);
}
@Override
protected ImageView initComponentHostView(@NonNull Context context) {
WXImageView view = new WXImageView(context);
view.setScaleType(ScaleType.FIT_XY);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.setCropToPadding(true);
}
view.holdComponent(this);
return view;
}
@Override
protected boolean setProperty(String key, Object param) {
switch (key) {
case Constants.Name.RESIZE_MODE:
String resize_mode = WXUtils.getString(param, null);
if (resize_mode != null)
setResizeMode(resize_mode);
return true;
case Constants.Name.RESIZE:
String resize = WXUtils.getString(param, null);
if (resize != null)
setResize(resize);
return true;
case Constants.Name.SRC:
String src = WXUtils.getString(param, null);
if (src != null)
setSrc(src);
return true;
case Constants.Name.IMAGE_QUALITY:
return true;
case Constants.Name.FILTER:
int blurRadius = 0;
if(param != null && param instanceof String) {
blurRadius = parseBlurRadius((String)param);
}
if(!TextUtils.isEmpty(this.mSrc)) {
setBlurRadius(this.mSrc,blurRadius);
}
return true;
}
return super.setProperty(key, param);
}
@Override
public void refreshData(WXComponent component) {
super.refreshData(component);
if (component instanceof WXImage) {
setSrc(component.getDomObject().getAttrs().getImageSrc());
}
}
@WXComponentProp(name = Constants.Name.RESIZE_MODE)
public void setResizeMode(String resizeMode) {
(getHostView()).setScaleType(getResizeMode(resizeMode));
}
private ScaleType getResizeMode(String resizeMode) {
ScaleType scaleType = ScaleType.FIT_XY;
if (TextUtils.isEmpty(resizeMode)) {
return scaleType;
}
switch (resizeMode) {
case "cover":
scaleType = ScaleType.CENTER_CROP;
break;
case "contain":
scaleType = ScaleType.FIT_CENTER;
break;
case "stretch":
scaleType = ScaleType.FIT_XY;
break;
}
return scaleType;
}
@WXComponentProp(name = Constants.Name.RESIZE)
public void setResize(String resize) {
(getHostView()).setScaleType(getResizeMode(resize));
}
/**
* Process local scheme, load drawable.
* @param rewrited
*/
private void setLocalSrc(Uri rewrited) {
ImageView imageView;
Drawable localDrawable = ImgURIUtil.getDrawableFromLoaclSrc(getContext(), rewrited);
if (localDrawable != null && (imageView = getHostView()) != null) {
imageView.setImageDrawable(localDrawable);
}
}
@WXComponentProp(name = Constants.Name.SRC)
public void setSrc(String src) {
if (src == null) {
return;
}
ImageView image = getHostView();
if("".equals(src) && image != null){
image.setImageDrawable(null);
return;
}
if(image != null){
if(image.getDrawable() != null){
image.setImageDrawable(null);
}
}
this.mSrc = src;
WXSDKInstance instance = getInstance();
Uri rewrited = instance.rewriteUri(Uri.parse(src), URIAdapter.IMAGE);
if (Constants.Scheme.LOCAL.equals(rewrited.getScheme())) {
setLocalSrc(rewrited);
} else {
int blur = 0;
if(getDomObject() != null) {
String blurStr = getDomObject().getStyles().getBlur();
blur = parseBlurRadius(blurStr);
}
setRemoteSrc(rewrited, blur);
}
}
private void setBlurRadius(@NonNull String src, int blurRadius) {
if(getInstance() != null && blurRadius != mBlurRadius) {
Uri parsedUri = getInstance().rewriteUri(Uri.parse(src), URIAdapter.IMAGE);
if(!Constants.Scheme.LOCAL.equals(parsedUri.getScheme())) {
setRemoteSrc(parsedUri,blurRadius);
}
}
}
private int parseBlurRadius(@Nullable String rawRadius) {
if(rawRadius == null) {
return 0;
}
SingleFunctionParser<Integer> parser = new SingleFunctionParser<Integer>(rawRadius,BLUR_RADIUS_MAPPER);
List<Integer> list = null;
try {
list = parser.parse("blur");
}catch (Exception e) {
return 0;
}
if(list == null || list.isEmpty()) {
return 0;
}
return list.get(0);
}
@Override
public void recycled() {
super.recycled();
if (getInstance().getImgLoaderAdapter() != null) {
getInstance().getImgLoaderAdapter().setImage(null, mHost,
null, null);
} else {
if (WXEnvironment.isApkDebugable()) {
throw new WXRuntimeException("getImgLoaderAdapter() == null");
}
WXLogUtils.e("Error getImgLoaderAdapter() == null");
}
}
private void setRemoteSrc(Uri rewrited,int blurRadius) {
WXImageStrategy imageStrategy = new WXImageStrategy();
imageStrategy.isClipping = true;
WXImageSharpen imageSharpen = getDomObject().getAttrs().getImageSharpen();
imageStrategy.isSharpen = imageSharpen == WXImageSharpen.SHARPEN;
imageStrategy.blurRadius = Math.max(0, blurRadius);
this.mBlurRadius = blurRadius;
imageStrategy.setImageListener(new WXImageStrategy.ImageListener() {
@Override
public void onImageFinish(String url, ImageView imageView, boolean result, Map extra) {
if (getDomObject() != null && getDomObject().getEvents().contains(Constants.Event.ONLOAD)) {
Map<String, Object> params = new HashMap<String, Object>();
Map<String, Object> size = new HashMap<>(2);
if (imageView != null && imageView instanceof Measurable) {
size.put("naturalWidth", ((Measurable) imageView).getNaturalWidth());
size.put("naturalHeight", ((Measurable) imageView).getNaturalHeight());
} else {
size.put("naturalWidth", 0);
size.put("naturalHeight", 0);
}
if (getDomObject() != null && containsEvent(Constants.Event.ONLOAD)) {
params.put("success", result);
params.put("size", size);
fireEvent(Constants.Event.ONLOAD, params);
}
}
}
});
String placeholder=null;
if(getDomObject().getAttrs().containsKey(Constants.Name.PLACEHOLDER)){
placeholder= (String) getDomObject().getAttrs().get(Constants.Name.PLACEHOLDER);
}else if(getDomObject().getAttrs().containsKey(Constants.Name.PLACE_HOLDER)){
placeholder=(String)getDomObject().getAttrs().get(Constants.Name.PLACE_HOLDER);
}
if(placeholder!=null){
imageStrategy.placeHolder = getInstance().rewriteUri(Uri.parse(placeholder),URIAdapter.IMAGE).toString();
}
IWXImgLoaderAdapter imgLoaderAdapter = getInstance().getImgLoaderAdapter();
if (imgLoaderAdapter != null) {
imgLoaderAdapter.setImage(rewrited.toString(), getHostView(),
getDomObject().getAttrs().getImageQuality(), imageStrategy);
}
}
@Override
public void updateProperties(Map<String, Object> props) {
super.updateProperties(props);
WXImageView imageView;
ImmutableDomObject imageDom;
if ((imageDom = getDomObject()) != null &&
getHostView() instanceof WXImageView) {
imageView = (WXImageView) getHostView();
BorderDrawable borderDrawable = WXViewUtils.getBorderDrawable(getHostView());
float[] borderRadius;
if (borderDrawable != null) {
RectF borderBox = new RectF(0, 0, WXDomUtils.getContentWidth(imageDom), WXDomUtils.getContentHeight(imageDom));
borderRadius = borderDrawable.getBorderRadius(borderBox);
} else {
borderRadius = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
}
imageView.setBorderRadius(borderRadius);
if (imageView.getDrawable() instanceof ImageDrawable) {
ImageDrawable imageDrawable = (ImageDrawable) imageView.getDrawable();
float[] previousRadius = imageDrawable.getCornerRadii();
if (!Arrays.equals(previousRadius, borderRadius)) {
imageDrawable.setCornerRadii(borderRadius);
}
}
readyToRender();
}
}
/**
* Need permission {android.permission.WRITE_EXTERNAL_STORAGE}
*/
@JSMethod(uiThread = false)
public void save(final JSCallback saveStatuCallback) {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (getContext() instanceof Activity) {
ActivityCompat.requestPermissions((Activity) getContext(),
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
}
}
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (saveStatuCallback != null) {
Map<String, Object> result = new HashMap<>();
result.put(SUCCEED, false);
result.put(ERRORDESC,"Permission denied: android.permission.WRITE_EXTERNAL_STORAGE");
saveStatuCallback.invoke(result);
}
return;
}
if (mHost == null) {
if (saveStatuCallback != null) {
Map<String, Object> result = new HashMap<>();
result.put(SUCCEED, false);
result.put(ERRORDESC,"Image component not initialized");
saveStatuCallback.invoke(result);
}
return;
}
if (mSrc == null || mSrc.equals("")) {
if (saveStatuCallback != null) {
Map<String, Object> result = new HashMap<>();
result.put(SUCCEED, false);
result.put(ERRORDESC,"Image does not have the correct src");
saveStatuCallback.invoke(result);
}
return;
}
WXViewToImageUtil.generateImage(mHost, 0, 0xfff8f8f8, new WXViewToImageUtil.OnImageSavedCallback() {
@Override
public void onSaveSucceed(String path) {
if (saveStatuCallback != null) {
Map<String, Object> result = new HashMap<>();
result.put(SUCCEED, true);
saveStatuCallback.invoke(result);
}
}
@Override
public void onSaveFailed(String errorDesc) {
if (saveStatuCallback != null) {
Map<String, Object> result = new HashMap<>();
result.put(SUCCEED, false);
result.put(ERRORDESC,errorDesc);
saveStatuCallback.invoke(result);
}
}
});
}
public interface Measurable {
int getNaturalWidth();
int getNaturalHeight();
}
}