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.core.internal.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.content.res.Resources;
12 import android.content.res.Resources.NotFoundException;
13 import android.util.Log;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.Class;
21 import java.util.HashMap;
23 import org.json.JSONArray;
24 import org.json.JSONException;
25 import org.json.JSONObject;
26 import org.xwalk.core.internal.extension.api.contacts.Contacts;
27 import org.xwalk.core.internal.extension.api.device_capabilities.DeviceCapabilities;
28 import org.xwalk.core.internal.extension.api.launchscreen.LaunchScreenExtension;
29 import org.xwalk.core.internal.extension.api.messaging.Messaging;
30 import org.xwalk.core.internal.extension.api.presentation.PresentationExtension;
33 * This internal class acts a manager to manage extensions.
35 public class XWalkExtensionManager implements XWalkExtensionContext {
36 private final static String TAG = "XWalkExtensionManager";
37 private final static String EXTENSION_CONFIG_FILE = "extensions-config.json";
38 // This class name is from runtime client. Need to keep consistency with it.
39 private final static String EXTENSION_CONTEXT_CLIENT_CLASS_NAME =
40 "org.xwalk.app.runtime.extension.XWalkExtensionContextClient";
42 private final Context mContext;
43 private final Activity mActivity;
45 private final HashMap<String, XWalkExtensionBridge> mExtensions = new HashMap<String, XWalkExtensionBridge>();
46 // This variable is to set whether to load external extensions. The default is true.
47 private boolean mLoadExternalExtensions;
49 public XWalkExtensionManager(Context context, Activity activity) {
52 mLoadExternalExtensions = true;
56 public void registerExtension(XWalkExtension extension) {
57 if (mExtensions.get(extension.getExtensionName()) != null) {
58 Log.e(TAG, extension.getExtensionName() + "is already registered!");
62 XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
63 mExtensions.put(extension.getExtensionName(), bridge);
67 public void unregisterExtension(String name) {
68 XWalkExtensionBridge bridge = mExtensions.get(name);
70 mExtensions.remove(name);
76 public Context getContext() {
81 public Activity getActivity() {
86 public void postMessage(XWalkExtension extension, int instanceID, String message) {
87 XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
88 if (bridge != null) bridge.postMessage(instanceID, message);
91 public void broadcastMessage(XWalkExtension extension, String message) {
92 XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
93 if (bridge != null) bridge.broadcastMessage(message);
96 public void onStart() {
97 for(XWalkExtensionBridge extension: mExtensions.values()) {
102 public void onResume() {
103 for(XWalkExtensionBridge extension: mExtensions.values()) {
104 extension.onResume();
108 public void onPause() {
109 for(XWalkExtensionBridge extension: mExtensions.values()) {
114 public void onStop() {
115 for(XWalkExtensionBridge extension: mExtensions.values()) {
120 public void onDestroy() {
121 for(XWalkExtensionBridge extension: mExtensions.values()) {
122 extension.onDestroy();
127 public void onActivityResult(int requestCode, int resultCode, Intent data) {
128 for(XWalkExtensionBridge extension: mExtensions.values()) {
129 extension.onActivityResult(requestCode, resultCode, data);
133 public void loadExtensions() {
134 loadInternalExtensions();
135 loadExternalExtensions();
138 public void setAllowExternalExtensions(boolean load) {
139 mLoadExternalExtensions = load;
142 private void loadInternalExtensions() {
143 // Create all extension instances directly here. The internal extension will register
144 // itself and add itself to XWalkExtensionManager.mExtensions automatically.
145 // The following sample shows how to create an extension that named Device:
146 // String jsApiContent = "";
148 // jsApiContent = getExtensionJSFileContent(mContext, Device.JS_API_PATH, true);
149 // new Device(jsApiContent, mExtensionContextImpl);
150 // } catch(IOException e) {
151 // Log.w(TAG, "Failed to read js API file of internal extension: Device");
154 String jsApiContent = "";
156 jsApiContent = getExtensionJSFileContent(
157 mContext, PresentationExtension.JS_API_PATH, true);
158 new PresentationExtension(jsApiContent, this);
159 } catch (IOException e) {
160 Log.w(TAG, "Failed to read JS API file: " + PresentationExtension.JS_API_PATH);
165 String jsApiContent = "";
167 jsApiContent = getExtensionJSFileContent(
168 mContext, LaunchScreenExtension.JS_API_PATH, true);
169 new LaunchScreenExtension(jsApiContent, this);
170 } catch (IOException e) {
171 Log.w(TAG, "Failed to read JS API file: " + LaunchScreenExtension.JS_API_PATH);
176 String jsApiContent = "";
178 jsApiContent = getExtensionJSFileContent(
179 mContext, Contacts.JS_API_PATH, true);
180 new Contacts(jsApiContent, this);
181 } catch(IOException e) {
182 Log.w(TAG, "Failed to read JS API file: " + Contacts.JS_API_PATH);
187 String jsApiContent = "";
189 jsApiContent = getExtensionJSFileContent(
190 mContext, DeviceCapabilities.JS_API_PATH, true);
191 new DeviceCapabilities(jsApiContent, this);
192 } catch(IOException e) {
193 Log.w(TAG, "Failed to read JS API file: " + DeviceCapabilities.JS_API_PATH);
198 String jsApiContent = "";
200 jsApiContent = getExtensionJSFileContent(
201 mContext, Messaging.JS_API_PATH, true);
202 new Messaging(jsApiContent, this);
203 } catch(IOException e) {
204 Log.w(TAG, "Failed to read JS API file: " + Messaging.JS_API_PATH);
209 private void loadExternalExtensions() {
210 if (!mLoadExternalExtensions) return;
212 // Read extensions-config.json and create external extensions.
213 String configFileContent;
215 configFileContent = getExtensionJSFileContent(mActivity, EXTENSION_CONFIG_FILE, false);
216 } catch (IOException e) {
217 Log.w(TAG, "Failed to read extensions-config.json");
221 // Initialize the context for external extensions.
222 XWalkExtensionContextWrapper contextWrapper =
223 new XWalkExtensionContextWrapper(this);
224 Object contextClient = createExtensionContextClient(contextWrapper);
227 JSONArray jsonFeatures = new JSONArray(configFileContent);
228 int extensionCount = jsonFeatures.length();
229 for (int i = 0; i < extensionCount; i++) {
230 JSONObject jsonObject = jsonFeatures.getJSONObject(i);
231 String name = jsonObject.getString("name");
232 String className = jsonObject.getString("class");
233 String jsApiFile = jsonObject.getString("jsapi");
235 // Load the content of the JavaScript file.
238 jsApi = getExtensionJSFileContent(mActivity, jsApiFile, false);
239 } catch (IOException e) {
240 Log.w(TAG, "Failed to read the file " + jsApiFile);
244 if (name != null && className != null && jsApi != null) {
245 createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
248 } catch (JSONException e) {
249 Log.w(TAG, "Failed to parse extensions-config.json");
253 private String getExtensionJSFileContent(Context context, String fileName, boolean fromRaw)
256 InputStream inputStream = null;
259 // If fromRaw is true, Try to find js file in res/raw first.
260 // And then try to get it from assets if failed.
261 Resources resource = context.getResources();
262 String resName = (new File(fileName).getName().split("\\."))[0];
263 int resId = resource.getIdentifier(resName, "raw", context.getPackageName());
266 inputStream = resource.openRawResource(resId);
267 } catch (NotFoundException e) {
268 Log.w(TAG, "Inputstream failed to open for R.raw." + resName +
269 ", try to find it in assets");
273 if (inputStream == null) {
274 AssetManager assetManager = context.getAssets();
275 inputStream = assetManager.open(fileName);
277 int size = inputStream.available();
278 byte[] buffer = new byte[size];
279 inputStream.read(buffer);
280 result = new String(buffer);
282 if (inputStream != null) {
289 private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
290 Activity activity = contextWrapper.getActivity();
292 Class<?> clazz = activity.getClassLoader().loadClass(EXTENSION_CONTEXT_CLIENT_CLASS_NAME);
293 Constructor<?> constructor = clazz.getConstructor(Activity.class, Object.class);
294 return constructor.newInstance(activity, contextWrapper);
295 } catch (ClassNotFoundException e) {
297 } catch (IllegalAccessException e) {
299 } catch (InstantiationException e) {
301 } catch (InvocationTargetException e) {
303 } catch (NoSuchMethodException e) {
309 private void createExternalExtension(String name, String className, String jsApi,
310 Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
311 Activity activity = contextWrapper.getActivity();
313 Class<?> clazz = activity.getClassLoader().loadClass(className);
314 Constructor<?> constructor = clazz.getConstructor(String.class,
315 String.class, contextClient.getClass());
316 constructor.newInstance(name, jsApi, contextClient);
317 } catch (ClassNotFoundException e) {
319 } catch (IllegalAccessException e) {
321 } catch (InstantiationException e) {
323 } catch (InvocationTargetException e) {
325 } catch (NoSuchMethodException e) {
330 private static void handleException(Exception e) {
331 // TODO(yongsheng): Handle exceptions here.
332 Log.e(TAG, "Error in calling methods of external extensions. " + e.toString());