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.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.extension.api.contacts.Contacts;
27 import org.xwalk.core.extension.api.device_capabilities.DeviceCapabilities;
28 import org.xwalk.core.extension.api.launchscreen.LaunchScreenExtension;
29 import org.xwalk.core.extension.api.messaging.Messaging;
30 import org.xwalk.core.extension.api.presentation.PresentationExtension;
31 import org.xwalk.core.extension.api.screenorientation.ScreenOrientationExtension;
34 * This internal class acts a manager to manage extensions.
36 public class XWalkExtensionManager implements XWalkExtensionContext {
37 private final static String TAG = "XWalkExtensionManager";
38 private final static String EXTENSION_CONFIG_FILE = "extensions-config.json";
39 // This class name is from runtime client. Need to keep consistency with it.
40 private final static String EXTENSION_CONTEXT_CLIENT_CLASS_NAME =
41 "org.xwalk.app.runtime.extension.XWalkExtensionContextClient";
43 private final Context mContext;
44 private final Activity mActivity;
46 private final HashMap<String, XWalkExtensionBridge> mExtensions = new HashMap<String, XWalkExtensionBridge>();
47 // This variable is to set whether to load external extensions. The default is true.
48 private boolean mLoadExternalExtensions;
50 public XWalkExtensionManager(Context context, Activity activity) {
53 mLoadExternalExtensions = true;
57 public void registerExtension(XWalkExtension extension) {
58 if (mExtensions.get(extension.getExtensionName()) != null) {
59 Log.e(TAG, extension.getExtensionName() + "is already registered!");
63 XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
64 mExtensions.put(extension.getExtensionName(), bridge);
68 public void unregisterExtension(String name) {
69 XWalkExtensionBridge bridge = mExtensions.get(name);
71 mExtensions.remove(name);
77 public Context getContext() {
82 public Activity getActivity() {
87 public void postMessage(XWalkExtension extension, int instanceID, String message) {
88 XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
89 if (bridge != null) bridge.postMessage(instanceID, message);
92 public void broadcastMessage(XWalkExtension extension, String message) {
93 XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
94 if (bridge != null) bridge.broadcastMessage(message);
97 public void onResume() {
98 for(XWalkExtensionBridge extension: mExtensions.values()) {
103 public void onPause() {
104 for(XWalkExtensionBridge extension: mExtensions.values()) {
109 public void onDestroy() {
110 for(XWalkExtensionBridge extension: mExtensions.values()) {
111 extension.onDestroy();
116 public void onActivityResult(int requestCode, int resultCode, Intent data) {
117 for(XWalkExtensionBridge extension: mExtensions.values()) {
118 extension.onActivityResult(requestCode, resultCode, data);
122 public void loadExtensions() {
123 loadInternalExtensions();
124 loadExternalExtensions();
127 public void setAllowExternalExtensions(boolean load) {
128 mLoadExternalExtensions = load;
131 private void loadInternalExtensions() {
132 // Create all extension instances directly here. The internal extension will register
133 // itself and add itself to XWalkExtensionManager.mExtensions automatically.
134 // The following sample shows how to create an extension that named Device:
135 // String jsApiContent = "";
137 // jsApiContent = getExtensionJSFileContent(mContext, Device.JS_API_PATH, true);
138 // new Device(jsApiContent, mExtensionContextImpl);
139 // } catch(IOException e) {
140 // Log.e(TAG, "Failed to read js API file of internal extension: Device");
143 String jsApiContent = "";
145 jsApiContent = getExtensionJSFileContent(
146 mContext, PresentationExtension.JS_API_PATH, true);
147 // Load PresentationExtension as an internal extension.
148 new PresentationExtension(PresentationExtension.NAME, jsApiContent, this);
149 } catch (IOException e) {
150 Log.e(TAG, "Failed to read JS API file: " + PresentationExtension.JS_API_PATH);
155 String jsApiContent = ScreenOrientationExtension.getInsertedString();
157 jsApiContent += getExtensionJSFileContent(
158 mContext, ScreenOrientationExtension.JS_API_PATH, true);
159 new ScreenOrientationExtension(ScreenOrientationExtension.NAME, jsApiContent,
160 ScreenOrientationExtension.JS_ENTRY_POINTS, this);
161 } catch (IOException e) {
162 Log.e(TAG, "Failed to read JS API file: " + ScreenOrientationExtension.JS_API_PATH);
167 String jsApiContent = "";
169 jsApiContent = getExtensionJSFileContent(
170 mContext, LaunchScreenExtension.JS_API_PATH, true);
171 // Load LaunchscreenExtension as an internal extension.
172 new LaunchScreenExtension(LaunchScreenExtension.NAME, jsApiContent,
173 LaunchScreenExtension.JS_ENTRY_POINTS, this);
174 } catch (IOException e) {
175 Log.e(TAG, "Failed to read JS API file: " + LaunchScreenExtension.JS_API_PATH);
180 String jsApiContent = "";
182 jsApiContent = getExtensionJSFileContent(
183 mContext, Contacts.JS_API_PATH, true);
184 new Contacts(jsApiContent, this);
185 } catch(IOException e) {
186 Log.e(TAG, "Failed to read JS API file: " + Contacts.JS_API_PATH);
191 String jsApiContent = "";
193 jsApiContent = getExtensionJSFileContent(
194 mContext, DeviceCapabilities.JS_API_PATH, true);
195 new DeviceCapabilities(jsApiContent, this);
196 } catch(IOException e) {
197 Log.e(TAG, "Failed to read JS API file: " + DeviceCapabilities.JS_API_PATH);
201 String jsApiContent = "";
203 jsApiContent = getExtensionJSFileContent(
204 mContext, Messaging.JS_API_PATH, true);
205 new Messaging(jsApiContent, this);
206 } catch(IOException e) {
207 Log.e(TAG, "Failed to read JS API file: " + Messaging.JS_API_PATH);
212 private void loadExternalExtensions() {
213 if (!mLoadExternalExtensions) return;
215 // Read extensions-config.json and create external extensions.
216 String configFileContent;
218 configFileContent = getExtensionJSFileContent(mActivity, EXTENSION_CONFIG_FILE, false);
219 } catch (IOException e) {
220 Log.e(TAG, "Failed to read extensions-config.json");
224 // Initialize the context for external extensions.
225 XWalkExtensionContextWrapper contextWrapper =
226 new XWalkExtensionContextWrapper(this);
227 Object contextClient = createExtensionContextClient(contextWrapper);
230 JSONArray jsonFeatures = new JSONArray(configFileContent);
231 int extensionCount = jsonFeatures.length();
232 for (int i = 0; i < extensionCount; i++) {
233 JSONObject jsonObject = jsonFeatures.getJSONObject(i);
234 String name = jsonObject.getString("name");
235 String className = jsonObject.getString("class");
236 String jsApiFile = jsonObject.getString("jsapi");
238 // Load the content of the JavaScript file.
241 jsApi = getExtensionJSFileContent(mActivity, jsApiFile, false);
242 } catch (IOException e) {
243 Log.e(TAG, "Failed to read the file " + jsApiFile);
247 if (name != null && className != null && jsApi != null) {
248 createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
251 } catch (JSONException e) {
252 Log.e(TAG, "Failed to parse extensions-config.json");
256 private String getExtensionJSFileContent(Context context, String fileName, boolean fromRaw)
259 InputStream inputStream = null;
262 // If fromRaw is true, Try to find js file in res/raw first.
263 // And then try to get it from assets if failed.
264 Resources resource = context.getResources();
265 String resName = (new File(fileName).getName().split("\\."))[0];
266 int resId = resource.getIdentifier(resName, "raw", context.getPackageName());
269 inputStream = resource.openRawResource(resId);
270 } catch (NotFoundException e) {
271 Log.w(TAG, "Inputstream failed to open for R.raw." + resName +
272 ", try to find it in assets");
276 if (inputStream == null) {
277 AssetManager assetManager = context.getAssets();
278 inputStream = assetManager.open(fileName);
280 int size = inputStream.available();
281 byte[] buffer = new byte[size];
282 inputStream.read(buffer);
283 result = new String(buffer);
285 if (inputStream != null) {
292 private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
293 Activity activity = contextWrapper.getActivity();
295 Class<?> clazz = activity.getClassLoader().loadClass(EXTENSION_CONTEXT_CLIENT_CLASS_NAME);
296 Constructor<?> constructor = clazz.getConstructor(Activity.class, Object.class);
297 return constructor.newInstance(activity, contextWrapper);
298 } catch (ClassNotFoundException e) {
300 } catch (IllegalAccessException e) {
302 } catch (InstantiationException e) {
304 } catch (InvocationTargetException e) {
306 } catch (NoSuchMethodException e) {
312 private void createExternalExtension(String name, String className, String jsApi,
313 Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
314 Activity activity = contextWrapper.getActivity();
316 Class<?> clazz = activity.getClassLoader().loadClass(className);
317 Constructor<?> constructor = clazz.getConstructor(String.class,
318 String.class, contextClient.getClass());
319 constructor.newInstance(name, jsApi, contextClient);
320 } catch (ClassNotFoundException e) {
322 } catch (IllegalAccessException e) {
324 } catch (InstantiationException e) {
326 } catch (InvocationTargetException e) {
328 } catch (NoSuchMethodException e) {
333 private static void handleException(Exception e) {
334 // TODO(yongsheng): Handle exceptions here.
335 Log.e(TAG, "Error in calling methods of external extensions. " + e.toString());