- update source.
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / java / src / org / xwalk / runtime / extension / XWalkExtensionManager.java
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.
4
5 package org.xwalk.runtime.extension;
6
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;
12
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;
20
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;
29
30 /**
31  * This internal class acts a manager to manage extensions.
32  */
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";
39
40     private final Context mContext;
41     private final Activity mActivity;
42
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;
46
47     public XWalkExtensionManager(Context context, Activity activity) {
48         mContext = context;
49         mActivity = activity;
50         mLoadExternalExtensions = true;
51     }
52
53     @Override
54     public void registerExtension(XWalkExtension extension) {
55         if (mExtensions.get(extension.getExtensionName()) != null) {
56             Log.e(TAG, extension.getExtensionName() + "is already registered!");
57             return;
58         }
59
60         XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
61         mExtensions.put(extension.getExtensionName(), bridge);
62     }
63
64     @Override
65     public void unregisterExtension(String name) {
66         XWalkExtensionBridge bridge = mExtensions.get(name);
67         if (bridge != null) {
68             mExtensions.remove(name);
69             bridge.onDestroy();
70         }
71     }
72
73     @Override
74     public Context getContext() {
75         return mContext;
76     }
77
78     @Override
79     public Activity getActivity() {
80         return mActivity;
81     }
82
83     @Override
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);
87     }
88
89     public void broadcastMessage(XWalkExtension extension, String message) {
90         XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
91         if (bridge != null) bridge.broadcastMessage(message);
92     }
93
94     public void onResume() {
95         for(XWalkExtensionBridge extension: mExtensions.values()) {
96             extension.onResume();
97         }
98     }
99
100     public void onPause() {
101         for(XWalkExtensionBridge extension: mExtensions.values()) {
102             extension.onPause();
103         }
104     }
105
106     public void onDestroy() {
107         for(XWalkExtensionBridge extension: mExtensions.values()) {
108             extension.onDestroy();
109         }
110         mExtensions.clear();
111     }
112
113     public void onActivityResult(int requestCode, int resultCode, Intent data) {
114         for(XWalkExtensionBridge extension: mExtensions.values()) {
115             extension.onActivityResult(requestCode, resultCode, data);
116         }
117     }
118
119     public void loadExtensions() {
120         loadInternalExtensions();
121         loadExternalExtensions();
122     }
123
124     public void setAllowExternalExtensions(boolean load) {
125         mLoadExternalExtensions = load;
126     }
127
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 = "";
133         //    try {
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");
138         //    }
139         {
140             String jsApiContent = "";
141             try {
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);
148             }
149         }
150
151         {
152             String jsApiContent = ScreenOrientationExtension.getInsertedString();
153             try {
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);
160             }
161         }
162
163         {
164             String jsApiContent = "";
165             try {
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);
171             }
172         }
173
174         {
175             String jsApiContent = "";
176             try {
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);
182             }
183         }
184         {
185             String jsApiContent = "";
186             try {
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);
192             }
193         }
194     }
195
196     private void loadExternalExtensions() {
197         if (!mLoadExternalExtensions) return;
198
199         // Read extensions-config.json and create external extensions.
200         String configFileContent;
201         try {
202             configFileContent = getAssetsFileContent(mActivity.getAssets(), EXTENSION_CONFIG_FILE);
203         } catch (IOException e) {
204             Log.e(TAG, "Failed to read extensions-config.json");
205             return;
206         }
207
208         // Initialize the context for external extensions.
209         XWalkExtensionContextWrapper contextWrapper =
210                 new XWalkExtensionContextWrapper(this);
211         Object contextClient = createExtensionContextClient(contextWrapper);
212
213         try {
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");
221
222                 // Load the content of the JavaScript file.
223                 String jsApi;
224                 try {
225                     jsApi = getAssetsFileContent(mActivity.getAssets(), jsApiFile);
226                 } catch (IOException e) {
227                     Log.e(TAG, "Failed to read the file " + jsApiFile);
228                     return;
229                 }
230
231                 if (name != null && className != null && jsApi != null) {
232                     createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
233                 }
234             }
235         } catch (JSONException e) {
236             Log.e(TAG, "Failed to parse extensions-config.json");
237         }
238     }
239
240     private String getAssetsFileContent(AssetManager assetManager, String fileName) throws IOException {
241         String result = "";
242         InputStream inputStream = null;
243         try {
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);
249         } finally {
250             if (inputStream != null) {
251                 inputStream.close();
252             }
253         }
254         return result;
255     }
256
257     private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
258         Activity activity = contextWrapper.getActivity();
259         try {
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) {
264             handleException(e);
265         } catch (IllegalAccessException e) {
266             handleException(e);
267         } catch (InstantiationException e) {
268             handleException(e);
269         } catch (InvocationTargetException e) {
270             handleException(e);
271         } catch (NoSuchMethodException e) {
272             handleException(e);
273         }
274         return null;
275     }
276
277     private void createExternalExtension(String name, String className, String jsApi,
278             Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
279         Activity activity = contextWrapper.getActivity();
280         try {
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) {
286             handleException(e);
287         } catch (IllegalAccessException e) {
288             handleException(e);
289         } catch (InstantiationException e) {
290             handleException(e);
291         } catch (InvocationTargetException e) {
292             handleException(e);
293         } catch (NoSuchMethodException e) {
294             handleException(e);
295         }
296     }
297
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());
301     }
302 }