184558061363c117a5795c14d7d81b3919917dc3
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core / src / org / xwalk / core / 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.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.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;
32
33 /**
34  * This internal class acts a manager to manage extensions.
35  */
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";
42
43     private final Context mContext;
44     private final Activity mActivity;
45
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;
49
50     public XWalkExtensionManager(Context context, Activity activity) {
51         mContext = context;
52         mActivity = activity;
53         mLoadExternalExtensions = true;
54     }
55
56     @Override
57     public void registerExtension(XWalkExtension extension) {
58         if (mExtensions.get(extension.getExtensionName()) != null) {
59             Log.e(TAG, extension.getExtensionName() + "is already registered!");
60             return;
61         }
62
63         XWalkExtensionBridge bridge = XWalkExtensionBridgeFactory.createInstance(extension);
64         mExtensions.put(extension.getExtensionName(), bridge);
65     }
66
67     @Override
68     public void unregisterExtension(String name) {
69         XWalkExtensionBridge bridge = mExtensions.get(name);
70         if (bridge != null) {
71             mExtensions.remove(name);
72             bridge.onDestroy();
73         }
74     }
75
76     @Override
77     public Context getContext() {
78         return mContext;
79     }
80
81     @Override
82     public Activity getActivity() {
83         return mActivity;
84     }
85
86     @Override
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);
90     }
91
92     public void broadcastMessage(XWalkExtension extension, String message) {
93         XWalkExtensionBridge bridge = mExtensions.get(extension.getExtensionName());
94         if (bridge != null) bridge.broadcastMessage(message);
95     }
96
97     public void onResume() {
98         for(XWalkExtensionBridge extension: mExtensions.values()) {
99             extension.onResume();
100         }
101     }
102
103     public void onPause() {
104         for(XWalkExtensionBridge extension: mExtensions.values()) {
105             extension.onPause();
106         }
107     }
108
109     public void onDestroy() {
110         for(XWalkExtensionBridge extension: mExtensions.values()) {
111             extension.onDestroy();
112         }
113         mExtensions.clear();
114     }
115
116     public void onActivityResult(int requestCode, int resultCode, Intent data) {
117         for(XWalkExtensionBridge extension: mExtensions.values()) {
118             extension.onActivityResult(requestCode, resultCode, data);
119         }
120     }
121
122     public void loadExtensions() {
123         loadInternalExtensions();
124         loadExternalExtensions();
125     }
126
127     public void setAllowExternalExtensions(boolean load) {
128         mLoadExternalExtensions = load;
129     }
130
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 = "";
136         //    try {
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");
141         //    }
142         {
143             String jsApiContent = "";
144             try {
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);
151             }
152         }
153
154         {
155             String jsApiContent = ScreenOrientationExtension.getInsertedString();
156             try {
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);
163             }
164         }
165
166         {
167             String jsApiContent = "";
168             try {
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);
176             }
177         }
178
179         {
180             String jsApiContent = "";
181             try {
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);
187             }
188         }
189
190         {
191             String jsApiContent = "";
192             try {
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);
198             }
199         }
200         {
201             String jsApiContent = "";
202             try {
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);
208             }
209         }
210     }
211
212     private void loadExternalExtensions() {
213         if (!mLoadExternalExtensions) return;
214
215         // Read extensions-config.json and create external extensions.
216         String configFileContent;
217         try {
218             configFileContent = getExtensionJSFileContent(mActivity, EXTENSION_CONFIG_FILE, false);
219         } catch (IOException e) {
220             Log.e(TAG, "Failed to read extensions-config.json");
221             return;
222         }
223
224         // Initialize the context for external extensions.
225         XWalkExtensionContextWrapper contextWrapper =
226                 new XWalkExtensionContextWrapper(this);
227         Object contextClient = createExtensionContextClient(contextWrapper);
228
229         try {
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");
237
238                 // Load the content of the JavaScript file.
239                 String jsApi;
240                 try {
241                     jsApi = getExtensionJSFileContent(mActivity, jsApiFile, false);
242                 } catch (IOException e) {
243                     Log.e(TAG, "Failed to read the file " + jsApiFile);
244                     return;
245                 }
246
247                 if (name != null && className != null && jsApi != null) {
248                     createExternalExtension(name, className, jsApi, contextClient, contextWrapper);
249                 }
250             }
251         } catch (JSONException e) {
252             Log.e(TAG, "Failed to parse extensions-config.json");
253         }
254     }
255
256     private String getExtensionJSFileContent(Context context, String fileName, boolean fromRaw)
257             throws IOException {
258         String result = "";
259         InputStream inputStream = null;
260         try {
261             if (fromRaw) {
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());
267                 if (resId > 0) {
268                     try {
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");
273                     }
274                 }
275             }
276             if (inputStream == null) {
277                 AssetManager assetManager = context.getAssets();
278                 inputStream = assetManager.open(fileName);
279             }
280             int size = inputStream.available();
281             byte[] buffer = new byte[size];
282             inputStream.read(buffer);
283             result = new String(buffer);
284         } finally {
285             if (inputStream != null) {
286                 inputStream.close();
287             }
288         }
289         return result;
290     }
291
292     private Object createExtensionContextClient(XWalkExtensionContextWrapper contextWrapper) {
293         Activity activity = contextWrapper.getActivity();
294         try {
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) {
299             handleException(e);
300         } catch (IllegalAccessException e) {
301             handleException(e);
302         } catch (InstantiationException e) {
303             handleException(e);
304         } catch (InvocationTargetException e) {
305             handleException(e);
306         } catch (NoSuchMethodException e) {
307             handleException(e);
308         }
309         return null;
310     }
311
312     private void createExternalExtension(String name, String className, String jsApi,
313             Object contextClient, XWalkExtensionContextWrapper contextWrapper) {
314         Activity activity = contextWrapper.getActivity();
315         try {
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) {
321             handleException(e);
322         } catch (IllegalAccessException e) {
323             handleException(e);
324         } catch (InstantiationException e) {
325             handleException(e);
326         } catch (InvocationTargetException e) {
327             handleException(e);
328         } catch (NoSuchMethodException e) {
329             handleException(e);
330         }
331     }
332
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());
336     }
337 }