package triaina.webview.config; import java.io.File; import java.lang.reflect.Method; import com.google.inject.Inject; import triaina.commons.exception.InvalidConfigurationRuntimeException; import triaina.commons.utils.ClassUtils; import triaina.webview.Callback; import triaina.webview.InjectorHelper; import triaina.webview.WebViewBridge; import triaina.webview.annotation.NoPause; import triaina.webview.annotation.Bridge; import triaina.webview.annotation.Domain; import triaina.webview.annotation.Layout; import triaina.webview.bridge.AccelerometerBridge; import triaina.webview.bridge.NetHttpSendBridge; import triaina.webview.bridge.NotificationBridge; import triaina.webview.bridge.ToastBridge; import triaina.webview.bridge.VibratorBridge; import triaina.webview.bridge.WebStatusBridge; import triaina.webview.bridge.WiFiBridge; import triaina.webview.bridge.WiFiP2PBridge; import triaina.webview.entity.Params; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.os.Build; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebSettings; public class WebViewBridgeAnnotationConfigurator implements WebViewBridgeConfigurator { private static final String TAG = WebViewBridgeAnnotationConfigurator.class.getSimpleName(); @Inject private ConfigCache mConfigCache; @Inject private InjectorHelper mInjectorHelper; @Inject private Context mContext; @Override public WebViewBridge loadWebViewBridge(Activity activity) { Class<?> clazz = activity.getClass(); LayoutConfig config = createLayoutConfig(clazz); activity.setContentView(config.getLayoutId()); WebViewBridge webViewBridge = (WebViewBridge) activity .findViewById(config.getWebViewBridgeId()); webViewBridge.setDomainConfig(createDomainConfig(clazz)); webViewBridge.setNoPause(hasNoPause(clazz)); return webViewBridge; } @Override public View loadInflatedView(Fragment fragment, LayoutInflater inflater, ViewGroup container) { Class<?> clazz = fragment.getClass(); LayoutConfig config = createLayoutConfig(clazz); return inflater.inflate(config.getLayoutId(), container, false); } @Override public WebViewBridge loadWebViewBridge(Fragment fragment, View inflatedView) { Class<?> clazz = fragment.getClass(); LayoutConfig config = createLayoutConfig(clazz); WebViewBridge webViewBridge = (WebViewBridge) inflatedView.findViewById(config.getWebViewBridgeId()); webViewBridge.setDomainConfig(createDomainConfig(clazz)); webViewBridge.setNoPause(hasNoPause(clazz)); return webViewBridge; } private LayoutConfig createLayoutConfig(Class<?> clazz) { LayoutConfig config = mConfigCache.getLayoutConfig(clazz); if (config == null) { Log.d(TAG, "Miss cache layout config"); Layout layoutAnn = (Layout) clazz.getAnnotation(Layout.class); if (layoutAnn == null) { throw new InvalidConfigurationRuntimeException( "Must be defined as use layout"); } triaina.webview.annotation.WebViewBridgeResouce bridgeAnn = (triaina.webview.annotation.WebViewBridgeResouce) clazz .getAnnotation(triaina.webview.annotation.WebViewBridgeResouce.class); if (bridgeAnn == null) throw new InvalidConfigurationRuntimeException( "Must be defined as use layout"); config = new LayoutConfig(layoutAnn.value(), bridgeAnn.value()); mConfigCache.putLayoutConfig(clazz, config); } else Log.d(TAG, "Layout config from cache"); return config; } private DomainConfig createDomainConfig(Class<?> clazz) { DomainConfig config = mConfigCache.getDomainConfig(clazz); if (config == null) { Domain domainAnn = (Domain) clazz.getAnnotation(Domain.class); if (domainAnn == null) throw new InvalidConfigurationRuntimeException( "Must be defined as access domain"); config = new DomainConfig(domainAnn.value()); } else Log.d(TAG, "Domain config from cache"); return config; } private boolean hasNoPause(Class<?> clazz) { return clazz.getAnnotation(NoPause.class) != null; } @Override public void configure(WebViewBridge bridge) { registerBridge(bridge, mContext); registerBridge(bridge, new WebStatusBridge(bridge)); registerBridge(bridge, new NetHttpSendBridge(bridge)); registerBridge(bridge, new WiFiBridge(bridge)); registerBridge(bridge, new VibratorBridge()); registerBridge(bridge, new ToastBridge()); registerBridge(bridge, new AccelerometerBridge(bridge)); registerBridge(bridge, new NotificationBridge(bridge)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) registerBridge(bridge, new WiFiP2PBridge(bridge)); } @Override public void registerBridge(WebViewBridge webViewBridge, Object bridgeObject) { bridgeObject = mInjectorHelper.inject(mContext, bridgeObject); Class<?> clazz = bridgeObject.getClass(); BridgeObjectConfig config = mConfigCache.getBridgeObjectConfig(clazz); if (config == null) { Log.d(TAG, "Miss cache bridge config"); config = createBridgeConfig(bridgeObject); mConfigCache.putBridgeObjectConfig(clazz, config); } else Log.d(TAG, "Bridge config from cache"); webViewBridge.addBridgeObjectConfig(bridgeObject, config); } private BridgeObjectConfig createBridgeConfig(Object bridgeObject) { Class<?> clazz = bridgeObject.getClass(); Method[] methods = clazz.getMethods(); BridgeObjectConfig configSet = new BridgeObjectConfig(); for (Method method : methods) { Bridge chAnn = method.getAnnotation(Bridge.class); if (chAnn != null) { if (validDestinationMethod(method)) { Log.d(TAG, "dest:" + chAnn.value()); Log.d(TAG, "method:" + method.getName()); BridgeMethodConfig config = new BridgeMethodConfig( chAnn.value(), method); configSet.add(config); } } } return configSet; } private boolean validDestinationMethod(Method method) { Class<?>[] argTypes = method.getParameterTypes(); switch (argTypes.length) { case 2: if (!ClassUtils.isImplement(argTypes[1], Callback.class)) return false; case 1: if (!ClassUtils.isImplement(argTypes[0], Params.class) && !ClassUtils.isImplement(argTypes[0], Callback.class)) return false; case 0: break; default: Log.w(TAG, "dest:" + method.getName() + " has illegal arguments"); return false; } return true; } @SuppressLint("SetJavaScriptEnabled") public void configureSetting(WebViewBridge webViewBridge) { WebSettings settings = webViewBridge.getSettings(); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setGeolocationEnabled(true); settings.setDomStorageEnabled(true); File databasePath = new File(mContext.getCacheDir(), "webstorage"); settings.setDatabasePath(databasePath.toString()); settings.setAppCacheEnabled(true); settings.setAppCacheMaxSize(1024 * 1024 * 4); } // for test public void setInjectorHelper(InjectorHelper injectorHelper) { mInjectorHelper = injectorHelper; } // for test public void setContext(Context context) { mContext = context; } // for test public void setConfigCache(ConfigCache configCache) { mConfigCache = configCache; } }