1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.xwalk.runtime.extension;
7 import android.app.Activity;
8 import android.content.Context;
9 import android.content.Intent;
10 import android.content.res.AssetManager;
11 import android.util.Log;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.lang.reflect.Constructor;
16 import java.lang.reflect.InvocationTargetException;
17 import java.lang.reflect.Method;
18 import java.lang.Class;
19 import java.util.HashMap;
21 import org.json.JSONArray;
22 import org.json.JSONException;
23 import org.json.JSONObject;
24 import org.xwalk.runtime.extension.api.contacts.Contacts;
25 import org.xwalk.runtime.extension.api.device_capabilities.DeviceCapabilities;
26 import org.xwalk.runtime.extension.api.messaging.Messaging;
27 import org.xwalk.runtime.extension.api.presentation.PresentationExtension;
28 import org.xwalk.runtime.extension.api.screenorientation.ScreenOrientationExtension;
31 * This internal class acts a manager to manage extensions.
33 public class XWalkExtensionManager implements XWalkExtensionContext {
34 private final static String TAG = "XWalkExtensionManager";
35 private final static String EXTENSION_CONFIG_FILE = "extensions-config.json";
36 // This class name is from runtime client. Need to keep consistency with it.
37 private final static String EXTENSION_CONTEXT_CLIENT_CLASS_NAME =
38 "org.xwalk.app.runtime.extension.XWalkExtensionContextClient";
40 private final Context mContext;
41 private final Activity mActivity;
43 private final HashMap<String, XWalkExtensionBridge> mExtensions = new HashMap<String, XWalkExtensionBridge>();
44 // This variable is to set whether to load external extensions. The default is true.
45 private boolean mLoadExternalExtensions;
47 public XWalkExtensionManager(Context context, Activity activity) {
50 mLoadExternalExtensions = true;
54 public void registerExtension(XWalkExtension extension) {
55 if (mExtensions.get(extension.getExtensionName()) != null) {
56 Log.e(TAG, extension.getExtensionName() + "is already registered!");
60 XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
61 mExtensions.put(extension.getExtensionName(), bridge);
65 public void unregisterExtension(String name) {
66 XWalkExtensionBridge bridge = mExtensions.get(name);
68 mExtensions.remove(name);
74 public Context getContext() {
79 public Activity getActivity() {
84 public void postMessage(XWalkExtension extension, int instanceID, String message) {
85 XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
86 if (bridge != null) bridge.postMessage(instanceID, message);
89 public void broadcastMessage(XWalkExtension extension, String message) {
90 XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
91 if (bridge != null) bridge.broadcastMessage(message);
94 public void onResume() {
95 for(XWalkExtensionBridge extension: mExtensions.values()) {
100 public void onPause() {
101 for(XWalkExtensionBridge extension: mExtensions.values()) {
106 public void onDestroy() {
107 for(XWalkExtensionBridge extension: mExtensions.values()) {
108 extension.onDestroy();
113 public void onActivityResult(int requestCode, int resultCode, Intent data) {
114 for(XWalkExtensionBridge extension: mExtensions.values()) {
115 extension.onActivityResult(requestCode, resultCode, data);
119 public void loadExtensions() {
120 loadInternalExtensions();
121 loadExternalExtensions();
124 public void setAllowExternalExtensions(boolean load) {
125 mLoadExternalExtensions = load;
128 private void loadInternalExtensions() {
129 // Create all extension instances directly here. The internal extension will register
130 // itself and add itself to XWalkExtensionManager.mExtensions automatically.
131 // The following sample shows how to create an extension that named Device:
132 // String jsApiContent = "";
134 // jsApiContent = getAssetsFileContent(mContext.getAssets(), Device.JS_API_PATH);
135 // new Device(jsApiContent, mExtensionContextImpl);
136 // } catch(IOException e) {
137 // Log.e(TAG, "Failed to read js API file of internal extension: Device");
140 String jsApiContent = "";
142 jsApiContent = getAssetsFileContent(mContext.getAssets(),
143 PresentationExtension.JS_API_PATH);
144 // Load PresentationExtension as an internal extension.
145 new PresentationExtension(PresentationExtension.NAME, jsApiContent, this);
146 } catch (IOException e) {
147 Log.e(TAG, "Failed to read JS API file: " + PresentationExtension.JS_API_PATH);
152 String jsApiContent = ScreenOrientationExtension.getInsertedString();
154 jsApiContent += getAssetsFileContent(mContext.getAssets(),
155 ScreenOrientationExtension.JS_API_PATH);
156 new ScreenOrientationExtension(ScreenOrientationExtension.NAME, jsApiContent,
157 ScreenOrientationExtension.JS_ENTRY_POINTS, this);
158 } catch (IOException e) {
159 Log.e(TAG, "Failed to read JS API file: " + ScreenOrientationExtension.JS_API_PATH);
164 String jsApiContent = "";
166 jsApiContent = getAssetsFileContent(mContext.getAssets(),
167 Contacts.JS_API_PATH);
168 new Contacts(jsApiContent, this);
169 } catch(IOException e) {
170 Log.e(TAG, "Failed to read JS API file: " + Contacts.JS_API_PATH);
175 String jsApiContent = "";
177 jsApiContent = getAssetsFileContent(mContext.getAssets(),
178 DeviceCapabilities.JS_API_PATH);
179 new DeviceCapabilities(jsApiContent, this);
180 } catch(IOException e) {
181 Log.e(TAG, "Failed to read JS API file: " + DeviceCapabilities.JS_API_PATH);
185 String jsApiContent = "";
187 jsApiContent = getAssetsFileContent(mContext.getAssets(),
188 Messaging.JS_API_PATH);
189 new Messaging(jsApiContent, this);
190 } catch(IOException e) {
191 Log.e(TAG, "Failed to read JS API file: " + Messaging.JS_API_PATH);
196 private void loadExternalExtensions() {
197 if (!mLoadExternalExtensions) return;
199 // Read extensions-config.json and create external extensions.
200 String configFileContent;
202 configFileContent = getAssetsFileContent(mActivity.getAssets(), EXTENSION_CONFIG_FILE);
203 } catch (IOException e) {
204 Log.e(TAG, "Failed to read extensions-config.json");
208 // Initialize the context for external extensions.
209 XWalkExtensionContextWrapper contextWrapper =
210 new XWalkExtensionContextWrapper(this);
211 Object contextClient = createExtensionContextClient(contextWrapper);
214 JSONArray jsonFeatures = new JSONArray(configFileContent);
215 int extensionCount = jsonFeatures.length();
216 for (int i = 0; i < extensionCount; i++) {
217 JSONObject jsonObject = jsonFeatures.getJSONObject(i);
218 String name = jsonObject.getString("name");
219 String className = jsonObject.getString("class");
220 String jsApiFile = jsonObject.getString("jsapi");
222 // Load the content of the JavaScript file.
225 jsApi = getAssetsFileContent(mActivity.getAssets(), jsApiFile);
226 } catch (IOException e) {
227 Log.e(TAG, "Failed to read the file " + jsApiFile);
231 if (name != null && className != null && jsApi != null) {
232 createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
235 } catch (JSONException e) {
236 Log.e(TAG, "Failed to parse extensions-config.json");
240 private String getAssetsFileContent(AssetManager assetManager, String fileName) throws IOException {
242 InputStream inputStream = null;
244 inputStream = assetManager.open(fileName);
245 int size = inputStream.available();
246 byte[] buffer = new byte[size];
247 inputStream.read(buffer);
248 result = new String(buffer);
250 if (inputStream != null) {
257 private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
258 Activity activity = contextWrapper.getActivity();
260 Class<?> clazz = activity.getClassLoader().loadClass(EXTENSION_CONTEXT_CLIENT_CLASS_NAME);
261 Constructor<?> constructor = clazz.getConstructor(Activity.class, Object.class);
262 return constructor.newInstance(activity, contextWrapper);
263 } catch (ClassNotFoundException e) {
265 } catch (IllegalAccessException e) {
267 } catch (InstantiationException e) {
269 } catch (InvocationTargetException e) {
271 } catch (NoSuchMethodException e) {
277 private void createExternalExtension(String name, String className, String jsApi,
278 Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
279 Activity activity = contextWrapper.getActivity();
281 Class<?> clazz = activity.getClassLoader().loadClass(className);
282 Constructor<?> constructor = clazz.getConstructor(String.class,
283 String.class, contextClient.getClass());
284 constructor.newInstance(name, jsApi, contextClient);
285 } catch (ClassNotFoundException e) {
287 } catch (IllegalAccessException e) {
289 } catch (InstantiationException e) {
291 } catch (InvocationTargetException e) {
293 } catch (NoSuchMethodException e) {
298 private static void handleException(Exception e) {
299 // TODO(yongsheng): Handle exceptions here.
300 Log.e(TAG, "Error in calling methods of external extensions. " + e.toString());