Upstream version 8.36.156.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core_internal / src / org / xwalk / core / internal / 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.core.internal.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.content.res.Resources;
12 import android.content.res.Resources.NotFoundException;
13 import android.util.Log;
14
15 import java.io.File;
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;
22
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;
31
32 /**
33  * This internal class acts a manager to manage extensions.
34  */
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";
41
42     private final Context mContext;
43     private final Activity mActivity;
44
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;
48
49     public XWalkExtensionManager(Context context, Activity activity) {
50         mContext = context;
51         mActivity = activity;
52         mLoadExternalExtensions = true;
53     }
54
55     @Override
56     public void registerExtension(XWalkExtension extension) {
57         if (mExtensions.get(extension.getExtensionName()) != null) {
58             Log.e(TAG, extension.getExtensionName() + "is already registered!");
59             return;
60         }
61
62         XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
63         mExtensions.put(extension.getExtensionName(), bridge);
64     }
65
66     @Override
67     public void unregisterExtension(String name) {
68         XWalkExtensionBridge bridge = mExtensions.get(name);
69         if (bridge != null) {
70             mExtensions.remove(name);
71             bridge.onDestroy();
72         }
73     }
74
75     @Override
76     public Context getContext() {
77         return mContext;
78     }
79
80     @Override
81     public Activity getActivity() {
82         return mActivity;
83     }
84
85     @Override
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);
89     }
90
91     public void broadcastMessage(XWalkExtension extension, String message) {
92         XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
93         if (bridge != null) bridge.broadcastMessage(message);
94     }
95
96     public void onResume() {
97         for(XWalkExtensionBridge extension: mExtensions.values()) {
98             extension.onResume();
99         }
100     }
101
102     public void onPause() {
103         for(XWalkExtensionBridge extension: mExtensions.values()) {
104             extension.onPause();
105         }
106     }
107
108     public void onDestroy() {
109         for(XWalkExtensionBridge extension: mExtensions.values()) {
110             extension.onDestroy();
111         }
112         mExtensions.clear();
113     }
114
115     public void onActivityResult(int requestCode, int resultCode, Intent data) {
116         for(XWalkExtensionBridge extension: mExtensions.values()) {
117             extension.onActivityResult(requestCode, resultCode, data);
118         }
119     }
120
121     public void loadExtensions() {
122         loadInternalExtensions();
123         loadExternalExtensions();
124     }
125
126     public void setAllowExternalExtensions(boolean load) {
127         mLoadExternalExtensions = load;
128     }
129
130     private void loadInternalExtensions() {
131         // Create all extension instances directly here. The internal extension will register
132         // itself and add itself to XWalkExtensionManager.mExtensions automatically.
133         // The following sample shows how to create an extension that named Device:
134         //    String jsApiContent = "";
135         //    try {
136         //        jsApiContent = getExtensionJSFileContent(mContext, Device.JS_API_PATH, true);
137         //        new Device(jsApiContent, mExtensionContextImpl);
138         //    } catch(IOException e) {
139         //        Log.w(TAG, "Failed to read js API file of internal extension: Device");
140         //    }
141         {
142             String jsApiContent = "";
143             try {
144                 jsApiContent = getExtensionJSFileContent(
145                         mContext, PresentationExtension.JS_API_PATH, true);
146                 // Load PresentationExtension as an internal extension.
147                 new PresentationExtension(PresentationExtension.NAME, jsApiContent, this);
148             } catch (IOException e) {
149                 Log.w(TAG, "Failed to read JS API file: " + PresentationExtension.JS_API_PATH);
150             }
151         }
152
153         {
154             String jsApiContent = "";
155             try {
156                 jsApiContent = getExtensionJSFileContent(
157                         mContext, LaunchScreenExtension.JS_API_PATH, true);
158                 // Load LaunchscreenExtension as an internal extension.
159                 new LaunchScreenExtension(LaunchScreenExtension.NAME, jsApiContent,
160                                           LaunchScreenExtension.JS_ENTRY_POINTS, this);
161             } catch (IOException e) {
162                 Log.w(TAG, "Failed to read JS API file: " + LaunchScreenExtension.JS_API_PATH);
163             }
164         }
165
166         {
167             String jsApiContent = "";
168             try {
169                 jsApiContent = getExtensionJSFileContent(
170                         mContext, Contacts.JS_API_PATH, true);
171                 new Contacts(jsApiContent, this);
172             } catch(IOException e) {
173                 Log.w(TAG, "Failed to read JS API file: " + Contacts.JS_API_PATH);
174             }
175         }
176
177         {
178             String jsApiContent = "";
179             try {
180                 jsApiContent = getExtensionJSFileContent(
181                         mContext, DeviceCapabilities.JS_API_PATH, true);
182                 new DeviceCapabilities(jsApiContent, this);
183             } catch(IOException e) {
184                 Log.w(TAG, "Failed to read JS API file: " + DeviceCapabilities.JS_API_PATH);
185             }
186         }
187         {
188             String jsApiContent = "";
189             try {
190                 jsApiContent = getExtensionJSFileContent(
191                         mContext, Messaging.JS_API_PATH, true);
192                 new Messaging(jsApiContent, this);
193             } catch(IOException e) {
194                 Log.w(TAG, "Failed to read JS API file: " + Messaging.JS_API_PATH);
195             }
196         }
197     }
198
199     private void loadExternalExtensions() {
200         if (!mLoadExternalExtensions) return;
201
202         // Read extensions-config.json and create external extensions.
203         String configFileContent;
204         try {
205             configFileContent = getExtensionJSFileContent(mActivity, EXTENSION_CONFIG_FILE, false);
206         } catch (IOException e) {
207             Log.w(TAG, "Failed to read extensions-config.json");
208             return;
209         }
210
211         // Initialize the context for external extensions.
212         XWalkExtensionContextWrapper contextWrapper =
213                 new XWalkExtensionContextWrapper(this);
214         Object contextClient = createExtensionContextClient(contextWrapper);
215
216         try {
217             JSONArray jsonFeatures = new JSONArray(configFileContent);
218             int extensionCount = jsonFeatures.length();
219             for (int i = 0; i < extensionCount; i++) {
220                 JSONObject jsonObject = jsonFeatures.getJSONObject(i);
221                 String name = jsonObject.getString("name");
222                 String className =  jsonObject.getString("class");
223                 String jsApiFile = jsonObject.getString("jsapi");
224
225                 // Load the content of the JavaScript file.
226                 String jsApi;
227                 try {
228                     jsApi = getExtensionJSFileContent(mActivity, jsApiFile, false);
229                 } catch (IOException e) {
230                     Log.w(TAG, "Failed to read the file " + jsApiFile);
231                     return;
232                 }
233
234                 if (name != null && className != null && jsApi != null) {
235                     createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
236                 }
237             }
238         } catch (JSONException e) {
239             Log.w(TAG, "Failed to parse extensions-config.json");
240         }
241     }
242
243     private String getExtensionJSFileContent(Context context, String fileName, boolean fromRaw)
244             throws IOException {
245         String result = "";
246         InputStream inputStream = null;
247         try {
248             if (fromRaw) {
249                 // If fromRaw is true, Try to find js file in res/raw first.
250                 // And then try to get it from assets if failed.
251                 Resources resource = context.getResources();
252                 String resName = (new File(fileName).getName().split("\\."))[0];
253                 int resId = resource.getIdentifier(resName, "raw", context.getPackageName());
254                 if (resId > 0) {
255                     try {
256                         inputStream = resource.openRawResource(resId);
257                     } catch (NotFoundException e) {
258                         Log.w(TAG, "Inputstream failed to open for R.raw." + resName +
259                                    ", try to find it in assets");
260                     }
261                 }
262             }
263             if (inputStream == null) {
264                 AssetManager assetManager = context.getAssets();
265                 inputStream = assetManager.open(fileName);
266             }
267             int size = inputStream.available();
268             byte[] buffer = new byte[size];
269             inputStream.read(buffer);
270             result = new String(buffer);
271         } finally {
272             if (inputStream != null) {
273                 inputStream.close();
274             }
275         }
276         return result;
277     }
278
279     private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
280         Activity activity = contextWrapper.getActivity();
281         try {
282             Class<?> clazz = activity.getClassLoader().loadClass(EXTENSION_CONTEXT_CLIENT_CLASS_NAME);
283             Constructor<?> constructor = clazz.getConstructor(Activity.class, Object.class);
284             return constructor.newInstance(activity, contextWrapper);
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         return null;
297     }
298
299     private void createExternalExtension(String name, String className, String jsApi,
300             Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
301         Activity activity = contextWrapper.getActivity();
302         try {
303             Class<?> clazz = activity.getClassLoader().loadClass(className);
304             Constructor<?> constructor = clazz.getConstructor(String.class,
305                     String.class, contextClient.getClass());
306             constructor.newInstance(name, jsApi, contextClient);
307         } catch (ClassNotFoundException e) {
308             handleException(e);
309         } catch (IllegalAccessException e) {
310             handleException(e);
311         } catch (InstantiationException e) {
312             handleException(e);
313         } catch (InvocationTargetException e) {
314             handleException(e);
315         } catch (NoSuchMethodException e) {
316             handleException(e);
317         }
318     }
319
320     private static void handleException(Exception e) {
321         // TODO(yongsheng): Handle exceptions here.
322         Log.e(TAG, "Error in calling methods of external extensions. " + e.toString());
323     }
324 }